HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Lightning spell strange BUG ..

09-20-2008, 04:39 PM#1
Flame_Phoenix
Hi guys, I started working on a lightning spell for my map. This spell will be better than any system IMO because for the first time (and only, afaik) it will allow the user to choose any model he wants for the lightning effect.
However this is not a lightning system, it is merely a lightning spell.
Anyway, such thing will never happen if I don't fix a bug.
If you run my code, it does fine, however.
However, when I was giving the final touch an evil thing happened. I found a loop doesn't work. And it isn't even a complicated loop, just a simple stupid loop...
My problem is in this loop:

Collapse JASS:
private function Actions takes nothing returns nothing
        local SpellData data = SpellData.create(GetTriggerUnit())
        local integer i
        
        set data.vic = GetSpellTargetUnit()
        
        //here we add the caster the the target to the picked group
        call GroupAddUnit(data.picked, data.caster)
        call GroupAddUnit(data.picked, data.vic)
        
        //Here we move the projectile
        call MoveMissile(data)
            
        //Pick new Target
        call NextTarget(data)
            
        //Here we move the projectile
        call MoveMissile(data)

//        WHY THE LOOP DOES NOT WORK ????
//        loop
//            exitwhen (i == TargetsNumber(data.level))
//            //Here we move the projectile
//            call MoveMissile(data)
//        
//            //Pick new Target
//            call NextTarget(data)
//            set i = i + 1
//        endloop
        
        call data.destroy()
    endfunction

I must use that loop, but I can't. However, if I use the instructions without the loop (as you can see I call same functions before loop) the code works just fine ...
Can any one find me a stupid reason for why this stupid mistake is happening ?

Full Code (it's really easy it just has lots of comments...):
Collapse JASS:
//===========================================================================
//A JESP spell that allows the user to create lightning spells with any model
//he desires.
//
//Requires TimerUtils
//
//@author Flame_Phoenix 
//
//@credits
//- Daelin, for his tutorials and codes
//
//@version 1.0
//===========================================================================
scope Lightning initializer Init

    private keyword tmpPlayer 
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
   globals
        private constant integer AID = 'A000'//'AUdc'   //rw of teh ability
        private constant real SPEED = 900.  //speed of the missile
        private constant integer MISSILE_ID = 'h000'    //rw of the missile
        private constant real TIMER_CICLE = 0.03    //cicles of the timer
    endglobals
    
    private function Range takes integer level returns real
    //If there is more than one Target, a next target will be picked in a 500
    //AOE from the first
        return 500. + (level * 0)   
    endfunction
    
    private function Damage takes integer level returns real
    //Damage each Target will take
        return 100. * level
    endfunction
    
    private function TargetsNumber takes integer level returns integer
    //The number of targets
        return 5 + (level * 0)
    endfunction
    
    private function AceptedTargets takes nothing returns boolean
        return IsUnitEnemy(GetFilterUnit(), tmpPlayer) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================

    globals
        private group g
        private boolexpr b
        private player tmpPlayer = null
    endglobals
    
    private struct SpellData 
        unit caster 
        unit vic    //the current victim
        integer level
        group picked   //saves all targeted units so far, so they don't get picked again
        timer t
        unit missile
        real wait
        
        static method create takes unit caster returns SpellData
            local SpellData data = SpellData.allocate()
            
            //setting variables
            set data.caster = caster
            set data.vic = null
            set data.level = GetUnitAbilityLevel(caster, AID)
            set data.picked = CreateGroup()
            set data.missile = CreateUnit(GetOwningPlayer(caster),  MISSILE_ID, GetUnitX(caster), GetUnitY(caster), 0)  
            set data.wait = 0.
            
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            call DestroyGroup(.picked)
            
            call ShowUnit(.missile, false)
            call KillUnit(.missile)
        endmethod
    endstruct
//=========================================================================== 
    private function NextTarget takes integer structure returns nothing
        local SpellData data = structure
        local unit f
        
        set tmpPlayer = GetOwningPlayer(data.caster)
        call GroupEnumUnitsInRange(g, GetUnitX(data.vic), GetUnitY(data.vic), 500., b)
        set data.vic = GroupPickRandomUnit(g)
        call GroupAddUnit(data.picked, data.vic)
        loop
            set f = FirstOfGroup(g)
            exitwhen(f == null)
            call GroupRemoveUnit(g, f)
        endloop
    endfunction
//=========================================================================== 
    private function TargetEffect takes integer structure returns nothing
        local SpellData data = structure
        call UnitDamageTarget(data.caster, data.vic, Damage(data.level), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
    endfunction
//=========================================================================== 
    private function Move takes nothing returns nothing
        local SpellData data = SpellData(GetTimerData(GetExpiredTimer())) 
        
        local real x1 = GetUnitX(data.missile)
        local real x2 = GetUnitX(data.vic)
        local real y1 = GetUnitY(data.missile)
        local real y2 = GetUnitY(data.vic)
        
        local real dx = TIMER_CICLE * (x2 - x1) / data.wait  
        local real dy = TIMER_CICLE * (y2 - y1) / data.wait
        
        call SetUnitPosition(data.missile, x1 + dx, y1 + dy)
        set data.wait = data.wait - TIMER_CICLE
        
        if data.wait < TIMER_CICLE then
            call SetUnitPosition(data.missile, x2, y2)
            call ReleaseTimer(data.t)
            call TargetEffect(data)
        endif
    endfunction
//===========================================================================
    private function MoveMissile takes integer structure returns nothing
        local SpellData data = structure
        local real a = GetUnitX(data.missile) - GetUnitX(data.vic)
        local real b = GetUnitY(data.missile) - GetUnitY(data.vic)
        local real d = SquareRoot(a*a + b*b) //the distance between "a" and "b"
        set data.t = NewTimer()
        set data.wait = d / SPEED
        
        //we attach the struct to the timer and we start it
        call SetTimerData(data.t, integer(data))
        call TimerStart(data.t, TIMER_CICLE, true, function Move)
        
        //gotta fix this =S
        call PolledWait(data.wait)
    endfunction
//===========================================================================    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == AID
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local SpellData data = SpellData.create(GetTriggerUnit())
        local integer i
        
        set data.vic = GetSpellTargetUnit()
        
        //here we add the caster the the target to the picked group
        call GroupAddUnit(data.picked, data.caster)
        call GroupAddUnit(data.picked, data.vic)
        
        //Here we move the projectile
        call MoveMissile(data)
            
        //Pick new Target
        call NextTarget(data)
            
        //Here we move the projectile
        call MoveMissile(data)

//        WHY THE LOOP DOES NOT WORK ????
//        loop
//            exitwhen (i == TargetsNumber(data.level))
//            //Here we move the projectile
//            call MoveMissile(data)
//        
//            //Pick new Target
//            call NextTarget(data)
//            set i = i + 1
//        endloop
        
        call data.destroy()
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger LightningTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( LightningTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( LightningTrg, Condition( function Conditions ) )
        call TriggerAddAction(LightningTrg, function Actions )
        
        set LightningTrg = null
        
        //setting globals
        set b = Condition(function AceptedTargets)
        set g = CreateGroup()
    endfunction
endscope

Is there a chance the problem is on the "call PolledWait(data.wait)" line ?
I don't understand, I saw this same thing in another code, and it worked just fine (in fact it is approved here in wc3c and it still works).

HELP !
+credits and rep will be given

Anyway, here is the map ... enjoy killing peasants !
Attached Files
File type: w3xlightning.w3x (24.1 KB)
09-20-2008, 04:47 PM#2
Crevax
You didn't give an initial value to i.
09-20-2008, 07:35 PM#3
Flame_Phoenix
LOL ... what a dummy mistake .. Anyway thx for help, +rep

EDIT EDIT EDIT

ARRGHHHH ! WHY IS THIS TORTURING ME !
WHYYY !!!!

Guys, Now I have another problem, but this one is a lot more complicated =(
When my code doesn't find a unit to kill, it just goes mad !!! It either kills the target instantly or even worse I get a damn annoying message saying:
"Warning: ReleaseTimer: Double free!"

And then my spell blows and it never ever works ever again ! EVER !
Please What am I doing wrong ?

PS: here you guys have map again, for you joy (or not lol)

Collapse JASS:
globals
        private group g
        private boolexpr b
        private player tmpPlayer = null
    endglobals
    
    private struct SpellData 
        unit caster 
        unit vic    //the current victim
        integer level
        group picked   //saves all targeted units so far, so they don't get picked again
        timer t
        unit missile
        real wait
        integer i   //counter for loop in function ACtions
        
        static method create takes unit caster returns SpellData
            local SpellData data = SpellData.allocate()
            
            //setting variables
            set data.caster = caster
            set data.vic = null
            set data.level = GetUnitAbilityLevel(caster, AID)
            set data.picked = CreateGroup()
            set data.missile = CreateUnit(GetOwningPlayer(caster),  MISSILE_ID, GetUnitX(caster), GetUnitY(caster), 0)  
            set data.wait = 0.
            set data.i = 0
            
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            call DestroyGroup(.picked)

            call ShowUnit(.missile, false)
            call KillUnit(.missile)
        endmethod
    endstruct
//=========================================================================== 
    private function NextTarget takes integer structure returns unit
        local SpellData data = structure
        local unit f
        local unit ret
        
        set tmpPlayer = GetOwningPlayer(data.caster)
        call GroupEnumUnitsInRange(g, GetUnitX(data.vic), GetUnitY(data.vic), Range(data.level), b)
        set ret = GroupPickRandomUnit(g)
        call GroupAddUnit(data.picked, ret)
        loop
            set f = FirstOfGroup(g)
            exitwhen(f == null)
            call GroupRemoveUnit(g, f)
        endloop
        
        if ret == null then
            call ReleaseTimer(data.t)
        endif
        
        return ret
    endfunction
//=========================================================================== 
    private function TargetEffect takes integer structure returns nothing
        local SpellData data = structure
        call UnitDamageTarget(data.caster, data.vic, Damage(data.level) - (Reduction(data.level) * 100 * data.i), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
    endfunction
//=========================================================================== 
    private function Move takes nothing returns nothing
        local SpellData data = SpellData(GetTimerData(GetExpiredTimer())) 
        
        local real x1 = GetUnitX(data.missile)
        local real x2 = GetUnitX(data.vic)
        local real y1 = GetUnitY(data.missile)
        local real y2 = GetUnitY(data.vic)
        
        local real dx = TIMER_CICLE * (x2 - x1) / data.wait  
        local real dy = TIMER_CICLE * (y2 - y1) / data.wait
        
        call SetUnitPosition(data.missile, x1 + dx, y1 + dy)
        set data.wait = data.wait - TIMER_CICLE
        
        if data.wait < TIMER_CICLE then
            call SetUnitPosition(data.missile, x2, y2)
            call ReleaseTimer(data.t)
            call TargetEffect(data)
        endif
    endfunction
//===========================================================================
    private function MoveMissile takes integer structure returns nothing
        local SpellData data = structure
        local real a = GetUnitX(data.missile) - GetUnitX(data.vic)
        local real b = GetUnitY(data.missile) - GetUnitY(data.vic)
        local real d = SquareRoot(a*a + b*b) //the distance between "a" and "b"
        set data.t = NewTimer()
        set data.wait = d / SPEED
        
        //we attach the struct to the timer and we start it
        call SetTimerData(data.t, integer(data))
        call TimerStart(data.t, TIMER_CICLE, true, function Move)
        
        //gotta fix this =S
        call PolledWait(data.wait)
    endfunction
//===========================================================================    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == AID
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local SpellData data = SpellData.create(GetTriggerUnit())
        
        set data.vic = GetSpellTargetUnit()
        
        //here we add the caster the the target to the picked group
        call GroupAddUnit(data.picked, data.caster)
        call GroupAddUnit(data.picked, data.vic)
        
        loop
            exitwhen (data.i == TargetsNumber(data.level)) or (data.vic == null)
            //Here we move the projectile
            call MoveMissile(data)
        
            //Pick new Target
            set data.vic = NextTarget(data)
            set data.i = data.i + 1
        endloop
        
        call data.destroy()
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger LightningTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( LightningTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( LightningTrg, Condition( function Conditions ) )
        call TriggerAddAction(LightningTrg, function Actions )
        
        set LightningTrg = null
        
        //setting globals
        set b = Condition(function AceptedTargets)
        set g = CreateGroup()
    endfunction
endscope

Again, +rep and credit, help !
Attached Files
File type: w3xlightning.w3x (24.1 KB)
09-20-2008, 08:17 PM#4
Bobo_The_Kodo
Something like this?:

Collapse JASS:
//===========================================================================
//A JESP spell that allows the user to create lightning spells with any model
//he desires.
//
//Requires TimerUtils
//
//@author Flame_Phoenix 
//
//@credits
//- Daelin, for his tutorials and codes
//
//@version 1.0
//===========================================================================
scope Lightning initializer Init

    private keyword tmpPlayer 
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
   globals
        private constant integer AID = 'A000'//'AUdc'   //rw of teh ability
        private constant real SPEED = 900.  //speed of the missile
        private constant integer MISSILE_ID = 'h000'    //rw of the missile
        private constant real TIMER_CICLE = 0.03    //cicles of the timer
    endglobals
    
    private function Range takes integer level returns real
    //If there is more than one Target, a next target will be picked in a 500
    //AOE from the first
        return 100. + (level * 0)   
    endfunction
    
    private function Damage takes integer level returns real
    //Damage each Target will take
        return 100. * level
    endfunction
    
    private function Reduction takes integer level returns real
    //Damage reduction per Target
        return 0.15 + (level * 0)
    endfunction
    
    private function TargetsNumber takes integer level returns integer
    //The number of targets
        return 5 + (level * 0)
    endfunction
    
    private function AceptedTargets takes nothing returns boolean
        return IsUnitEnemy(GetFilterUnit(), tmpPlayer) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================

    globals
        private group g
        private boolexpr b
        private player tmpPlayer = null
    endglobals
    
    private struct SpellData 
        unit caster 
        unit vic    //the current victim
        integer level
        group picked   //saves all targeted units so far, so they don't get picked again
        timer t
        unit missile
        real wait
        integer i   //counter for loop in function ACtions
        
        static method create takes unit caster returns SpellData
            local SpellData data = SpellData.allocate()
            
            //setting variables
            set data.caster = caster
            set data.vic = null
            set data.level = GetUnitAbilityLevel(caster, AID)
            set data.picked = CreateGroup()
            set data.missile = CreateUnit(GetOwningPlayer(caster),  MISSILE_ID, GetUnitX(caster), GetUnitY(caster), 0)  
            set data.wait = 0.
            set data.i = 0
            
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            call DestroyGroup(.picked)

            call ShowUnit(.missile, false)
            call KillUnit(.missile)
        endmethod
    endstruct
//=========================================================================== 
    private function NextTarget takes integer structure returns unit
        local SpellData data = structure
        local unit f
        local unit ret
        
        set tmpPlayer = GetOwningPlayer(data.caster)
        call GroupEnumUnitsInRange(g, GetUnitX(data.vic), GetUnitY(data.vic), Range(data.level), b)
        set ret = GroupPickRandomUnit(g)
        loop
            call GroupRemoveUnit( g, ret )
            if not IsUnitInGroup( ret, data.picked ) or ret == null then
                exitwhen true
            endif
            set ret = GroupPickRandomUnit(g)
        endloop
            
        if ret != null then
            call GroupAddUnit(data.picked, ret)
            loop
                set f = FirstOfGroup(g)
                exitwhen(f == null)
                call GroupRemoveUnit(g, f)
            endloop
        endif
        
        return ret
    endfunction
//=========================================================================== 
    private function TargetEffect takes integer structure returns nothing
        local SpellData data = structure
        call UnitDamageTarget(data.caster, data.vic, Damage(data.level) - (Reduction(data.level) * 100 * data.i), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
    endfunction
//=========================================================================== 
    private function Move takes nothing returns nothing
        local SpellData data = SpellData(GetTimerData(GetExpiredTimer())) 
        
        local real x1 = GetUnitX(data.missile)
        local real x2 = GetUnitX(data.vic)
        local real y1 = GetUnitY(data.missile)
        local real y2 = GetUnitY(data.vic)
        
        local real dx = TIMER_CICLE * (x2 - x1) / data.wait  
        local real dy = TIMER_CICLE * (y2 - y1) / data.wait
        
        call SetUnitPosition(data.missile, x1 + dx, y1 + dy)
        set data.wait = data.wait - TIMER_CICLE
        
        if data.wait < TIMER_CICLE then
            call SetUnitPosition(data.missile, x2, y2)
            call ReleaseTimer(data.t)
            call TargetEffect(data)
        endif
    endfunction
//===========================================================================
    private function MoveMissile takes integer structure returns nothing
        local SpellData data = structure
        local real a = GetUnitX(data.missile) - GetUnitX(data.vic)
        local real b = GetUnitY(data.missile) - GetUnitY(data.vic)
        local real d = SquareRoot(a*a + b*b) //the distance between "a" and "b"
        set data.t = NewTimer()
        set data.wait = d / SPEED
        
        //we attach the struct to the timer and we start it
        call SetTimerData(data.t, integer(data))
        call TimerStart(data.t, TIMER_CICLE, true, function Move)
        
        //gotta fix this =S
        call PolledWait(data.wait)
    endfunction
//===========================================================================    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == AID
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local SpellData data = SpellData.create(GetTriggerUnit())
        
        set data.vic = GetSpellTargetUnit()
        
        //here we add the caster the the target to the picked group
        call GroupAddUnit(data.picked, data.caster)
        call GroupAddUnit(data.picked, data.vic)
        
        loop
            exitwhen (data.i == TargetsNumber(data.level)) or (data.vic == null)
            //Here we move the projectile
            call MoveMissile(data)
        
            //Pick new Target
            set data.vic = NextTarget(data)
            if data.vic == null then
                exitwhen true
            else
                set data.i = data.i + 1
            endif
        endloop
        
        call data.destroy()
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger LightningTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( LightningTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( LightningTrg, Condition( function Conditions ) )
        call TriggerAddAction(LightningTrg, function Actions )
        
        set LightningTrg = null
        
        //setting globals
        set b = Condition(function AceptedTargets)
        set g = CreateGroup()
    endfunction
endscope
09-20-2008, 08:57 PM#5
Flame_Phoenix
Bobo_Kodo, thx for your code, I didn't see what you changed but I tested it. Thing is while your code works for one case (the case with 1 unit) and it doesn't work for the other cases (with many units) my code works for the last case, but not for the first. Our complement each other xD

Thx for trying anyway =S
09-21-2008, 01:58 AM#6
Bobo_The_Kodo
ya it does work, the range is just really small... you had it set at 100 not 500 ><
09-21-2008, 09:56 AM#7
Flame_Phoenix
Quote:
ya it does work, the range is just really small... you had it set at 100 not 500 ><
I tried your code with 500, the result was the same as with 100.

EDIT EDIT EDIT

Guys I have a problem with a loop. I was meant to be a tricky solution, but it doesn't work =S
Like I am remaking the spell, without Waits, the obviously screwed up the whole spell, so I am using a loop and a variable instead.
The I have problems with is the loop with the BJDebuggMessaged... Some how the variable is ALWAYS false, even if when I set it to true! but how !
Much of the code is useless, I just posted the two entire functions so you guys could see the globals thing:

Collapse JASS:
 private function MoveMissile takes nothing returns nothing
        local SpellData data = SpellData(GetTimerData(GetExpiredTimer()))
        
        local real x1 = GetUnitX(data.missile)
        local real x2 = GetUnitX(data.vic)
        local real y1 = GetUnitY(data.missile)
        local real y2 = GetUnitY(data.vic)
        
        local real dx = TIMER_CICLE * (x2 - x1) / data.wait  
        local real dy = TIMER_CICLE * (y2 - y1) / data.wait
        
        call SetUnitPosition(data.missile, x1 + dx, y1 + dy)
        set data.wait = data.wait - TIMER_CICLE
        
        //this is when the missile gets to the unit
        if data.wait < TIMER_CICLE then
            call SetUnitPosition(data.missile, x2, y2)
            set data.inTarg = true
            call PauseTimer(data.t)
            
//            call TargetEffect(data)
        endif
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local SpellData data = SpellData.create(GetTriggerUnit(), GetSpellTargetUnit())
        local integer i =0
        local real a
        local real b
        local real d
        
        loop
            exitwhen(i >= 2)
            set a = GetUnitX(data.missile) - GetUnitX(data.vic)
            set b = GetUnitY(data.missile) - GetUnitY(data.vic)
            set d = SquareRoot(a*a + b*b) //the distance between "a" and "b"
            set data.wait = d / SPEED

            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, TIMER_CICLE, true, function MoveMissile)
            
            loop
                exitwhen(data.inTarg)
                if data.inTarg then
                    call BJDebugMsg("true")
                else
                    call BJDebugMsg("false")
                endif
            endloop
            
            set data.vic = data.caster
        endloop
        
//        call data.destroy()
    endfunction

Can some one explain me why this doesn't work ?
09-21-2008, 01:52 PM#8
Bobo_The_Kodo
Actually mine does work, replay attached

And on your new method, won't this cause on inifinite loop -> thread crash?

Collapse JASS:
            loop
                exitwhen(data.inTarg)
                if data.inTarg then
                    call BJDebugMsg("true")
                else
                    call BJDebugMsg("false")
                endif
            endloop
Attached Files
File type: w3xlightning.w3x (24.3 KB)
File type: w3gReplay 1.22.w3g (3.3 KB)