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?
 

Artificial

Without Intelligence
Reaction score
326
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
 

Forty

New Member
Reaction score
6
JASS:

        set .N = .N + 1


maybe im wrong but shouldnt be .N be IN the loop? (spellactions function)
 

Flare

Stops copies me!
Reaction score
662
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
 

Artificial

Without Intelligence
Reaction score
326
> 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? ^_^
 

Flare

Stops copies me!
Reaction score
662
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>
 

Artificial

Without Intelligence
Reaction score
326
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?
 

emjlr3

Change can be a good thing
Reaction score
395
you should prolly exit when i>.N

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

what is the current issue?
 

Flare

Stops copies me!
Reaction score
662
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>
 

emjlr3

Change can be a good thing
Reaction score
395
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
 

Flare

Stops copies me!
Reaction score
662
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>
 

emjlr3

Change can be a good thing
Reaction score
395
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.
  • Ghan Ghan:
    Howdy
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top