• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • It's time for the first HD Modeling Contest of 2025. Join the theme discussion for Hive's HD Modeling Contest #7! Click here to post your idea!

[Solved] Can't find the infinite loop in my spell

Status
Not open for further replies.
Level 24
Joined
Jun 26, 2020
Messages
1,921
I made this spell to emulate the stampede but instead of lizzards, there are fire ball, I wanna test it but there is an infinite loop that I can't find, could you do it for me pls?
I'm using a custom Alloc and Table and a unit indexer
vJASS:
scope FireStorm initializer Init
    
    struct FireStorm extends array
    
        implement Alloc
        static group g1=CreateGroup()
        static group g2=CreateGroup()
        static constant real INTERVAL=0.05
        static constant real AREA_EFF=1000
        static constant real AREA_COL=55
        static constant real AREA_DMG=100
        static constant real SPEED=500*thistype.INTERVAL
        static constant integer MAX_DIS=R2I(thistype.AREA_EFF/thistype.SPEED)
        static constant string MODEL="war3mapImported\\GreatElderHydraMoltenBreathV.196.mdx"
        static effect array attached
        static integer array reached
        unit caster
        player owner
        integer counter
        real damage
        real x_offset
        real y_offset
        real face
        real face_comp
        Table next_fb
        Table prev_fb
        timer t
        
        static method create takes unit caster, integer spell, integer level, real x, real y returns thistype
            local thistype this=thistype.allocate()
            //Set the data
            set this.caster=caster
            set this.owner=GetOwningPlayer(caster)
            if spell=='A049' then
                set this.damage=60
            endif
            set this.face=GetUnitFacing(caster)*bj_DEGTORAD
            set this.face_comp=this.face-bj_PI/2
            set this.x_offset=GetUnitX(caster)-thistype.AREA_EFF*Cos(this.face)
            set this.y_offset=GetUnitY(caster)-thistype.AREA_EFF*Sin(this.face)
            set this.counter=0
            set t=NewTimerEx(this)
            call TimerStart(this.t,thistype.INTERVAL,true,function thistype.callperiodic)
            return this
        endmethod
        
        static method callperiodic takes nothing returns nothing
            local thistype this=GetTimerData(GetExpiredTimer())
            local integer i
            local real r
            if OrderId2String(GetUnitCurrentOrder(this.caster))=="stampede" then
                //Create the fire ball
                set r=GetRandomReal(-thistype.AREA_EFF,thistype.AREA_EFF)
                set bj_lastCreatedUnit=GetRecycledUnit(this.owner/*
                */,'h013'/*
                */,this.x_offset+r*Cos(this.face_comp)/*
                */,this.y_offset+r*Sin(this.face_comp)/*
                */,this.face*bj_RADTODEG)
                set i=GetUnitUserData(bj_lastCreatedUnit)
                
                //Add to the list
                set this.next_fb.integer[i]=0
                set this.prev_fb.integer[i]=this.prev_fb.integer[0]
                set this.next_fb.integer[this.prev_fb.integer[i]]=i
                set this.prev_fb.integer[0]=i
                
                set thistype.attached[i]=AddSpecialEffectTarget(thistype.MODEL,bj_lastCreatedUnit,"chest")
                set thistype.reached[i]=0
                
                set this.counter=this.counter+1
            else
                if this.counter==0 then
                    call this.destroy()
                endif
            endif
            call this.move()
        endmethod
        
        method move takes nothing returns nothing
            local integer i=this.next_fb.integer[0]
            local real x
            local real y
            local unit u1
            local unit u2
            loop
                exitwhen i==0
                //Moving the fire ball
                set x=GetUnitX(udg_UDexUnits[i])
                set y=GetUnitY(udg_UDexUnits[i])
                call SetUnitPosition(udg_UDexUnits[i],x+thistype.SPEED*Cos(this.face),y+thistype.SPEED*Sin(this.face))
                //Check the units around the fire ball
                call GroupEnumUnitsInRange(thistype.g1,x,y,thistype.AREA_COL,null)
                loop
                    set u1=FirstOfGroup(thistype.g1)
                    exitwhen u1==null
                    if IsUnitEnemy(u1,this.owner) and IsUnitAliveBJ(u1) then
                        //If true then damage the nearby enemy units
                        call GroupEnumUnitsInRange(thistype.g2,x,y,thistype.AREA_DMG,null)
                        loop
                            set u2=FirstOfGroup(thistype.g2)
                            exitwhen u2==null
                            if IsUnitEnemy(u2,this.owner) and IsUnitAliveBJ(u2) then
                                call UnitDamageTarget(this.caster,u2,this.damage,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,WEAPON_TYPE_WHOKNOWS)
                            endif
                            call GroupRemoveUnit(thistype.g2,u2)
                        endloop
                        call GroupClear(thistype.g1)
                        call this.removeFromList(i)
                    endif
                    call GroupRemoveUnit(thistype.g1,u1)
                endloop
                
                //Remove if reaches the objective
                set thistype.reached[i]=thistype.reached[i]+1
                
                if thistype.reached[i]>=thistype.MAX_DIS then
                    call this.removeFromList(i)
                endif
                
                set i=this.next_fb.integer[i]
            endloop
        endmethod
        
        method removeFromList takes integer i returns nothing
            //Removing from list
            set this.next_fb.integer[this.prev_fb.integer[i]]=this.next_fb.integer[i]
            set this.prev_fb.integer[this.next_fb.integer[i]]=this.prev_fb.integer[i]
            //Clear the data of the fire ball
            set this.counter=this.counter-1
            call DestroyEffect(thistype.attached[i])
            set thistype.attached[i]=null
            call RecycleUnitDelayed(udg_UDexUnits[i],0.27)
        endmethod
        
        method destroy takes nothing returns nothing
            //Clear the data of the instance
            call ReleaseTimer(this.t)
            call this.next_fb.flush()
            call this.prev_fb.flush()
            set this.caster=null
            set this.owner=null
            set this.t=null
            call this.deallocate()
        endmethod
    endstruct
    
    private function Conditions takes nothing returns boolean
        return Spell=='A049'
    endfunction
    
    private function Actions takes nothing returns nothing
        call FireStorm.create(Caster,Spell,Level,GetSpellTargetX(),GetSpellTargetY())
    endfunction
    
    private function Init takes nothing returns nothing
        set gg_trg_Fire_storm=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(gg_trg_Fire_storm,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(gg_trg_Fire_storm,Condition(function Conditions))
        call TriggerAddAction(gg_trg_Fire_storm,function Actions)
    endfunction
endscope
 
Level 24
Joined
Jun 26, 2020
Messages
1,921
Might want to provide a demonstration map for people to poke around at it with. This would also rule out other influences causing an infinite loop.

Destruction condition looks strange. It will only cancel if the ability is interrupted before the first periodic call (counter is 0).
I though with saying "there is an infinity loop" it will be clear the problem is the game freezes when I cast the spell, but well the worst part is I found the problem, I didn't created the tables, why this dumb things happen to me?

And what exactly you mean with the destroy condition?
But now I changed it a bit.
 
Level 21
Joined
May 16, 2012
Messages
640
Relativistic missiles is for newer versions and I'm using 1.26, but I will take a look at SpellEffectEvent, thanks.
There is a version for patches 1.30-. Go to the Preview Triggers in the thread and you will see a folder called Patch 1.30- inside the Missile folder. You can then copy the codes to an empty script inside your map, so you can use it no problem.

Edit: You will also need a dummy missile unit, like in the picture below and the dummy model by Vexorian. I'm uploading it in case you can't find it. I'm also uploading the ObjectData from Missile map, so you can import it to an empty map and copy the dummy unit configuration.

Screenshot_1.png


Screenshot_2.png


Screenshot_3.png
 

Attachments

  • data.rar
    1.6 KB · Views: 14
  • dummy.mdx
    34.2 KB · Views: 11
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,921
There is a version for patches 1.30-. Go to the Preview Triggers in the thread and you will see a folder called Patch 1.30- inside the Missile folder. You can then copy the codes to an empty script inside your map, so you can use it no problem.

Edit: You will also need a dummy missile unit, like in the picture below and the dummy model by Vexorian. I'm uploading it in case you can't find it. I'm also uploading the ObjectData from Missile map, so you can import it to an empty map and copy the dummy unit configuration.

Thanks, although it wasn't necessary been too detailed, with just saying there is a 1.30- version was enough, but I know is "just in case you don't know", because also I already have the dummy even I used it in my code I showed, and I already have a unit recycler, so I don't wanna add another, there just left adapt the library to the unit recycler I'm using.

Edit: Question, how can I use this to what I want? because it was not clear to me.

Edit 2: Going back to what i said before I can't use it, I see the code and there are natives from recent versions, no offense but next time make sure I can use the code, but still thanks for trying.
 
Last edited:
Level 21
Joined
May 16, 2012
Messages
640
Edit 2: Going back to what i said before I can't use it, I see the code and there are natives from recent versions, no offense but next time make sure I can use the code, but still thanks for trying.
Which natives? Couldn't find them. You didn't imported the MissileEffect library did you? That library is only for 1.31+ so if you imported it that might be the problem. Send me an image of the error.

EDIT: Oh I see it now. It's a mistake on my part when i was re-arranging the code from the last version. Will fix it now. For now you can just replace that function call will a constant value like 64, which is the value i used back then.

Screenshot_1.png
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,921
Which natives? Couldn't find them. You didn't imported the MissileEffect library did you? That library is only for 1.31+ so if you imported it that might be the problem. Send me an image of the error.

EDIT: Oh I see it now. It's a mistake on my part when i was re-arranging the code from the last version. Will fix it now. For now you can just replace that function call will a constant value like 64, which is the value i used back then.

View attachment 379983
Well, it worked, so back to this question
Edit: Question, how can I use this to what I want? because it was not clear to me.
 
Level 21
Joined
May 16, 2012
Messages
640
Here a basic implementation of your spell using Missiles. I commented as much as possible to make it easier to understand, but if you have any doubts you can PM me. I'll release the fix for the Missiles system today probably, so keep an eye out to get the latest version.

vJASS:
scope Q
    private struct Missile extends Missiles // To make a struct be able to behave like a missile simply make it extend the Missiles library
        /*
            There are 11 events in total, but you can declare only those that you want.
            All events but the onRemove event return a boolean. If true the missile will be destroyed.
            Take a look at the Missiles - Q example in the Preview Trigger at the system thread to see
            every event being declared and used.
            
            For this example in particular we only need the onHit event, so i'll only declare him
        */
        method onHit takes unit hit returns boolean
            // Here we decide what to do when the missile hits a target. In this case we want to damage the hitted target
            // which is the hit parameter of this method if it is an enemy, and is alive, etc..
            if IsUnitEnemy(hit, owner) and UnitAlive(hit) then
                call UnitDamageTarget(source, hit, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) // damagin the hitted unit
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", hit, "origin")) // Create some eye candy on it
                
                return true // In original Stampede, when the lizzard hit's the target it disappear, so when hitting a valid target return true anf terminate the missile
            endif
            
            return false
        endmethod
    endstruct

    private struct Stampede
        timer  timer
        unit   unit
        player player
        real   duration
        real   angle
        real   tx
        real   ty
        real   x
        real   y
        
        private static method angleBetweenCoordinates takes real x, real y, real x2, real y2 returns real
            return Atan2(y2 - y, x2 - x)
        endmethod
        
        private static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real     maxRange
            local real     theta
            local real     range
            local real     toX
            local real     toY
            local real     fromX
            local real     fromY
            local Missile  missile

            if duration > 0 then
                set duration = duration - 0.2
                set theta = angle + bj_PI/2*GetRandomReal(-1, 1) // Angle for initial position
                set fromX = x + 800*Cos(theta) // Starting x position of the missile
                set fromY = y + 800*Sin(theta) // Starting y position of the missile
                set theta = angle + bj_PI // Angle for ending position
                set toX = fromX + 1600*Cos(theta) // Finishing x position of the missile
                set toY = fromY + 1600*Sin(theta) // Finishing y position of the missile
                set missile = Missile.create(fromX, fromY, 50, toX, toY, 50) // create always take the initial x, y, z and the final x, y, z
                
                // Setting the members needed for this spell in particular. There are others, check the Examples or the API
                set missile.source = unit // The missile source unit (Will do the damage)
                set missile.owner = player // The owning player of the missile
                set missile.model = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl" // The missile model
                set missile.scale = 1.5 // The missile scale
                set missile.speed = 1000 // The missile speed
                set missile.collision = 55 // The missile collision range. Must be set for the onHit event to trigger.
                set missile.damage = 60 // The missile damage
                
                call missile.launch() // Launches the missile
            else
                call ReleaseTimer(timer)
                set timer = null
                set unit = null
                call deallocate()
            endif
        endmethod
        
        private static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()
            
            // Saving information since this spell is periodic not instant
            // Note: If you want to use Spell. members you can import my SpellEffectPlugin to your map
            set timer = NewTimerEx(this)
            set unit = Spell.source.unit // The casting unit
            set player = Spell.source.player // The owner of the casting unit
            set x = Spell.source.x // The x coordinate of the casting unit
            set y = Spell.source.y // The y coordinate of the casting unit
            set tx = Spell.x // The x coordinate of point where the ability was cast
            set ty = Spell.y // The y coordinate of point where the ability was cast
            set angle = angleBetweenCoordinates(tx, ty, x, y)
            set duration = 30 // The spell duration
            
            call TimerStart(timer, 0.2, true, function thistype.onPeriod)
        endmethod
        
        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent('A000', function thistype.onCast) // SpellEffectEvent makes triggering onCast events a brease
        endmethod
    endstruct
endscope


full
 
Status
Not open for further replies.
Top