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

Инквизиция

Герой призывает архангела, который взмахом меча направляет святую силу в указанном направлении, что исцеляет союзников и наносит урон вражеской нежити
раскрыть
– Покайтесь и обратитесь от всех преступлений ваших, чтобы нечестие не было вам преткновением.
код
library RandomIntGenMem
globals
    private constant hashtable H = InitHashtable( )
    constant key RandomIntKey
endglobals

function ClearRandomIntMem takes nothing returns nothing
    call FlushChildHashtable( H, RandomIntKey )
endfunction

function GetRandomIntMem takes integer lowBound, integer highBound returns integer
    local integer r
    local integer simple
    
    if highBound <= lowBound then
        return highBound
    endif
    
    set simple = GetRandomInt( lowBound, highBound )
    set r = simple
    
    loop
        exitwhen not HaveSavedBoolean( H, RandomIntKey, r )
        
        if r < highBound and r >= simple then
            set r = r + 1
        elseif r == highBound and simple > lowBound then
            set r = simple - 1
        elseif r > lowBound and r < simple then
            set r = r - 1
        elseif r <= lowBound or r >= highBound then
            set r = simple
            exitwhen true
        endif
    endloop
    
    if HaveSavedBoolean( H, RandomIntKey, r ) then
        call FlushChildHashtable( H, RandomIntKey )
    endif
    
    call SaveBoolean( H, RandomIntKey, r, true )
    
    return r
endfunction
endlibrary

library InquisitionLib requires RandomIntGenMem
globals
    private constant hashtable H = InitHashtable( )
    private constant group TempGroup = CreateGroup( )
    private constant location LFZ = Location( 0.00, 0.00 )
    private constant integer RessurectionID = 'u000'
    private constant integer HealingID      = 'u002'
    private constant integer HolyID         = 'u003'
    
    private real MaxX
    private real MinX
    private real MaxY
    private real MinY
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 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 InquisitionS
    unit caster
    unit array dummy[20]
    unit array target[20]
    player p
    boolexpr b
    boolexpr b1
    integer array dhType[20]
    
    real x
    real y
    real damage
    real heal
    real radius
    real d
    real a
    real time
    
    Point p0
    Point array pl[20]
    Point array p1[20]
    Point array p2[20]
    Point array p3[20]
endstruct

private function Move takes nothing returns nothing
    local InquisitionS A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local real x
    local real y
    local real z
    local integer i = 0
    local unit u
    
    set A.time = A.time + ( 0.03125 / 1.50 )
    
    if A.time >= 1.00 then
        set udg_TempUnit = A.caster
    endif
    
    loop
        //(1-t)3*P0+3t(1-t)2P1+3t2(1-t)P2+t3P3 https://uk.wikipedia.org/wiki/Крива_Безьє
        if A.target[i] != null then
            set A.p3[i].x = GetUnitX( A.target[i] )
            set A.p3[i].y = GetUnitY( A.target[i] )
            set A.p3[i].z = GetUnitFlyHeight( A.target[i] ) + GetLocZ( A.p3[i].x, A.p3[i].y )
        endif
        
        set x = ( 1.00 - A.time ) * ( 1.00 - A.time ) * ( 1.00 - A.time ) * A.p0.x + 3.00 * A.time * ( ( 1.00 - A.time ) * ( 1.00 - A.time ) ) * A.p1[i].x + 3.00 * ( A.time * A.time ) * ( 1.00 - A.time ) * A.p2[i].x + ( A.time * A.time * A.time ) * A.p3[i].x
        set y = ( 1.00 - A.time ) * ( 1.00 - A.time ) * ( 1.00 - A.time ) * A.p0.y + 3.00 * A.time * ( ( 1.00 - A.time ) * ( 1.00 - A.time ) ) * A.p1[i].y + 3.00 * ( A.time * A.time ) * ( 1.00 - A.time ) * A.p2[i].y + ( A.time * A.time * A.time ) * A.p3[i].y
        set z = ( 1.00 - A.time ) * ( 1.00 - A.time ) * ( 1.00 - A.time ) * A.p0.z + 3.00 * A.time * ( ( 1.00 - A.time ) * ( 1.00 - A.time ) ) * A.p1[i].z + 3.00 * ( A.time * A.time ) * ( 1.00 - A.time ) * A.p2[i].z + ( A.time * A.time * A.time ) * A.p3[i].z
        
        call SetUnitPositionEx( A.dummy[i], x, y )
        call SetUnitFlyHeight( A.dummy[i], z - GetLocZ( x, y ), 0.00 )
        call SetUnitFacing( A.dummy[i], Atan2( y - A.pl[i].y, x - A.pl[i].x ) * bj_RADTODEG )
        // for Pitch = Atan2( z - A.pl[i].z, SquareRoot( ( x - A.pl[i].x ) * ( x - A.pl[i].x ) + ( y - A.pl[i].y ) * ( y - A.pl[i].y ) ) )
        
        if A.time >= 1.00 then
            if A.target[i] != null then
                if A.dhType[i] == 0 then
                    if A.heal >= 0.00 then
                        call SetWidgetLife( A.target[i], GetWidgetLife( A.target[i] ) + A.heal )
                    else
                        call UnitDamageTarget( A.caster, A.target[i], -A.heal, false, false, null, null, null )
                    endif
                elseif A.dhType[i] == 1 then
                    if A.damage >= 0.00 then
                        call UnitDamageTarget( A.caster, A.target[i], A.damage, false, false, null, null, null )
                    else
                        call SetWidgetLife( A.target[i], GetWidgetLife( A.target[i] ) - A.damage )
                    endif
                endif
            else
                set udg_TempUnit = A.caster
                
                call GroupEnumUnitsInRange( TempGroup, x, y, 250.00, A.b )
                        
                loop
                    set u = FirstOfGroup( TempGroup )
                    exitwhen u == null
                    call GroupRemoveUnit( TempGroup, u )
                    
                    if IsUnitInRangeXY( u, x, y, 50.00 ) then
                        call GroupClear( TempGroup )
                        
                        if A.heal >= 0.00 then
                            call SetWidgetLife( u, GetWidgetLife( u ) + A.heal )
                        else
                            call UnitDamageTarget( A.caster, u, -A.heal, false, false, null, null, null )
                        endif
                    endif
                endloop
                
                call GroupEnumUnitsInRange( TempGroup, x, y, 250.00, A.b1 )
                        
                loop
                    set u = FirstOfGroup( TempGroup )
                    exitwhen u == null
                    call GroupRemoveUnit( TempGroup, u )
                    
                    if IsUnitInRangeXY( u, x, y, 50.00 ) then
                        call GroupClear( TempGroup )
                        
                        if A.damage >= 0.00 then
                            call UnitDamageTarget( A.caster, u, A.damage, false, false, null, null, null )
                        else
                            call SetWidgetLife( u, GetWidgetLife( u ) - A.damage )
                        endif
                    endif
                endloop
            endif
        
            call SetUnitAnimation( A.dummy[i], "death" )
            call UnitApplyTimedLife( A.dummy[i], 'BTLF', 2.00 )
            
            set A.dummy[i] = CreateUnit( A.p, HolyID, x, y, GetRandomReal( 0.00, 360.00 ) )
            call SetUnitPositionEx( A.dummy[i], x, y )
            call UnitApplyTimedLife( A.dummy[i], 'BTLF', 2.00 )
            
            set A.dummy[i] = null
            set A.target[i] = null
            
            call A.pl[i].destroy( )
            call A.p1[i].destroy( )
            call A.p2[i].destroy( )
            call A.p3[i].destroy( )
        else
            set A.pl[i].x = x
            set A.pl[i].y = y
            set A.pl[i].z = z
        endif
        
        set i = i + 1
        exitwhen i >= 20
    endloop
    
    if A.time >= 1.00 then
        call PauseTimer( GetExpiredTimer( ) )
        call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
        call DestroyTimer( GetExpiredTimer( ) )
        
        call A.p0.destroy( )
        
        set A.caster = null
        call A.destroy( )
    endif
endfunction

private function Create takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    local InquisitionS A = LoadInteger( H, GetHandleId( t ), 0 )
    local integer i = 0
    local integer k
    local real a = A.a - 45.00 * bj_DEGTORAD
    local real a1 = GetRandomReal( 0.00, 360.00 )
    local unit u
    
    set A.target[0] = null
    set udg_TempUnit = A.caster
    
    call GroupEnumUnitsInRange( TempGroup, A.x, A.y, A.radius + 200.00, A.b )
            
    loop
        set u = FirstOfGroup( TempGroup )
        exitwhen u == null
        call GroupRemoveUnit( TempGroup, u )
        
        if IsUnitInRangeXY( u, A.x, A.y, A.radius ) then
            set A.target[i] = u
            set A.dhType[i] = 0
            set i = i + 1
            
            if i >= 20 then
                call GroupClear( TempGroup )
            endif
        endif
    endloop
    
    if i <= 20 then
        call GroupEnumUnitsInRange( TempGroup, A.x, A.y, A.radius + 200.00, A.b1 )
                
        loop
            set u = FirstOfGroup( TempGroup )
            exitwhen u == null
            call GroupRemoveUnit( TempGroup, u )
            
            if IsUnitInRangeXY( u, A.x, A.y, A.radius ) then
                set A.target[i] = u
                set A.dhType[i] = 1
                set i = i + 1
                
                if i >= 20 then
                    call GroupClear( TempGroup )
                endif
            endif
        endloop
        
        if A.target[0] != null then
            loop
                exitwhen i >= 20
                set k = GetRandomIntMem( 0, i - 1 )
                set A.target[i] = A.target[k]
                set A.dhType[i] = A.dhType[k]
                
                set i = i + 1
            endloop
        endif
    endif
    
    set i = 0
    
    loop
        set A.dummy[i] = CreateUnit( A.p, HealingID, A.p0.x, A.p0.y, A.a * bj_RADTODEG )
        call SetUnitAnimation( A.dummy[i], "birth" )
        call QueueUnitAnimation( A.dummy[i], "stand" )
        call UnitAddAbility( A.dummy[i], 'Arav' )
        call SetUnitPositionEx( A.dummy[i], A.p0.x, A.p0.y )
        call SetUnitFlyHeight( A.dummy[i], A.p0.z - GetLocZ( A.p0.x, A.p0.y ), 0.00 )
        
        set A.pl[i] = Point.create( A.p0.x, A.p0.y, A.p0.z )
        set A.p1[i] = Point.create( A.p0.x + ( A.d + 500.00 ) * Cos( A.a + GetRandomReal( -30.00, 30.00 ) * bj_DEGTORAD ), A.p0.y + ( A.d + 500.00 ) * Sin( A.a + GetRandomReal( -30.00, 30.00 ) * bj_DEGTORAD ), A.p0.z + GetRandomReal( 200.00, 500.00 ) )
        set A.p2[i] = Point.create( A.p0.x + ( A.d + 500.00 ) * Cos( A.a + GetRandomReal( -90.00, 90.00 ) * bj_DEGTORAD ), A.p0.y + ( A.d + 500.00 ) * Sin( A.a + GetRandomReal( -90.00, 90.00 ) * bj_DEGTORAD ), A.p0.z + GetRandomReal( 500.00, 700.00 ) )
        
        if A.target[0] != null then
            set A.p3[i] = Point.create( GetUnitX( A.target[i] ), GetUnitY( A.target[i] ), GetUnitFlyHeight( A.target[i] ) )
        else
            set A.p3[i] = Point.create( A.x + GetRandomReal( 15.00, A.radius - 50.00 ) * Cos( a1 * bj_DEGTORAD ), A.y + GetRandomReal( 15.00, A.radius - 50.00 ) * Sin( a1 * bj_DEGTORAD ), 0.00 )
        endif
        
        set A.p3[i].z = A.p3[i].z + GetLocZ( A.p3[i].x, A.p3[3].y )
        
        set i = i + 1
        exitwhen i >= 20
        set a = a + 90.00 / 20.00 * bj_DEGTORAD
        set a1 = a1 + 360.00 / 20.00
    endloop
        
    call TimerStart( t, 0.03125, true, function Move )
    
    set t = null
endfunction

private function SetAnim takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    
    call SetUnitTimeScale( LoadUnitHandle( H, GetHandleId( t ), 0 ), 1.00 )
    call FlushChildHashtable( H, GetHandleId( t ) )
    call DestroyTimer( t )
    
    set t = null
endfunction

function Inquisition_Actions takes unit caster, real damage, real heal, real radius, boolexpr b, boolexpr b1 returns nothing
    local timer t = CreateTimer( )
    local InquisitionS A = InquisitionS.create( )
    
    set A.caster = GetTriggerUnit( )
    set A.damage = damage
    set A.radius = radius
    set A.heal   = heal
    set A.x  = GetSpellTargetX( )
    set A.y  = GetSpellTargetY( )
    set A.b  = b
    set A.b1 = b1
    set A.p  = GetOwningPlayer( A.caster )
    set A.p0 = Point.create( GetUnitX( A.caster ), GetUnitY( A.caster ), 400.00 )
    set A.a  = Atan2( A.y - A.p0.y, A.x - A.p0.x )
    set A.d  = SquareRoot( ( A.x - A.p0.x ) * ( A.x - A.p0.x ) + ( A.y - A.p0.y ) * ( A.y - A.p0.y ) )
    set A.time = 0.00
    set A.p0.z = A.p0.z + GetLocZ( A.p0.x, A.p0.y )
    
    call SaveInteger( H, GetHandleId( t ), 0, A )
    call TimerStart( t, 1.00, false, function Create )
    
    set A.dummy[0] = CreateUnit( A.p, RessurectionID, A.p0.x, A.p0.y, A.a * bj_RADTODEG )
    call SetUnitTimeScale( A.dummy[0], 1.70 )
    call SetUnitPositionEx( A.dummy[0], A.p0.x, A.p0.y )
    call UnitApplyTimedLife( A.dummy[0], 'BTLF', 5.00 )
    
    set t = CreateTimer( )
    
    call SaveUnitHandle( H, GetHandleId( t ), 0, A.dummy[0] )
    call TimerStart( t, 1.00, false, function SetAnim )
    
    set t = null
endfunction

//===========================================================================
function InitTrig_Inquisition takes nothing returns nothing
    local rect r = GetWorldBounds( )
    //set gg_trg_Inquisition = CreateTrigger(  )
    
    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

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


используется немного отредактированная мной версия Генератора случайных чисел без повторений от ScorpioT1000

upd 13.01.2024: немного отредактированная версия, добавлен поиск целей

спасиб Maxlaid за помощь в нахождении идеи для этого спелла и PROSHELDOTU за замечание неэффективности (неиграбельности) данной недоспособности, что сподвигло добавить поиск цели

я не думаю что кто-то всерьёз будет этот спелл где-то использовать, так что просто демонстрирую идею
`
ОЖИДАНИЕ РЕКЛАМЫ...
2
22
3 месяца назад
2
визуально норм
но это не играбельно судя по падению частиц, на дистанции ни своих толком не похилишь, ни вражескую нежить не ранишь, только если под себя бросать, и то противники ещё 10 раз успеют задоджить, даже сели под себя, а куда-то в точку, так ваще изи
и указатель надо областью бы, чтобы видеть радиус
2
15
3 месяца назад
2
Выглядит эффектно! Однозначно лайк!
3
27
3 месяца назад
3
но это не играбельно судя по падению частиц
Соглашусь, но в основном это просто показ идеи, т.е. можно, например, кастомному боссу в рпгшке добавить что-то подобное в рядовые способности с маркерами падения
2
12
3 месяца назад
2
Кривые Безье топ тема, жаль ноут мой большое количество подобных просчётов (даммиков) не выдерживает, ну или сам варик, не тестил на другом железе.
2
27
3 месяца назад
2
PROSHELDOTU, мне захотелось сделать новую версию с поиском целей, спасибо за замечание
2
22
3 месяца назад
2
rsfghd, нужно в описание подробно написать как спелл всё же работает
сколько частиц, как они наводятся, как приоритеты расставляет, как и сколько в целом спелл хилит дамажит и всё такое
3
27
3 месяца назад
Отредактирован rsfghd
3
PROSHELDOTU, почти всё из этого есть в гуи примере
Не настраивается количество частиц, периодичность таймера, скорость полета, секунда на которой вылетают самонаводящиеся частицы, как именно они лететь будут, и нет расширенного приоритета (а-ля наводиться в первую очередь на самых раненых или здоровых). Сначала ищутся цели для урона, затем для хила (чтобы было наоборот достаточно инвертировать значения урона/хила), оставшиеся частицы распределяются поровну между пойманными целями. Если при спавне частиц в области каста никого не было, они падают рандомно по области, так же производя дамаг или хил рандомной цели вокруг падения
Мне лень было добавлять 999 настроек зная, что никто это использовать не будет и возьмёт лишь идею. Если кому-либо действительно понадобится это, но с небольшими изменениями, то всегда можно оставить комментарий с просьбой и я скину отредактированную версию
2
22
3 месяца назад
2
rsfghd, речь конкретно про описание
игроку банально нужно понимать сколько оно похилит или продамажит
3
27
3 месяца назад
3
PROSHELDOTU, там в гуи есть настройки урона и дамага, и кого хилять/дамажить через булекспры, а так же радиус
каждый напишет своё описание на своём языке со своими настройками урона типа 25 + [Интеллект х 2.5], продвинутые сделают динамическое описание, короче это уже заморочки пользователей
Чтобы оставить комментарий, пожалуйста, войдите на сайт.