• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Spell] jass spell

Status
Not open for further replies.
Level 5
Joined
Aug 14, 2020
Messages
118
this is pyroblast jass spell,it works fine I need some one explain this for me, what is the job of each part.

Code:
function Pyroblast_Cond takes nothing returns boolean
    return GetSpellAbilityId()=='A005'
endfunction

function Pyroblast_Move takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer h = GetHandleId(t)
    local unit pyroblast = LoadUnitHandle(Hash,h,1)
    local unit target = LoadUnitHandle(Hash,h,2)
    local unit caster = LoadUnitHandle(Hash,h,3)
    local real X1 = GetUnitX(pyroblast)
    local real X2 = GetUnitX(target)
    local real Y1 = GetUnitY(pyroblast)
    local real Y2 = GetUnitY(target)
    local real angle = Atan2(Y2 - Y1, X2 - X1)
    local player PlayerCaster = GetOwningPlayer(caster)
    local integer data = GetUnitUserData(caster)
    local real damage = DamagePyroblast[0][data]
    local real damageLow = DamagePyroblastRect[0][data]
    local real dist = SquareRoot((X2 - X1) * (X2 - X1) + (Y2 - Y1) * (Y2 - Y1))
    local real distance = 200
    if GetWidgetLife( target ) >.405 then
    call SetUnitFacing(pyroblast, angle*bj_RADTODEG)
    call SetUnitFlyHeight(pyroblast,GetUnitFlyHeight(target),dist)
    call SetUnitX(pyroblast, X1 + 2.5 * Cos(angle))
    call SetUnitY(pyroblast, Y1 + 2.5 * Sin(angle))
        if dist <= 10.00 then
            local group G = CreateGroup()
            local unit enemy
            local player player1
            call GroupEnumUnitsInRange(G, X1, Y1, distance, null)
            loop
                set enemy = FirstOfGroup(G)
                player1 = GetOwningPlayer(enemy)
                if IsPlayerEnemy(player1, PlayerCaster) and enemy != target then
                    call UnitDamageTarget( caster, enemy, damageLow, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, null )
                endif
                call GroupRemoveUnit(G,enemy)
                exitwhen enemy == null
            endloop
            call UnitDamageTarget( caster, target, damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, null )
            call KillUnit(pyroblast)
            call GroupClear(G)
            call DestroyGroup(G)
            call FlushChildHashtable(Hash,h)
            call PauseTimer(t)
            call DestroyTimer(t)
            set G = null
            set player1 = null
        endif
    else
        call UnitRemoveAbility( caster, 'A005' )
        call UnitAddAbility( caster, 'A005' )
        call KillUnit(pyroblast)
        call FlushChildHashtable(Hash,h)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif
   
    set t = null
    set pyroblast = null
    set caster = null
    set target = null
endfunction

function Pyroblast_Scale takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer h = GetHandleId(t)
    local unit pyroblast = LoadUnitHandle(Hash,h,1)
    local unit array dummy
    local unit target = LoadUnitHandle(Hash,h,2)
    local unit caster = LoadUnitHandle(Hash,h,3)
    local real scale = LoadReal(Hash,h,4)
    local real face = GetUnitFacing(caster)
    local real X1 = GetUnitX(caster) + 75 * Cos(face* bj_DEGTORAD)
    local real x1 = GetUnitX(caster)
    local real X2 = GetUnitX(target)
    local real Y1 = GetUnitY(caster) + 75 * Sin(face* bj_DEGTORAD)
    local real Y2 = GetUnitY(target)
    local integer n = 0
    local boolean spell = GetUnitCurrentOrder(caster) == 852119
    if spell == true and IsUnitType(caster, UNIT_TYPE_STUNNED) == false or GetWidgetLife( caster ) >.405  then
        local real dist = 1.50
        set scale = scale + 0.05
        call SetUnitScale( pyroblast, scale, scale, scale )
        call SetUnitPosition(pyroblast, X1, Y1)
        call SetUnitFacing(pyroblast,face)
        if scale >= dist then
            local timer tick = CreateTimer()
            local integer l = GetHandleId(tick)
            call UnitRemoveAbility( caster, 'Amrf' )
            loop
            exitwhen (n == 11)
                set dummy[n] = LoadUnitHandle(Hash,h,5+n)
                call KillUnit(dummy[n])
                set dummy[n] = null
            set n = n + 1
            endloop
            call SaveUnitHandle(Hash,l,1,pyroblast)
            call SaveUnitHandle(Hash,l,2,target)
            call SaveUnitHandle(Hash,l,3,caster)
            call TimerStart(tick,.01,true,function Pyroblast_Move)
            call FlushChildHashtable(Hash,h)
            call PauseTimer(t)
            call DestroyTimer(t)
            set tick = null
        else
            call SaveReal(Hash,h,4,scale)
            if scale == 0.65 then
                call SetUnitAnimationByIndex(caster,5)
                QueueUnitAnimation(caster,"stand")
            endif
        endif
       
        set pyroblast = null
        set target = null
        set caster = null
        set t = null
    else
       
        call UnitRemoveAbility( caster, 'A005' )
        call UnitAddAbility( caster, 'A005' )
        loop
        exitwhen (n == 11)
            set dummy[n] = LoadUnitHandle(Hash,h,5+n)
            call KillUnit(dummy[n])
            set dummy[n] = null
        set n = n + 1
        endloop
        call KillUnit(pyroblast)
        call FlushChildHashtable(Hash,h)
        call PauseTimer(t)
        call DestroyTimer(t)
        set pyroblast = null
        set target = null
        set caster = null
        set t = null
    endif
endfunction

function Pyroblast takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local unit target = GetSpellTargetUnit()
    local timer t = CreateTimer()
    local integer h = GetHandleId(t)
    local real face = GetUnitFacing(caster)
    local player PlayerCaster = GetOwningPlayer(caster)
    local real scale = 1.0
    local real x = GetUnitX(caster) + 75 * Cos(face* bj_DEGTORAD)
    local real x1 = GetUnitX(caster)
    local real y = GetUnitY(caster) + 75 * Sin(face* bj_DEGTORAD)
    local real y1 = GetUnitY(caster)
    local unit pyroblast = CreateUnit( PlayerCaster, 'h001', x, y, face )
    local unit array dummy
   
    local integer i = 'h007'
    local integer l = 0
    loop
   exitwhen (l == 11)
        if (l == GetPlayerId(PlayerCaster) and GetLocalPlayer() == PlayerCaster) then
            i = 'h004'
        endif
    set dummy[l] = CreateUnit( Player(l), i, x, y, face )
    call SetUnitTimeScale( dummy[l], 0.50 )
    call SetUnitScale( dummy[l], 1.5, 1, 1 )
    call SaveUnitHandle(Hash,h,5+l,dummy[l])
        set i = 'h007'
    set dummy[l] = null
   set l = l + 1
    endloop
    call SetUnitPathing( pyroblast, false )
    call SetUnitScale( pyroblast, scale, scale, scale )
    call SaveUnitHandle(Hash,h,1,pyroblast)
    call SaveUnitHandle(Hash,h,2,target)
    call SaveUnitHandle(Hash,h,3,caster)
    call TimerStart(t,.05,true,function Pyroblast_Scale)
    set t = null
    set pyroblast = null
    set caster = null
    set target = null
endfunction

//===========================================================================
function InitTrig_Pyroblast takes nothing returns nothing
    set gg_trg_Pyroblast = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Pyroblast, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddCondition( gg_trg_Pyroblast, Condition( function Pyroblast_Cond ) )
    call TriggerAddAction( gg_trg_Pyroblast, function Pyroblast )
endfunction [\CODE]
 
Level 23
Joined
Apr 3, 2018
Messages
460
The very bottom function ("InitTrig_Pyroblast") is the initialization of the trigger done when the map is first loading. It defines the trigger's event, conditions and actions.
The trigger's first action when the spell is cast is the function "Pyroblast" directly above it.
"Pyroblast_Cond" (on the very top) is the trigger's condition just checks which spell was cast.

function Pyroblast (on cast).
It creates a "projectile" unit of type 'h001' 75 points in front of the caster.
Then, it also creates a dummy for every player. For the spell's caster, the dummy will have a different type 'h004' (don't know what it is for without objective editor data, probably just has a differet looking model). This will only be seen on that player's computer which is achieved through using GetLocalPlayer() - on everyone else's computers, the function will create unit 'h007'.
After all this, it saves all those things (caster, target, projectile, dummies) on a timer through the hashtable.

function Pyroblast_Scale (periodic on timer)
First it detects if the caster is still channeling an ability with "chainlightning" order, if not, the spell finishes and dummies die.
It then makes the main "projectile" grow in size while it remains in front of the caster.
Once its scale reaches 65%, the caster's animation changes to an animation with index 5 (don't know what it is without the model, likely some next stage of casting the spell).
Once its scale reaches 150%, it kills all the previously created dummies and the timer, and creates a new timer which launches the next function.

function Pyroblast_Move (next stage timer)
This function moves the projectile towards the spell's target.
It uses Atan2 to calculate the angle towards the target, then moves the projectile 2.5 points along that direction.
If the target dies before the projectile reaches it, the spell ends.
Once the projectile reaches the target, it damages all enemy units in a 200 radius, and deals different damage to the main target.
The damage amount itself seems to be kept in some arrays not defined in this trigger.
 
Last edited:
Level 23
Joined
Apr 3, 2018
Messages
460
The hashtables are just a way to get the local variables from one function to work in another.

So first we need something to tie them to, like a timer, and get its handle
- integer h = GetHandleId(t)

You can then attach a unit to that handle
- SaveUnitHandle(Hash,h,1,pyroblast)
here h is the handle id of the timer, and 1 is a key you choose for your variable like an array index, it should be different for each attached variable

Then in your other function you can get the attached unit by using the same timer handle and key
- local unit pyroblast = LoadUnitHandle(Hash,h,1)

When the timer is destroyed, you also need to get rid of the data stored for it using
- FlushChildHastable(Hash,h)
 
Level 23
Joined
Apr 3, 2018
Messages
460
That part calculates an angle from the unit pyroblast to the target.
It first takes the coordinates of the units, then uses the trigonometry function Arctangent to get the angle from the differences on the Y and X axis. The angle returned is in radians, not degrees. If it was Atan2BJ, it would return degrees.

This angle is then used to move the projectile
speed*Cos(angle) - speed on the X axis
speed*Sin(angle) - speed on the Y axis
 
Status
Not open for further replies.
Top