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

Loop stops running prematurely

Status
Not open for further replies.
Level 7
Joined
May 11, 2010
Messages
278
This is part of my trigger
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • ASysProjectileLifetime[ASysLoopInt] Greater than 0.00
    • Then - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in ASysKnockedTargets[ASysLoopInt]) Greater than or equal to 1
        • Then - Actions
          • Set ASysProjectileLifetime[ASysLoopInt] = (ASysProjectileLifetime[ASysLoopInt] - 0.02)
          • Game - Display to (All players) the text: (String(ASysProjectileLifetime[ASysLoopInt]))
          • Unit Group - Pick every unit in ASysKnockedTargets[ASysLoopInt] and do (Actions)
            • Loop - Actions
              • Set TempPoint = (Position of (Picked unit))
              • Set TempPoint2 = (TempPoint offset by ASysProjectileSpeed[ASysLoopInt] towards ASysKnockAngle[ASysLoopInt] degrees)
              • Unit - Move (Picked unit) instantly to TempPoint2
              • Custom script: call RemoveLocation(udg_TempPoint)
              • Custom script: call RemoveLocation(udg_TempPoint2)
        • Else - Actions
          • Game - Display to (All players) the text: test 1
          • Set ASysAttackComplete[ASysLoopInt] = True
    • Else - Actions
      • Game - Display to (All players) the text: test 2
      • Unit Group - Pick every unit in ASysKnockedTargets[ASysLoopInt] and do (Actions)
        • Loop - Actions
          • Unit - Unpause (Picked unit)
          • Unit Group - Remove (Picked unit) from ASysKnockedTargets[ASysLoopInt]
      • Set ASysAttackComplete[ASysLoopInt] = True
It runs on a time loop of 0.02 seconds. It's a knockback ability. After about 0.5 seconds, it stops running even though there's still time left on ProjectileLifetime. It doesn't show any of the "test" messages.

I've heard that too many "If/Then/Else"s in the same trigger can cause errors. Might this be because of that or is something else wrong?
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,522
> After about 0.5 seconds, it stops running even though there's still time left on ProjectileLifetime. It doesn't show any of the "test" messages.

That's a logic issue, not an issue with WC3. Your spell just uses some knockback library so it's hard to see the issue without all of the trigger(s).

> I've heard that too many "If/Then/Else"s in the same trigger can cause errors.

There is no specific limit on the number of conditional branches are in a trigger, but there is a very hard and very tangible limit on the number of operations that can be performed within one virtual thread. I see no indication of this happening here.

> Solved it by "outsourcing" that part to another trigger.

Specifically?
 
Level 7
Joined
May 11, 2010
Messages
278
  • ASysKnockbackLoop
    • Events
      • Time - Every 0.02 seconds of game time
    • Conditions
    • Actions
      • For each (Integer ASysKnockLoopInt) from 1 to ASysKnockInt, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ASysKnockDuration[ASysKnockLoopInt] Greater than 0.00
            • Then - Actions
              • Set ASysKnockDuration[ASysKnockLoopInt] = (ASysKnockDuration[ASysKnockLoopInt] - 0.02)
              • Unit Group - Pick every unit in ASysKnockedTargets[ASysKnockLoopInt] and do (Actions)
                • Loop - Actions
                  • Set TempPoint = (Position of (Picked unit))
                  • Set TempPoint2 = (TempPoint offset by ASysKnockSpeed[ASysKnockLoopInt] towards ASysKnockAngle[ASysKnockLoopInt] degrees)
                  • Unit - Move (Picked unit) instantly to TempPoint2
                  • Custom script: call RemoveLocation(udg_TempPoint)
                  • Custom script: call RemoveLocation(udg_TempPoint2)
            • Else - Actions
              • Unit Group - Pick every unit in ASysKnockedTargets[ASysKnockLoopInt] and do (Actions)
                • Loop - Actions
                  • Unit - Unpause (Picked unit)
                  • Unit Group - Remove (Picked unit) from ASysKnockedTargets[ASysKnockLoopInt]
              • -------- recycle --------
              • Game - Display to (All players) the text: (ASysKnockInt = + (String(ASysKnockInt)))
              • Game - Display to (All players) the text: (ASysKnockLoopInt = + (String(ASysKnockLoopInt)))
              • Set ASysKnockAngle[ASysKnockLoopInt] = ASysKnockAngle[ASysKnockInt]
              • Set ASysKnockDuration[ASysKnockLoopInt] = ASysKnockDuration[ASysKnockInt]
              • Set ASysKnockSpeed[ASysKnockLoopInt] = ASysKnockSpeed[ASysKnockInt]
              • Set ASysKnockedTargets[ASysKnockLoopInt] = ASysKnockedTargets[ASysKnockInt]
              • -------- recycle end --------
              • Set ASysKnockInt = (ASysKnockInt - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ASysKnockInt Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
However, I have another, related problem. As you can see, I'm using an Unit Group array. Trying to add units to a unit group that isn't initialized causes stuff to break as I believe you're well aware. I know there's a custom script for creating unit groups, I don't know what it is though. I want something that checks if a group is created, and if it's not, it creates the group.
Essentially:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (UnitGroup[TempInt] exists) Equal to True
    • Then - Actions
    • Else - Actions
      • Unit Group - Create UnitGroup[TempInt]
If someone could perhaps make that for me it would be great ^^
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,190
I don't know what it is though.
The native is declared as...
JASS:
native CreateGroup takes nothing returns group
Here are some examples of using it.
JASS:
local group g = CreateGroup()
set udg_globalgroup = CreateGroup()
set udg_group_array[1] = CreateGroup()
set udg_garray[udg_loopint] = CreateGroup()

I want something that checks if a group is created, and if it's not, it creates the group.
Something like this would work.
JASS:
if udg_UnitGroup[udg_TempInt] == null then
    set udg_UnitGroup[udg_TempInt] = CreateGroup()
endif

// Explination of above (just another copy of it)...
// We open a conditional block testing if the group array at index TempInt is null
if udg_UnitGroup[udg_TempInt] == null then
    // If so then we set it to a new group.
    // You might want to destroy this group and null it if using an indexing/instancing system since it could be counted as a leak as the result of spike loads on the system.
    set udg_UnitGroup[udg_TempInt] = CreateGroup()
// We need to close the conditional statement. This tells the compiler to end the conditional block.
endif
Each line has to be a separate line of custom script in GUI. I added a commented version to try and explain more clearly what is going on.
 
Level 7
Joined
May 11, 2010
Messages
278
Thanks Dr Super Good! It seems to work. My trigger, however, does not :p
It works properly as long as only one unit is being knocked. As soon as multiple units are being knocked at the same time, the trigger goes crazy.


  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • ASysIsKnockback[ASysLoopInt] Equal to True
    • Then - Actions
      • Set ASysKnockInt = (ASysKnockInt + 1)
      • Set ASysKnockAngle[ASysKnockInt] = (Facing of ASysCaster[ASysLoopInt])
      • Set ASysKnockDuration[ASysKnockInt] = ASysProjectileLifetime[ASysLoopInt]
      • Set ASysKnockSpeed[ASysKnockInt] = ASysProjectileSpeed[ASysLoopInt]
      • Custom script: if udg_ASysKnockedTargets[udg_ASysKnockInt] == null then
      • Custom script: set udg_ASysKnockedTargets[udg_ASysKnockInt] = CreateGroup()
      • Custom script: endif
      • Unit Group - Pick every unit in ASysHitTargets and do (Actions)
        • Loop - Actions
          • Unit Group - Add (Picked unit) to ASysKnockedTargets[ASysKnockInt]
          • Unit - Make (Picked unit) face ASysCaster[ASysLoopInt] over 0.00 seconds
          • Unit - Pause (Picked unit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (ASysKnockbackLoop <gen> is on) Equal to False
        • Then - Actions
          • Trigger - Turn on ASysKnockbackLoop <gen>
        • Else - Actions
    • Else - Actions
  • ASysKnockbackLoop
    • Events
      • Time - Every 0.02 seconds of game time
    • Conditions
    • Actions
      • For each (Integer ASysKnockLoopInt) from 1 to ASysKnockInt, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ASysKnockDuration[ASysKnockLoopInt] Greater than 0.00
            • Then - Actions
              • Set ASysKnockDuration[ASysKnockLoopInt] = (ASysKnockDuration[ASysKnockLoopInt] - 0.02)
              • Unit Group - Pick every unit in ASysKnockedTargets[ASysKnockLoopInt] and do (Actions)
                • Loop - Actions
                  • Set TempPoint = (Position of (Picked unit))
                  • Set TempPoint2 = (TempPoint offset by ASysKnockSpeed[ASysKnockLoopInt] towards ASysKnockAngle[ASysKnockLoopInt] degrees)
                  • Unit - Move (Picked unit) instantly to TempPoint2
                  • Custom script: call RemoveLocation(udg_TempPoint)
                  • Custom script: call RemoveLocation(udg_TempPoint2)
            • Else - Actions
              • Unit Group - Pick every unit in ASysKnockedTargets[ASysKnockLoopInt] and do (Actions)
                • Loop - Actions
                  • Unit - Unpause (Picked unit)
                  • Unit Group - Remove (Picked unit) from ASysKnockedTargets[ASysKnockLoopInt]
              • -------- recycle --------
              • Set ASysKnockAngle[ASysKnockLoopInt] = ASysKnockAngle[ASysKnockInt]
              • Set ASysKnockDuration[ASysKnockLoopInt] = ASysKnockDuration[ASysKnockInt]
              • Set ASysKnockSpeed[ASysKnockLoopInt] = ASysKnockSpeed[ASysKnockInt]
              • Set ASysKnockedTargets[ASysKnockLoopInt] = ASysKnockedTargets[ASysKnockInt]
              • -------- recycle end --------
              • Set ASysKnockInt = (ASysKnockInt - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ASysKnockInt Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
I have no clue as to why it doens't work. But basically, it seems as if whenever mulitple units are knocked at the same time, they move once per loop no matter what unit group they're in.
 
Level 7
Joined
May 11, 2010
Messages
278
I didn't show the full setup trigger because it's part of another system and that trigger is looooooooong. I showed the relevant part of it.

Why should I not use the pause unit function? Never had any issues with it before.

Yeah I spent tons of time putting test messages at different places but it didn't help me figure it out.

Good news though! I realized that the way I was doing it was stupid, changed it and now it works seemingly flawlessly.

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • ASysIsKnockback[ASysLoopInt] Equal to True
    • Then - Actions
      • Unit Group - Pick every unit in ASysHitTargets and do (Actions)
        • Loop - Actions
          • Set ASysKnockInt = (ASysKnockInt + 1)
          • Set ASysKnockAngle[ASysKnockInt] = (Facing of ASysCaster[ASysLoopInt])
          • Set ASysKnockDuration[ASysKnockInt] = ASysProjectileLifetime[ASysLoopInt]
          • Set ASysKnockSpeed[ASysKnockInt] = ASysProjectileSpeed[ASysLoopInt]
          • Set ASysKnockUnit[ASysKnockInt] = (Picked unit)
          • Unit - Make (Picked unit) face ASysCaster[ASysLoopInt] over 0.00 seconds
          • Unit - Pause (Picked unit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (ASysKnockbackLoop <gen> is on) Equal to False
        • Then - Actions
          • Trigger - Turn on ASysKnockbackLoop <gen>
        • Else - Actions
    • Else - Actions
  • ASysKnockbackLoop
    • Events
      • Time - Every 0.02 seconds of game time
    • Conditions
    • Actions
      • For each (Integer ASysKnockLoopInt) from 1 to ASysKnockInt, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ASysKnockDuration[ASysKnockLoopInt] Greater than 0.00
            • Then - Actions
              • Set ASysKnockDuration[ASysKnockLoopInt] = (ASysKnockDuration[ASysKnockLoopInt] - 0.02)
              • Set TempPoint = (Position of ASysKnockUnit[ASysKnockLoopInt])
              • Set TempPoint2 = (TempPoint offset by ASysKnockSpeed[ASysKnockLoopInt] towards ASysKnockAngle[ASysKnockLoopInt] degrees)
              • Unit - Move ASysKnockUnit[ASysKnockLoopInt] instantly to TempPoint2
              • Custom script: call RemoveLocation(udg_TempPoint)
              • Custom script: call RemoveLocation(udg_TempPoint2)
            • Else - Actions
              • Unit - Unpause ASysKnockUnit[ASysKnockLoopInt]
              • -------- recycle --------
              • Set ASysKnockAngle[ASysKnockLoopInt] = ASysKnockAngle[ASysKnockInt]
              • Set ASysKnockDuration[ASysKnockLoopInt] = ASysKnockDuration[ASysKnockInt]
              • Set ASysKnockSpeed[ASysKnockLoopInt] = ASysKnockSpeed[ASysKnockInt]
              • Set ASysKnockUnit[ASysKnockLoopInt] = ASysKnockUnit[ASysKnockInt]
              • -------- recycle end --------
              • Set ASysKnockInt = (ASysKnockInt - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ASysKnockInt Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
As I was lying in my bed I was suddenly struck with the realization that there is literally no reason to use unit groups for this. So I got up at ~1 am and fixed it. Because that's how I work :p
 
Level 7
Joined
May 11, 2010
Messages
278
wellllll that is problematic. I want to make sure the unit can't do anything while being knocked though.
I know that a common way to do this is to create a dummy unit and have them cast stormbolt on the unit. However, how do I make the duration of the spell match up to the duration of the knock? Creating one spell for every duration isn't reasonable. Can I set duration or remove the stun with triggers?

Is there another way to make sure the unit can't recieve commands during a period of time? I'd really rather not have to use a dummy unit with stormbolt.
Also, is there a way to make sure a unit can't recieve commands that doesn't disrupt its current orders? Essentially for a period of time it will not listen to any input made by the player but it will continue doing what it was doing before?
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I dont remember if it truly works, but you can try making storm bolt with 9999 seconds duration and after specified time remove the debuff from unit, which could potentially make unit unstunned. Dont know if it works tho, because stuns are a bit weird
 
Status
Not open for further replies.
Top