Projectile motion stopping, repeat spell instances not working

Flare

Stops copies me!
Reaction score
662
EDIT: Solved the first part (was incrementing i2 within the If :p), still need to figure out how to solve the second problem

1) For some reason, when one or two of my projectiles complete their movement (and hit the ground), it causes the rest of the projectiles in the spell instance to stop in midair (for no particular reason)

(full spell code, if needed)
JASS:
//Phantasmal barrage
//By Flare
//Requires: dummy.mdx
//          Projectile dummy (h000)
//          Launcher Dummy (h001)
//          Phantasmal Barrage ability (A000)
//          NewGen WE


//Thanks to:
//Vexorian - TimerUtils
//Shadow1500 - Parabola function
struct PB
//Configurables

//Ability rawcode
    private static constant integer SID = 'AOsh'
//Launcher Dummy rawcode
    private static constant integer LID = 'h001'
//Projectile Dummy rawcode
    private static constant integer PID = 'h000'
//Timer interval
    private static constant real TIMER_INT = 0.1
//Number of projectiles used
    private static constant integer PROJCOUNT = 5
//Projectile model
    private static constant string PROJ_FX = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"
//Attachment point for projectile
    private static constant string ATTACH_PT = "origin"
//Low bound for projectile speed - projectile speed will be somewhere between SPEED_LOW and SPEED_HIGH
    private static constant real SPEED_LOW = 600
//High bound for projectile speed
    private static constant real SPEED_HIGH = 900
//Projectile's target offset - base value for how far from the target point the projectile will land
    private static constant real TARGET_OFFSET = 100.
//Intensity of curve - lower values result in a "sharper" curve, higher values result in a bow-shaped curve
    private static constant real CURVE = 1.
//Accurate range
    private static constant real ACC_RANGE = 800.
//Accuracy discrepancy - higher values result in a less accurate barrage at longer distance
//Discrepancy only takes effect if the target point if more than ACC_RANGE units away from the caster
    private static constant real ACC_DISCREP_MULTI = 50
//For every ACC_DISCREP_MULTI units beyond ACC_RANGE, the projectiles' targeting will be off by up to ACC_DISCREP units
//e.g. (using current values) if you cast the spell 1400 units away
//the projectiles will land up to ((1400-800)/50)*25 (300) units away from their original target
//NOTE: Projectile's original target is the target point offset by TARGET_OFFSET towards a random angle
    private static constant real ACC_DISCREP = 25
//Minimum damage percentage - percentage of whole damage that is dealt at the edge of damage radius
    private static constant real MIN_DMG_PERCENT = 0.25
//Attack type, damage type, weapon type
    private static constant attacktype ATYPE = ATTACK_TYPE_MAGIC
    private static constant damagetype DTYPE = DAMAGE_TYPE_NORMAL
    private static constant weapontype WTYPE = WEAPON_TYPE_WHOKNOWS
//Animation played by the launcher dummies
    private static constant string ANIM = "attack"
//Lifespan of launcher dummies
    private static constant real LIFESPAN = 1.
    
    
//Damage radius calculation
    private static method DamageRadius takes unit u returns real
        local real base = 150.
        local real m1 = I2R (GetUnitAbilityLevel (u, .SID))
        local real m2 = 50.
        return base + m1*m2
    endmethod
    
    
//Damage calculation
    private static method Damage takes unit u returns real
        local real base
        local real m1
        local real m2
        return base + m1*m2
    endmethod
    
    
//Required globals    
    private static location tempLoc = Location (0,0)
    private static group genGroup = CreateGroup ()
    private static timer spellTimer = CreateTimer ()
    private static PB array data
    private static PB gdata
    private static integer N = 0
    private static real groupX = 0
    private static real groupY = 0
    private static real doublePi = bj_PI * 2
    private static boolean grounded = false
    
    
//Required struct members
    unit caster
    unit array proj[.PROJCOUNT]
    real array speed[.PROJCOUNT]
    real array pcos[.PROJCOUNT]
    real array psin[.PROJCOUNT]
    real array curdist[.PROJCOUNT]
    real array maxdist[.PROJCOUNT]
    effect array pfx[.PROJCOUNT]
    boolean array done[.PROJCOUNT]
    integer destroyed = 0
    
    
    
//Required helper functions
    private static constant method JumpParabola takes real dist, real maxdist,real curve returns real
        local real t = (dist*2)/maxdist-1
        return (-t*t+1)*(maxdist/curve)
    endmethod
    
    
    private static method SetUnitXY takes unit u,real x,real y returns nothing
        if x<GetRectMaxX(bj_mapInitialPlayableArea) and x>GetRectMinX(bj_mapInitialPlayableArea) and y<GetRectMaxY(bj_mapInitialPlayableArea) and y>GetRectMinY(bj_mapInitialPlayableArea) then
            call SetUnitX(u,x)
            call SetUnitY(u,y)
        endif
    endmethod
    
    
    private static method GetCoordZ takes real x, real y returns real
        call MoveLocation (.tempLoc, x, y)
        return GetLocationZ (.tempLoc)
    endmethod

    
    private static method SetUnitZ takes unit u, real h returns nothing
        local real z = h - .GetCoordZ (GetUnitX (u), GetUnitY (u))
        if z > 0 then
            call SetUnitFlyHeight (u, z, 0)
            set .grounded = false
        else
            set .grounded = true
        endif
    endmethod
    
    private static method GetUnitZ takes unit u returns real
        return .GetCoordZ (GetUnitX (u), GetUnitY (u)) + GetUnitFlyHeight (u)
    endmethod
    
    
    private static method GetRandOffsetX takes real x, real dist returns real
        return x + (ModuloReal (dist - .ACC_RANGE, .ACC_DISCREP_MULTI) * .ACC_DISCREP) * Cos (GetRandomReal (0, .doublePi))
    endmethod
    
    
    private static method GetRandOffsetY takes real y, real dist returns real
        return y +(ModuloReal (dist - .ACC_RANGE, .ACC_DISCREP_MULTI) * .ACC_DISCREP) * Sin (GetRandomReal (0, .doublePi))
    endmethod
    
    
//Spell functions
    private static method GroupFunc takes nothing returns boolean
        local PB a = .gdata
        local unit u = GetFilterUnit ()
        local real ux = GetUnitX (u)
        local real uy = GetUnitY (u)
        local real x = .groupX - ux
        local real y = .groupY - uy
        local real dist = SquareRoot (x*x + y*y)
        local real percent = dist/.DamageRadius (a.caster)
        local real dmg
        if percent < .MIN_DMG_PERCENT then
            set percent = .MIN_DMG_PERCENT
        endif
        set dmg = .Damage (a.caster) * percent
        call UnitDamageTarget (a.caster, u, dmg, false, true, .ATYPE, .DTYPE, .WTYPE)
        return false
    endmethod
    
    method onDestroy takes nothing returns nothing
        call BJDebugMsg ("Struct destroyed")
    endmethod

    
    private static method Move takes nothing returns nothing
        local integer i = 0
        local integer i2 = 0
        local real x
        local real y
        local real z
        local boolean b
        local real animindex
        local PB a
        loop
        exitwhen i == .N
        set a = .data<i>
            loop
            exitwhen i2 == .PROJCOUNT
                if a.done[i2] == false then
                    set x = GetUnitX (a.proj[i2])
                    set y = GetUnitY (a.proj[i2])
                    set x = x + a.pcos[i2]*a.speed[i2]
                    set y = y + a.psin[i2]*a.speed[i2]
                    set z = .JumpParabola (a.curdist[i2], a.maxdist[i2], .CURVE)
                    //call BJDebugMsg (&quot;Current z - &quot; + R2S (z) + &quot;, speed - &quot; + R2S (a.speed[i2]) + &quot;, current distance - &quot; + R2S (a.curdist[i2]))
                    set animindex = Atan2 (z, a.curdist[i2] - a.maxdist[i2] * 0.5)
                    call .SetUnitXY (a.proj[i2], x, y)
                    call .SetUnitZ (a.proj[i2], z)
                    if .grounded == true and a.curdist[i2] &gt; 0 then
                        set a.destroyed = a.destroyed + 1
                        set a.done[i2] = true
                        set a.curdist[i2] = 0
                        call KillUnit (a.proj[i2])
                        call DestroyEffect (a.pfx[i2])
                        set .gdata = a
                        call GroupEnumUnitsInRange (.genGroup, x, y, .DamageRadius (a.caster), Condition (function PB.GroupFunc))
                    endif
                    set a.curdist[i2] = a.curdist[i2] + a.speed[i2]
                    set i2 = i2 + 1
                endif
            endloop
            
            if a.destroyed &gt;= .PROJCOUNT then
                call BJDebugMsg (&quot;Value is &quot; + I2S (a.destroyed) + &quot;/&quot; + I2S (.PROJCOUNT))
                call a.destroy ()
                set .data<i> = .data[.N]
                set .N = .N - 1
                set i = i - 1
            endif
            
            set i = i + 1
        endloop
    endmethod

    
    private static method SpellCond takes nothing returns boolean
        return GetSpellAbilityId () == .SID
    endmethod
    
    
    private static method SpellActions takes nothing returns nothing
        local PB a = PB.create ()
        local integer i = 0
        local unit u
        local unit c = GetTriggerUnit ()
        local real cx = GetUnitX (c)
        local real cy = GetUnitY (c)
        local location l = GetSpellTargetLoc ()
        local real tx = GetLocationX (l)
        local real ty = GetLocationY (l)
        local real x = tx - cx
        local real y = ty - cy
        local real rx
        local real ry
        local real angle = Atan2 (y, x)
        local real dist = SquareRoot (x*x + y*y)
        set a.caster = c
        set c = null
        set rx = cx + Cos (angle) * dist
        set ry = cy + Sin (angle) * dist
        call RemoveLocation (l)
        set l = null
        loop
        exitwhen i == .PROJCOUNT
           set u = CreateUnit (GetOwningPlayer (a.caster), .LID, cx, cy, angle * bj_RADTODEG)
           //call BJDebugMsg (&quot;Angle - &quot; + R2S (angle * bj_RADTODEG))
           call SetUnitAnimation (u, .ANIM)
           call QueueUnitAnimation (u, &quot;stand&quot;)
           call UnitApplyTimedLife (u, &#039;BTLF&#039;, .LIFESPAN)
           if dist &lt;= .ACC_RANGE then
                set rx = rx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
                set ry = ry + Sin(GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
           else
                set rx = .GetRandOffsetX (rx, dist)
                set ry = .GetRandOffsetY (ry, dist)
           endif
           set x = rx-cx
           set y = ry-cy
           set angle = Atan2 (y, x)
           set a.pcos<i> = Cos (angle)
           set a.psin<i> = Sin (angle)
           set a.maxdist<i> = SquareRoot (x*x + y*y)
           set a.proj<i> = CreateUnit (GetOwningPlayer (a.caster), .PID, cx, cy, angle * bj_RADTODEG)
           call UnitAddAbility (a.proj<i>, &#039;Arav&#039;)
           call UnitRemoveAbility (a.proj<i>, &#039;Arav&#039;)
           set a.curdist<i> = 0
           set a.speed<i> = GetRandomReal (.SPEED_LOW, .SPEED_HIGH) * .TIMER_INT
           set a.pfx<i> = AddSpecialEffectTarget (.PROJ_FX, a.proj<i>, .ATTACH_PT)
           set a.done<i> = false
           set i = i + 1
        endloop
        
        set .N = .N + 1
    
        
        set u = null
    endmethod
    
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger ()
        call TriggerRegisterAnyUnitEventBJ (t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition (t, Condition (function PB.SpellCond))
        call TriggerAddAction (t, function PB.SpellActions)
        call TimerStart (.spellTimer, .TIMER_INT, true, function PB.Move)
    endmethod
endstruct</i></i></i></i></i></i></i></i></i></i></i></i></i>


Trigger action/condition and timer callback functions
JASS:
//Required struct members
    unit caster
    unit array proj[.PROJCOUNT]
    real array speed[.PROJCOUNT]
    real array pcos[.PROJCOUNT]
    real array psin[.PROJCOUNT]
    real array curdist[.PROJCOUNT]
    real array maxdist[.PROJCOUNT]
    effect array pfx[.PROJCOUNT]
    boolean array done[.PROJCOUNT]
    integer destroyed = 0

    private static method Move takes nothing returns nothing
        local integer i = 0
        local integer i2 = 0
        local real x
        local real y
        local real z
        local boolean b
        local real animindex
        local PB a
        loop
        exitwhen i == .N
        set a = .data<i>
            loop
            exitwhen i2 == .PROJCOUNT
                if a.done[i2] == false then
                    set x = GetUnitX (a.proj[i2])
                    set y = GetUnitY (a.proj[i2])
                    set x = x + a.pcos[i2]*a.speed[i2]
                    set y = y + a.psin[i2]*a.speed[i2]
                    set z = .JumpParabola (a.curdist[i2], a.maxdist[i2], .CURVE)
                    //call BJDebugMsg (&quot;Current z - &quot; + R2S (z) + &quot;, speed - &quot; + R2S (a.speed[i2]) + &quot;, current distance - &quot; + R2S (a.curdist[i2]))
                    set animindex = Atan2 (z, a.curdist[i2] - a.maxdist[i2] * 0.5)
                    call .SetUnitXY (a.proj[i2], x, y)
                    call .SetUnitZ (a.proj[i2], z)
                    if .grounded == true and a.curdist[i2] &gt; 0 then
                        set a.destroyed = a.destroyed + 1
                        set a.done[i2] = true
                        set a.curdist[i2] = 0
                        call KillUnit (a.proj[i2])
                        call DestroyEffect (a.pfx[i2])
                        set .gdata = a
                        call GroupEnumUnitsInRange (.genGroup, x, y, .DamageRadius (a.caster), Condition (function PB.GroupFunc))
                    endif
                    set a.curdist[i2] = a.curdist[i2] + a.speed[i2]
                    set i2 = i2 + 1
                endif
            endloop
            
            if a.destroyed &gt;= .PROJCOUNT then
                call BJDebugMsg (&quot;Value is &quot; + I2S (a.destroyed) + &quot;/&quot; + I2S (.PROJCOUNT))
                call a.destroy ()
                set .data<i> = .data[.N]
                set .N = .N - 1
                set i = i - 1
            endif
            
            set i = i + 1
        endloop
    endmethod

    
    private static method SpellCond takes nothing returns boolean
        return GetSpellAbilityId () == .SID
    endmethod
    
    
    private static method SpellActions takes nothing returns nothing
        local PB a = PB.create ()
        local integer i = 0
        local unit u
        local unit c = GetTriggerUnit ()
        local real cx = GetUnitX (c)
        local real cy = GetUnitY (c)
        local location l = GetSpellTargetLoc ()
        local real tx = GetLocationX (l)
        local real ty = GetLocationY (l)
        local real x = tx - cx
        local real y = ty - cy
        local real rx
        local real ry
        local real angle = Atan2 (y, x)
        local real dist = SquareRoot (x*x + y*y)
        set a.caster = c
        set c = null
        set rx = cx + Cos (angle) * dist
        set ry = cy + Sin (angle) * dist
        call RemoveLocation (l)
        set l = null
        loop
        exitwhen i == .PROJCOUNT
           set u = CreateUnit (GetOwningPlayer (a.caster), .LID, cx, cy, angle * bj_RADTODEG)
           //call BJDebugMsg (&quot;Angle - &quot; + R2S (angle * bj_RADTODEG))
           call SetUnitAnimation (u, .ANIM)
           call QueueUnitAnimation (u, &quot;stand&quot;)
           call UnitApplyTimedLife (u, &#039;BTLF&#039;, .LIFESPAN)
           if dist &lt;= .ACC_RANGE then
                set rx = rx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
                set ry = ry + Sin(GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
           else
                set rx = .GetRandOffsetX (rx, dist)
                set ry = .GetRandOffsetY (ry, dist)
           endif
           set x = rx-cx
           set y = ry-cy
           set angle = Atan2 (y, x)
           set a.pcos<i> = Cos (angle)
           set a.psin<i> = Sin (angle)
           set a.maxdist<i> = SquareRoot (x*x + y*y)
           set a.proj<i> = CreateUnit (GetOwningPlayer (a.caster), .PID, cx, cy, angle * bj_RADTODEG)
           call UnitAddAbility (a.proj<i>, &#039;Arav&#039;)
           call UnitRemoveAbility (a.proj<i>, &#039;Arav&#039;)
           set a.curdist<i> = 0
           set a.speed<i> = GetRandomReal (.SPEED_LOW, .SPEED_HIGH) * .TIMER_INT
           set a.pfx<i> = AddSpecialEffectTarget (.PROJ_FX, a.proj<i>, .ATTACH_PT)
           set a.done<i> = false
           set i = i + 1
        endloop
        
        set .N = .N + 1
    
        
        set u = null
    endmethod</i></i></i></i></i></i></i></i></i></i></i></i></i>


2) Whenever I cast the spell for a second/third/fourth (etc) time, the callback function doesn't do anything - the units are created, but no movement takes place and I can't see why that is :( Any idea why that would happen?
 
Why can't I see where you're setting the newly created struct to .data[.N]? Am I just blind or don't you have it in there? :p
 
JASS:

        set .N = .N + 1


maybe im wrong but shouldnt be .N be IN the loop? (spellactions function)
 
Why can't I see where you're setting the newly created struct to .data[.N]? Am I just blind or don't you have it in there? :p
Hmmm, you may be onto something there :D

EDIT: Ye, that was the problem :p Now I've got more sh*t to sort out - now each instance waits for all instances ahead of it to complete before commencing :(

JASS:
        set .N = .N + 1


maybe im wrong but shouldnt be .N be IN the loop? (spellactions function)
No - one instance consists of PROJCOUNT components, so incrementing N within the loop would allocate PROJCOUNT instances for the loop
 
> now each instance waits for all instances ahead of it to complete before commencing
Have you tried putting
JASS:
set i2 = 0
after increasing i in the callback function? ^_^
 
Ye, I noticed that :p

Still having some weird problems - second spell instance seems to be buggy, but I can spam the spell and MOST projectiles work fine (there's a few here and there that get stuck and do nothing :p)

Trigger/callback code (that's all that I've changed)
JASS:
    private static method Move takes nothing returns nothing
        local integer i = 0
        local integer i2 = 0
        local integer i3 = 0
        local integer i4 = 0
        local real fxAngle = 0
        local real x
        local real y
        local real z
        local real ex
        local real ey
        local boolean b
        local PB a
        loop
        exitwhen i == .N
        set a = .data<i>
            loop
            exitwhen i2 == .PROJCOUNT
                if a.done[i2] == false then
                    set x = GetUnitX (a.proj[i2])
                    set y = GetUnitY (a.proj[i2])
                    set x = x + a.pcos[i2]*a.speed[i2]
                    set y = y + a.psin[i2]*a.speed[i2]
                    set z = .JumpParabola (a.curdist[i2], a.maxdist[i2], .CURVE)
                    //call BJDebugMsg (&quot;Current z - &quot; + R2S (z) + &quot;, speed - &quot; + R2S (a.speed[i2]) + &quot;, current distance - &quot; + R2S (a.curdist[i2]))
                    call .SetUnitXY (a.proj[i2], x, y)
                    set b = .SetUnitZ (a.proj[i2], z)
                    if b and a.curdist[i2] &gt; 0 then
                        set a.destroyed = a.destroyed + 1
                        set a.done[i2] = true
                        set a.curdist[i2] = 0
                        call KillUnit (a.proj[i2])
                        set .gdata = a
                        call GroupEnumUnitsInRange (.genGroup, x, y, .DamageRadius (a.caster), Condition (function PB.GroupFunc))
                        loop
                        exitwhen i3 == .FX_COUNT
                            loop
                            exitwhen i4 == .RING_COUNT
                                set fxAngle = fxAngle + .doublePi/.FX_COUNT
                                set ex = x + Cos (fxAngle) * (i4 * .RING_OFFSET)
                                set ey = y + Sin (fxAngle) * (i4 * .RING_OFFSET)
                                call DestroyEffect (AddSpecialEffect (.END_FX, ex, ey))
                                set i4 = i4 + 1
                            endloop
                            set fxAngle = 0
                            set i4 = 0
                            set i3 = i3 + 1
                        endloop
                        set i3 = 0
                    endif
                    set a.curdist[i2] = a.curdist[i2] + a.speed[i2]
                endif
                set i2 = i2 + 1
            endloop
            set i2 = 0
            
            if a.destroyed &gt;= .PROJCOUNT then
                call a.destroy ()
                set .data<i> = .data[.N]
                set .N = .N - 1
                set i = i - 1
            endif
            
            set i = i + 1
            call BJDebugMsg (I2S (i))
            call BJDebugMsg (&quot;Got this far, &quot; + I2S (i) + &quot; of &quot; + I2S (.N) + &quot; instance elapsed&quot;)
        endloop
    endmethod

    
    private static method SpellCond takes nothing returns boolean
        return GetSpellAbilityId () == .SID
    endmethod
    
    
    private static method SpellActions takes nothing returns nothing
        local PB a = PB.create ()
        local integer i = 0
        local unit u
        local unit c = GetTriggerUnit ()
        local real cx = GetUnitX (c)
        local real cy = GetUnitY (c)
        local real sx
        local real sy
        local location l = GetSpellTargetLoc ()
        local real tx = GetLocationX (l)
        local real ty = GetLocationY (l)
        local real x = tx - cx
        local real y = ty - cy
        local real rx
        local real ry
        local real angle = Atan2 (y, x)
        local real dist = SquareRoot (x*x + y*y)
        set a.caster = c
        set c = null
        set rx = cx + Cos (angle) * dist
        set ry = cy + Sin (angle) * dist
        call RemoveLocation (l)
        set l = null
        loop
        exitwhen i == .PROJCOUNT
           set sx = cx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set sy = cy + Sin (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set u = CreateUnit (GetOwningPlayer (a.caster), .LID, sx, sy, angle * bj_RADTODEG)
           //call BJDebugMsg (&quot;Angle - &quot; + R2S (angle * bj_RADTODEG))
           call SetUnitAnimation (u, .ANIM)
           call QueueUnitAnimation (u, &quot;stand&quot;)
           call UnitApplyTimedLife (u, &#039;BTLF&#039;, .LIFESPAN)
           if dist &lt;= .ACC_RANGE then
                set rx = rx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
                set ry = ry + Sin(GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
           else
                set rx = .GetRandOffsetX (rx, dist)
                set ry = .GetRandOffsetY (ry, dist)
           endif
           set x = rx-cx
           set y = ry-cy
           set angle = Atan2 (y, x)
           set a.pcos<i> = Cos (angle)
           set a.psin<i> = Sin (angle)
           set a.maxdist<i> = SquareRoot (x*x + y*y)
           set a.proj<i> = CreateUnit (GetOwningPlayer (a.caster), .PID, sx, sy, angle * bj_RADTODEG)
           call UnitAddAbility (a.proj<i>, &#039;Arav&#039;)
           call UnitRemoveAbility (a.proj<i>, &#039;Arav&#039;)
           set a.curdist<i> = 0
           set a.speed<i> = GetRandomReal (.SPEED_LOW, .SPEED_HIGH) * .TIMER_INT
           set a.done<i> = false
           set i = i + 1
        endloop
        
        
        set .N = .N + 1
        set .data[.N] = a
    
        //call BJDebugMsg (I2S (.N))
        set u = null
    endmethod</i></i></i></i></i></i></i></i></i></i></i>
 
When you create new instances you set them to array slots starting from 1 (as you first increase .N), but when looping you start from slot 0 and end to .N - 1. And when you're done with one instance you use .N as the max array slot again. :nuts:
Make the callback's i start from 1 and make it exit when i > .N?
 
you should prolly exit when i>.N

and when the hell do you start/stop your timer?

what is the current issue?
 
When you create new instances you set them to array slots starting from 1 (as you first increase .N), but when looping you start from slot 0 and end to .N - 1. And when you're done with one instance you use .N as the max array slot again. :nuts:
Make the callback's i start from 1 and make it exit when i > .N?

you should prolly exit when i>.N

and when the hell do you start/stop your timer?

what is the current issue?

@both: Hmmm, I'll give that a go (the i > .N that is) and see how it works out

@emjlr3: Timer is started in my onInit method at the moment (I was thinking that the timer was getting paused and not restarted, so I let it go continuously for testing purposes), and current issue is that some projectiles freeze (either in midair, or at starting position) and are only moved by next spellcast (which causes the new projectiles to freeze)

Current code:
JASS:
//Phantasmal barrage
//By Flare
//Requires: Projectile dummy (h000)
//          Launcher Dummy (h001)
//          Phantasmal Barrage ability (A000)
//          NewGen WE


//Thanks to:
//Vexorian - TimerUtils
//Shadow1500 - Parabola function
struct PB
//Configurables

//Ability rawcode
    private static constant integer SID = &#039;AOsh&#039;
//Launcher Dummy rawcode
    private static constant integer LID = &#039;h001&#039;
//Projectile Dummy rawcode
    private static constant integer PID = &#039;h000&#039;
//Timer interval
    private static constant real TIMER_INT = 0.03125
//Number of projectiles used
    private static constant integer PROJCOUNT = 5
//Projectile effect
    private static constant string PROJ_FX = &quot;Abilities\\Spells\\Other\\BlackArrow\\BlackArrowMissile.mdl&quot;
//Explosion effect
    private static constant string END_FX = &quot;Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl&quot;
//Number of effects spawned per ring
    private static constant integer FX_COUNT = 5
//Number of rings spawned
    private static constant integer RING_COUNT = 1
//Ring offset
    private static constant real RING_OFFSET = 75.
//Spawn offset
    private static constant real SPAWN_OFFSET = 300.
//Attachment point for projectile
    private static constant string ATTACH_PT = &quot;origin&quot;
//Low bound for projectile speed - projectile speed will be somewhere between SPEED_LOW and SPEED_HIGH
    private static constant real SPEED_LOW = 300
//High bound for projectile speed
    private static constant real SPEED_HIGH = 1000
//Projectile&#039;s target offset - base value for how far from the target point the projectile will land
    private static constant real TARGET_OFFSET = 0.
//Intensity of curve - lower values result in a &quot;sharper&quot; curve, higher values result in a bow-shaped curve
    private static constant real CURVE = 2.
//Accurate range
    private static constant real ACC_RANGE = 800.
//Accuracy discrepancy - higher values result in a less accurate barrage at longer distance
//Discrepancy only takes effect if the target point if more than ACC_RANGE units away from the caster
    private static constant real ACC_DISCREP_MULTI = 75
//For every ACC_DISCREP_MULTI units beyond ACC_RANGE, the projectiles&#039; targeting will be off by up to ACC_DISCREP units
//e.g. (using current values) if you cast the spell 1400 units away
//the projectiles will land up to ((1400-800)/50)*25 (300) units away from their original target
//NOTE: Projectile&#039;s original target is the target point offset by TARGET_OFFSET towards a random angle
    private static constant real ACC_DISCREP = 25
//Minimum damage percentage - percentage of whole damage that is dealt at the edge of damage radius
    private static constant real MIN_DMG_PERCENT = 0.25
//Attack type, damage type, weapon type
    private static constant attacktype ATYPE = ATTACK_TYPE_MAGIC
    private static constant damagetype DTYPE = DAMAGE_TYPE_NORMAL
    private static constant weapontype WTYPE = WEAPON_TYPE_WHOKNOWS
//Animation played by the launcher dummies
    private static constant string ANIM = &quot;attack&quot;
//Lifespan of launcher dummies
    private static constant real LIFESPAN = 1.
    
    
//Damage radius calculation
    private static method DamageRadius takes unit u returns real
        local real base = 150.
        local real m1 = I2R (GetUnitAbilityLevel (u, .SID))
        local real m2 = 50.
        return base + m1*m2
    endmethod
    
    
//Damage calculation
    private static method Damage takes unit u returns real
        local real base = 15
        local real m1 = GetUnitAbilityLevel (u, .SID)
        local real m2 = 5
        return base + m1*m2
    endmethod
    
    
//Required globals    
    private static location tempLoc = Location (0,0)
    private static group genGroup = CreateGroup ()
    private static timer spellTimer = CreateTimer ()
    private static PB array data
    private static PB gdata
    private static integer N = 0
    private static real groupX = 0
    private static real groupY = 0
    private static real doublePi = bj_PI * 2
    private static boolean grounded = false
    
    
//Required struct members
    unit caster
    unit array proj[.PROJCOUNT]
    real array speed[.PROJCOUNT]
    real array pcos[.PROJCOUNT]
    real array psin[.PROJCOUNT]
    real array curdist[.PROJCOUNT]
    real array maxdist[.PROJCOUNT]
    boolean array done[.PROJCOUNT]
    effect array pfx[.PROJCOUNT]
    integer destroyed = 0
    
    
    
//Required helper functions
    private static method RingEffect takes string fxString, real ringCount, real effectCount, real offset, real x, real y returns nothing
        local integer i1 = 1
        local integer i2 = 1
        local real angle = GetRandomReal (0, .doublePi)
        local real x2
        local real y2
        loop
        exitwhen i1 &gt; ringCount
            loop
            exitwhen i2 &gt; effectCount
                set angle = angle + .doublePi/effectCount
                set x2 = x + Cos (angle) * (i1 * offset)
                set y2 = y + Sin (angle) * (i1 * offset)
                call DestroyEffect(AddSpecialEffect (fxString, x2, y2))
                set i2 = i2 + 1
            endloop
            set i1 = i1 + 1
        endloop
    endmethod


    private static constant method JumpParabola takes real dist, real maxdist,real curve returns real
        local real t = (dist*2)/maxdist-1
        return (-t*t+1)*(maxdist/curve)
    endmethod
    
    
    private static method SetUnitXY takes unit u,real x,real y returns nothing
        if x&lt;GetRectMaxX(bj_mapInitialPlayableArea) and x&gt;GetRectMinX(bj_mapInitialPlayableArea) and y&lt;GetRectMaxY(bj_mapInitialPlayableArea) and y&gt;GetRectMinY(bj_mapInitialPlayableArea) then
            call SetUnitX(u,x)
            call SetUnitY(u,y)
        endif
    endmethod
    
    
    private static method GetCoordZ takes real x, real y returns real
        call MoveLocation (.tempLoc, x, y)
        return GetLocationZ (.tempLoc)
    endmethod

    
    private static method SetUnitZ takes unit u, real h returns boolean
        local real z = h - .GetCoordZ (GetUnitX (u), GetUnitY (u))
        if z &gt; 0 then
            call SetUnitFlyHeight (u, z, 0)
            return false
        else
            return true
        endif
    endmethod
    
    private static method GetUnitZ takes unit u returns real
        return .GetCoordZ (GetUnitX (u), GetUnitY (u)) + GetUnitFlyHeight (u)
    endmethod
    
    
    private static method GetRandOffsetX takes real x, real dist returns real
        local real discrepdist = ((dist - .ACC_RANGE)/.ACC_DISCREP_MULTI) * .ACC_DISCREP
        local real angle = Cos (GetRandomReal (0, .doublePi))
        return x + angle * discrepdist
    endmethod
    
    
    private static method GetRandOffsetY takes real y, real dist returns real
        local real discrepdist = ((dist - .ACC_RANGE)/.ACC_DISCREP_MULTI) * .ACC_DISCREP
        local real angle = Sin (GetRandomReal (0, .doublePi))
        return y + angle * discrepdist
    endmethod
    
    
//Spell functions
    private static method GroupFunc takes nothing returns boolean
        local PB a = .gdata
        local unit u = GetFilterUnit ()
        local real ux = GetUnitX (u)
        local real uy = GetUnitY (u)
        local real x = .groupX - ux
        local real y = .groupY - uy
        local real dist = SquareRoot (x*x + y*y)
        local real percent = (.DamageRadius (a.caster) - dist)/.DamageRadius (a.caster)
        local real dmg
        if percent &lt; .MIN_DMG_PERCENT then
            set percent = .MIN_DMG_PERCENT
        endif
        set dmg = .Damage (a.caster) * percent
        call UnitDamageTarget (a.caster, u, dmg, false, true, .ATYPE, .DTYPE, .WTYPE)
        return false
    endmethod

    
    private static method Move takes nothing returns nothing
        local integer i = 0
        local integer i2 = 0
        local integer i3 = 0
        local integer i4 = 0
        local real fxAngle = 0
        local real x
        local real y
        local real x2
        local real y2
        local real z
        local real uz
        local real ex
        local real ey
        local boolean b
        local real hDist
        local real zDif
        local integer animindex
        local PB a
        loop
        exitwhen i &gt; .N
        set a = .data<i>
            loop
            exitwhen i2 &gt; .PROJCOUNT
                if a.done[i2] == false then
                    set x = GetUnitX (a.proj[i2])
                    set y = GetUnitY (a.proj[i2])
                    set x2 = x + a.pcos[i2]*a.speed[i2]
                    set y2 = y + a.psin[i2]*a.speed[i2]
                    set z = .JumpParabola (a.curdist[i2], a.maxdist[i2], .CURVE)
                    set uz = .GetUnitZ (a.proj[i2])
                    set hDist = SquareRoot ((x2-x)*(x2-x) + (y2-y)*(y2-y))
                    set zDif = z - uz
                    set animindex = R2I (Atan2 (zDif, hDist) * bj_RADTODEG + 90)
                    //call BJDebugMsg (I2S (animindex))
                    if animindex &gt;= 0 and animindex &lt;= 180 then
                        call SetUnitAnimationByIndex (a.proj[i2], animindex)
                    endif
                    //call BJDebugMsg (&quot;Current z - &quot; + R2S (z) + &quot;, speed - &quot; + R2S (a.speed[i2]) + &quot;, current distance - &quot; + R2S (a.curdist[i2]))
                    call .SetUnitXY (a.proj[i2], x2, y2)
                    set b = .SetUnitZ (a.proj[i2], z)
                    if b and a.curdist[i2] &gt; 0 then
                        set a.destroyed = a.destroyed + 1
                        call DestroyEffect (a.pfx[i2])
                        set a.done[i2] = true
                        set a.curdist[i2] = 0
                        call KillUnit (a.proj[i2])
                        set .gdata = a
                        call GroupEnumUnitsInRange (.genGroup, x, y, .DamageRadius (a.caster), Condition (function PB.GroupFunc))
                        call .RingEffect (.END_FX, .RING_COUNT, .FX_COUNT, .RING_OFFSET, x, y)
                        set i3 = 0
                    endif
                    set a.curdist[i2] = a.curdist[i2] + a.speed[i2]
                endif
                set i2 = i2 + 1
            endloop
            set i2 = 0
            
            if a.destroyed &gt;= .PROJCOUNT then
                call a.destroy ()
                set .data<i> = .data[.N]
                set .N = .N - 1
                set i = i - 1
            endif
            
            set i = i + 1
            call BJDebugMsg (I2S (i))
            call BJDebugMsg (&quot;Got this far, &quot; + I2S (i) + &quot; of &quot; + I2S (.N) + &quot; instance elapsed&quot;)
        endloop
    endmethod

    
    private static method SpellCond takes nothing returns boolean
        return GetSpellAbilityId () == .SID
    endmethod
    
    
    private static method SpellActions takes nothing returns nothing
        local PB a = PB.create ()
        local integer i = 0
        local unit u
        local unit c = GetTriggerUnit ()
        local real cx = GetUnitX (c)
        local real cy = GetUnitY (c)
        local real sx
        local real sy
        local location l = GetSpellTargetLoc ()
        local real tx = GetLocationX (l)
        local real ty = GetLocationY (l)
        local real x = tx - cx
        local real y = ty - cy
        local real rx
        local real ry
        local real angle = Atan2 (y, x)
        local real dist = SquareRoot (x*x + y*y)
        set a.caster = c
        set c = null
        set rx = cx + Cos (angle) * dist
        set ry = cy + Sin (angle) * dist
        call RemoveLocation (l)
        set l = null
        loop
        exitwhen i == .PROJCOUNT
           set sx = cx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set sy = cy + Sin (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set u = CreateUnit (GetOwningPlayer (a.caster), .LID, sx, sy, angle * bj_RADTODEG)
           //call BJDebugMsg (&quot;Angle - &quot; + R2S (angle * bj_RADTODEG))
           call SetUnitAnimation (u, .ANIM)
           call QueueUnitAnimation (u, &quot;stand&quot;)
           call UnitApplyTimedLife (u, &#039;BTLF&#039;, .LIFESPAN)
           if dist &lt;= .ACC_RANGE then
                set rx = rx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
                set ry = ry + Sin(GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
           else
                set rx = .GetRandOffsetX (rx, dist)
                set ry = .GetRandOffsetY (ry, dist)
           endif
           set x = rx-cx
           set y = ry-cy
           set angle = Atan2 (y, x)
           set a.pcos<i> = Cos (angle)
           set a.psin<i> = Sin (angle)
           set a.maxdist<i> = SquareRoot (x*x + y*y)
           set a.proj<i> = CreateUnit (GetOwningPlayer (a.caster), .PID, sx, sy, angle * bj_RADTODEG)
           call UnitAddAbility (a.proj<i>, &#039;Arav&#039;)
           call UnitRemoveAbility (a.proj<i>, &#039;Arav&#039;)
           set a.curdist<i> = 0
           set a.speed<i> = GetRandomReal (.SPEED_LOW, .SPEED_HIGH) * .TIMER_INT
           set a.done<i> = false
           set a.pfx<i> = AddSpecialEffectTarget (.PROJ_FX, a.proj<i>, .ATTACH_PT)
           set i = i + 1
        endloop
        
        
        set .N = .N + 1
        set .data[.N] = a
    
        //call BJDebugMsg (I2S (.N))
        set u = null
    endmethod
    
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger ()
        call TriggerRegisterAnyUnitEventBJ (t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition (t, Condition (function PB.SpellCond))
        call TriggerAddAction (t, function PB.SpellActions)
        call TimerStart (.spellTimer, .TIMER_INT, true, function PB.Move)
    endmethod
endstruct</i></i></i></i></i></i></i></i></i></i></i></i></i>
 
sounds like your instances are getting overlapped:

start i at 1 in Move, there should be no .data[0] right?

I see no other reason why they should overlap
 
Hmmm, seems I've broken things some more...

I decided to drop the arrayed struct members in favour of one struct instance/projectile, and it works fine, for a single instance at a time.
If I try to cast again while a projectile is in flight:
  • Previous projectile freezes
  • New projectile flies fine*
  • -ALL- spell instances after that don't work at all
*Assuming the new projectile was the first attempt to test MUI capabilities

JASS:
//Phantasmal barrage
//By Flare
//Requires: Projectile dummy (h000)
//          Launcher Dummy (h001)
//          Phantasmal Barrage ability (A000)
//          NewGen WE
//          dummy.mdx


//Thanks to:
//Shadow1500 - Parabola function
//???        - dummy.mdx
//Please let me know who the creator of the dummy.mdx model is, so I can credit them <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />

struct PB
//Configurables

//Ability rawcode
    private static constant integer SID = &#039;AOsh&#039;
//Launcher Dummy rawcode
    private static constant integer LID = &#039;h001&#039;
//Projectile Dummy rawcode
    private static constant integer PID = &#039;h000&#039;
//Timer interval
    private static constant real TIMER_INT = 0.1
//Number of projectiles used
    private static constant integer PROJCOUNT = 1
//Projectile effect
    private static constant string PROJ_FX = &quot;Abilities\\Spells\\Other\\BlackArrow\\BlackArrowMissile.mdl&quot;
//Explosion effect
    private static constant string END_FX = &quot;Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl&quot;
//Number of effects spawned per ring
    private static constant integer FX_COUNT = 1
//Number of rings spawned
    private static constant integer RING_COUNT = 1
//Ring offset
    private static constant real RING_OFFSET = 0.
//Spawn offset
    private static constant real SPAWN_OFFSET = 300.
//Attachment point for projectile
    private static constant string ATTACH_PT = &quot;origin&quot;
//Low bound for projectile speed - projectile speed will be somewhere between SPEED_LOW and SPEED_HIGH
    private static constant real SPEED_LOW = 300
//High bound for projectile speed
    private static constant real SPEED_HIGH = 1000
//Projectile&#039;s target offset - base value for how far from the target point the projectile will land
    private static constant real TARGET_OFFSET = 0.
//Intensity of curve - lower values result in a &quot;sharper&quot; curve, higher values result in a bow-shaped curve
    private static constant real CURVE = 2.
//Accurate range
    private static constant real ACC_RANGE = 800.
//Accuracy discrepancy - higher values result in a less accurate barrage at longer distance
//Discrepancy only takes effect if the target point if more than ACC_RANGE units away from the caster
    private static constant real ACC_DISCREP_MULTI = 75
//For every ACC_DISCREP_MULTI units beyond ACC_RANGE, the projectiles&#039; targeting will be off by up to ACC_DISCREP units
//e.g. (using current values) if you cast the spell 1400 units away
//the projectiles will land up to ((1400-800)/75)*25 (200) units away from their original target
//NOTE: Projectile&#039;s original target is the target point offset by TARGET_OFFSET towards a random angle
    private static constant real ACC_DISCREP = 25
//Minimum damage percentage - percentage of whole damage that is dealt at the edge of damage radius
    private static constant real MIN_DMG_PERCENT = 0.25
//Attack type, damage type, weapon type
    private static constant attacktype ATYPE = ATTACK_TYPE_MAGIC
    private static constant damagetype DTYPE = DAMAGE_TYPE_NORMAL
    private static constant weapontype WTYPE = WEAPON_TYPE_WHOKNOWS
//Animation played by the launcher dummies
    private static constant string ANIM = &quot;attack&quot;
//Lifespan of launcher dummies
    private static constant real LIFESPAN = 1.
    
//RGBA and scaling values
//Launcher RGBA and scaling
    private static constant integer LR = 255
    private static constant integer LG = 255
    private static constant integer LB = 255
    private static constant integer LA = 127
    
    private static constant real LS = 1.
    
//Projectile RGBA and scaling
    private static constant integer PR = 255
    private static constant integer PG = 255
    private static constant integer PB = 255
    private static constant integer PA = 255
    
    private static constant real PS = 1.25
    
    
//Damage radius calculation
    private static method DamageRadius takes unit u returns real
        local real base = 150.
        local real m1 = I2R (GetUnitAbilityLevel (u, .SID))
        local real m2 = 50.
        return base + m1*m2
    endmethod
    
    
//Damage calculation
    private static method Damage takes unit u returns real
        local real base = 15
        local real m1 = GetUnitAbilityLevel (u, .SID)
        local real m2 = 5
        return base + m1*m2
    endmethod
    
    
//Required globals    
    private static location tempLoc = Location (0,0)
    private static group genGroup = CreateGroup ()
    private static timer spellTimer = CreateTimer ()
    private static PB array data
    private static PB gdata = 0
    private static integer N = 0
    private static real groupX = 0
    private static real groupY = 0
    private static real doublePi = bj_PI * 2
    private static boolean timerStarted = false
    
    
//Required struct members
    unit caster
    unit proj
    real speed
    real pcos
    real psin
    real curdist
    real maxdist
    effect pfx
    
    
    
//Required helper functions
    private static method RingEffect takes string fxString, real ringCount, real effectCount, real offset, real x, real y returns nothing
        local integer i1 = 1
        local integer i2 = 1
        local real angle = GetRandomReal (0, .doublePi)
        local real x2
        local real y2
        loop
        exitwhen i1 &gt; ringCount
            loop
            exitwhen i2 &gt; effectCount
                set angle = angle + .doublePi/effectCount
                set x2 = x + Cos (angle) * (i1 * offset)
                set y2 = y + Sin (angle) * (i1 * offset)
                call DestroyEffect(AddSpecialEffect (fxString, x2, y2))
                set i2 = i2 + 1
            endloop
            set i1 = i1 + 1
        endloop
    endmethod


    private static constant method JumpParabola takes real dist, real maxdist,real curve returns real
        local real t = (dist*2)/maxdist-1
        return (-t*t+1)*(maxdist/curve)
    endmethod
    
    
    private static method SetUnitXY takes unit u,real x,real y returns nothing
        if x&lt;GetRectMaxX(bj_mapInitialPlayableArea) and x&gt;GetRectMinX(bj_mapInitialPlayableArea) and y&lt;GetRectMaxY(bj_mapInitialPlayableArea) and y&gt;GetRectMinY(bj_mapInitialPlayableArea) then
            call SetUnitX(u,x)
            call SetUnitY(u,y)
        endif
    endmethod
    
    
    private static method GetCoordZ takes real x, real y returns real
        call MoveLocation (.tempLoc, x, y)
        return GetLocationZ (.tempLoc)
    endmethod

    
    private static method SetUnitZ takes unit u, real h returns boolean
        local real z = h - .GetCoordZ (GetUnitX (u), GetUnitY (u))
        if z &gt; 0 then
            call SetUnitFlyHeight (u, z, 0)
            return false
        else
            return true
        endif
    endmethod
    
    private static method GetUnitZ takes unit u returns real
        return .GetCoordZ (GetUnitX (u), GetUnitY (u)) + GetUnitFlyHeight (u)
    endmethod
    
    
    private static method GetRandOffsetX takes real x, real dist returns real
        local real discrepdist = ((dist - .ACC_RANGE)/.ACC_DISCREP_MULTI) * .ACC_DISCREP
        local real angle = Cos (GetRandomReal (0, .doublePi))
        return x + angle * discrepdist
    endmethod
    
    
    private static method GetRandOffsetY takes real y, real dist returns real
        local real discrepdist = ((dist - .ACC_RANGE)/.ACC_DISCREP_MULTI) * .ACC_DISCREP
        local real angle = Sin (GetRandomReal (0, .doublePi))
        return y + angle * discrepdist
    endmethod
    
    
//Spell functions
    private static method GroupFunc takes nothing returns boolean
        local PB a = .gdata
        local unit u = GetFilterUnit ()
        local real ux = GetUnitX (u)
        local real uy = GetUnitY (u)
        local real x = .groupX - ux
        local real y = .groupY - uy
        local real dist = SquareRoot (x*x + y*y)
        local real percent = (.DamageRadius (a.caster) - dist)/.DamageRadius (a.caster)
        local real dmg
        if percent &lt; .MIN_DMG_PERCENT then
            set percent = .MIN_DMG_PERCENT
        endif
        set dmg = .Damage (a.caster) * percent
        call UnitDamageTarget (a.caster, u, dmg, false, true, .ATYPE, .DTYPE, .WTYPE)
        return false
    endmethod
    
    
    private method onDestroy takes nothing returns nothing
        call BJDebugMsg (&quot;.destroy was successfully called&quot;)
    endmethod

    
    private static method Move takes nothing returns nothing
        local integer i = 0
        local integer i2 = 0
        local real fxAngle = 0
        local real x = 0
        local real y = 0
        local real x2 = 0
        local real y2 = 0
        local real z = 0
        local real uz = 0
        local real ex = 0
        local real ey = 0
        local boolean b = false
        local real hDist = 0
        local real zDif = 0
        local integer animindex = 0
        local PB a
        call BJDebugMsg (&quot;Timer is running, N is &quot; + I2S (.N))
        loop
        exitwhen i &gt; .N
            //call BJDebugMsg (&quot;&gt;-&gt;-&gt;-&gt;Moving on to instance #&quot; + I2S (i) + &quot;&lt;-&lt;-&lt;-&lt;&quot;)
            //call BJDebugMsg (&quot;&gt;&gt;&gt;-----------------------------&lt;&lt;&lt;&quot;)
            set a = .data<i>
            set x = GetUnitX (a.proj)
            set y = GetUnitY (a.proj)
            set x2 = x + a.pcos*a.speed
            set y2 = y + a.psin*a.speed
            set z = .JumpParabola (a.curdist, a.maxdist, .CURVE)
            set uz = .GetUnitZ (a.proj)
            set hDist = SquareRoot ((x2-x)*(x2-x) + (y2-y)*(y2-y))
            set zDif = z - uz
            set animindex = R2I (Atan2 (zDif, hDist) * bj_RADTODEG + 90)
            
            if animindex &gt;= 0 and animindex &lt;= 180 then
                call SetUnitAnimationByIndex (a.proj, animindex)
            endif
            
            call .SetUnitXY (a.proj, x2, y2)
            set b = .SetUnitZ (a.proj, z)
            
            if b and a.curdist &gt; 0 then
                call DestroyEffect (a.pfx)
                set a.curdist = 0
                call KillUnit (a.proj)
                set .gdata = a
                call GroupEnumUnitsInRange (.genGroup, x, y, .DamageRadius (a.caster), Condition (function PB.GroupFunc))
                call .RingEffect (.END_FX, .RING_COUNT, .FX_COUNT, .RING_OFFSET, x, y)
                call a.destroy ()
                set .data<i> = .data[.N]
                set .N = .N - 1
                set i = i - 1
                
                if .N == 0 and .timerStarted then
                    call PauseTimer (.spellTimer)
                    set .timerStarted = false
                endif
            endif
            
            set a.curdist = a.curdist + a.speed
            set i = i + 1
        endloop
    endmethod

    
    private static method SpellCond takes nothing returns boolean
        return GetSpellAbilityId () == .SID
    endmethod
    
    
    private static method SpellActions takes nothing returns nothing
        local PB a
        local integer i = 0
        local unit u
        local unit c = GetTriggerUnit ()
        local real cx = GetUnitX (c)
        local real cy = GetUnitY (c)
        local real sx
        local real sy
        local location l = GetSpellTargetLoc ()
        local real tx = GetLocationX (l)
        local real ty = GetLocationY (l)
        local real x = tx - cx
        local real y = ty - cy
        local real rx
        local real ry
        local real angle = Atan2 (y, x)
        local real dist = SquareRoot (x*x + y*y)
        set c = null
        set rx = cx + Cos (angle) * dist
        set ry = cy + Sin (angle) * dist
        call RemoveLocation (l)
        set l = null
        
        if .timerStarted == false then
            call TimerStart (.spellTimer, .TIMER_INT, true, function PB.Move)
        endif
        
        loop
        exitwhen i == .PROJCOUNT
           set a = PB.create ()
           //call BJDebugMsg (&quot;Struct created&quot;)
           set a.caster = GetTriggerUnit ()
           set sx = cx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set sy = cy + Sin (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .SPAWN_OFFSET)
           set u = CreateUnit (GetOwningPlayer (a.caster), .LID, sx, sy, angle * bj_RADTODEG)
           //call BJDebugMsg (&quot;Launcher created, it&#039;s name is &quot; + GetUnitName (u))
           call SetUnitVertexColor (u, .LR, .LG, .LB, .LA)
           call SetUnitScale (u, .LS, .LS, .LS)
           call SetUnitAnimation (u, .ANIM)
           call QueueUnitAnimation (u, &quot;stand&quot;)
           call UnitApplyTimedLife (u, &#039;BTLF&#039;, .LIFESPAN)
           //call BJDebugMsg (&quot;Launcher aesthetics applied&quot;)
           if dist &lt;= .ACC_RANGE then
                set rx = rx + Cos (GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
                set ry = ry + Sin(GetRandomReal (0, .doublePi)) * GetRandomReal (0, .TARGET_OFFSET)
           else
                set rx = .GetRandOffsetX (rx, dist)
                set ry = .GetRandOffsetY (ry, dist)
           endif
           set x = rx-cx
           set y = ry-cy
           set angle = Atan2 (y, x)
           set a.pcos = Cos (angle)
           set a.psin = Sin (angle)
           set a.maxdist = SquareRoot (x*x + y*y)
           //call BJDebugMsg (&quot;Projectile particulars calculated&quot;)
           set a.proj = CreateUnit (GetOwningPlayer (a.caster), .PID, sx, sy, angle * bj_RADTODEG)
           call SetUnitVertexColor (a.proj, .PR, .PG, .PB, .PA)
           call UnitAddAbility (a.proj, &#039;Arav&#039;)
           call UnitRemoveAbility (a.proj, &#039;Arav&#039;)
           //call BJDebugMsg (&quot;Projectile created, it&#039;s name is &quot; + GetUnitName (a.proj) + &quot;, aesthetics applied&quot;)
           set a.curdist = 0
           set a.speed = GetRandomReal (.SPEED_LOW, .SPEED_HIGH) * .TIMER_INT
           set a.pfx = AddSpecialEffectTarget (.PROJ_FX, a.proj, .ATTACH_PT)
           set i = i + 1
           set .data[.N] = a
           set .N = .N + 1
           call BJDebugMsg (&quot;Total instances incremented (&quot; + I2S (.N) + &quot;)&quot; + &quot;, struct variable (a) assigned to array&quot;)
        endloop
    
        set u = null
    endmethod
    
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger ()
        call TriggerRegisterAnyUnitEventBJ (t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition (t, Condition (function PB.SpellCond))
        call TriggerAddAction (t, function PB.SpellActions)
    endmethod
endstruct</i></i>
 
the fact that you have everything within your struct bothers me, I have never seen anyone do it before, nor have I done it myself - put all your globals and config functions outside the struct please, and try it again
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    News portal has been retired. Main page of site goes to Headline News forum now
  • The Helper The Helper:
    I am working on getting access to the old news portal under a different URL for those that would rather use that for news before we get a different news view.
  • Ghan Ghan:
    Easily done
    +1
  • The Helper The Helper:
    https://www.thehelper.net/pages/news/ is a link to the old news portal - i will integrate it into the interface somewhere when i figure it out
  • Ghan Ghan:
    Need to try something
  • Ghan Ghan:
    Hopefully this won't cause problems.
  • Ghan Ghan:
    Hmm
  • Ghan Ghan:
    I have converted the Headline News forum to an Article type forum. It will now show the top 20 threads with more detail of each thread.
  • Ghan Ghan:
    See how we like that.
  • The Helper The Helper:
    I do not see a way to go past the 1st page of posts on the forum though
  • The Helper The Helper:
    It is OK though for the main page to open up on the forum in the view it was before. As long as the portal has its own URL so it can be viewed that way I do want to try it as a regular forum view for a while
  • Ghan Ghan:
    Yeah I'm not sure what the deal is with the pagination.
  • Ghan Ghan:
    It SHOULD be there so I think it might just be an artifact of having an older style.
  • Ghan Ghan:
    I switched it to a "Standard" article forum. This will show the thread list like normal, but the threads themselves will have the first post set up above the rest of the "comments"
  • The Helper The Helper:
    I don't really get that article forum but I think it is because I have never really seen it used on a multi post thread
  • Ghan Ghan:
    RpNation makes more use of it right now as an example: https://www.rpnation.com/news/
  • The Helper The Helper:
  • The Helper The Helper:
    What do you think Tom?
  • tom_mai78101 tom_mai78101:
    I will have to get used to this.
  • tom_mai78101 tom_mai78101:
    The latest news feed looks good
  • The Helper The Helper:
    I would like to see it again like Ghan had it the first time with pagination though - without the pagination that view will not work but with pagination it just might...
  • The Helper The Helper:
    This drink recipe I have had more than a few times back in the day! Mind Eraser https://www.thehelper.net/threads/cocktail-mind-eraser.194720/

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top