• 🏆 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!

Why does this trigger ignore the invulnerable effect sometimes?

Status
Not open for further replies.
Level 14
Joined
Jul 19, 2007
Messages
770
+SOLVED+

I got a problem with one of my triggered spells and can't see why it crashes sometimes. It's meant to give the casting Hero and all other friendly Heroes in the map an invulnerability for a short period of time after the Hero has casted the spell sometimes it gives the invulnerability like it should and sometimes it ignores it, why does it happening?

The triggers of the spell.
  • Divine Light Cast
    • Events
      • Unit - A unit Finishes casting an ability
    • Conditions
      • (Ability being cast) Equal to Divine Light
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DivineLightIndex Equal to 0
        • Then - Actions
          • Trigger - Turn on Divine Light Loop <gen>
        • Else - Actions
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) is A Hero) Equal to True
              • ((Picked unit) belongs to an ally of (Triggering player)) Equal to True
            • Then - Actions
              • Set DivineLightIndex = (DivineLightIndex + 1)
              • Set DivineLightUnit[DivineLightIndex] = (Picked unit)
              • Set DivineLightDur[DivineLightIndex] = (0.00 + (2.00 x (Real((Level of Divine Light for (Triggering unit))))))
              • Unit - Add Invulnerable (Divine Light) to DivineLightUnit[DivineLightIndex]
            • Else - Actions
  • Divine Light Loop
    • Events
      • Time - Every 0.25 seconds of game time
    • Conditions
    • Actions
      • For each (Integer DivineLightLoop) from 1 to DivineLightIndex, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • DivineLightDur[DivineLightLoop] Less than or equal to 0.00
            • Then - Actions
              • Unit - Remove Invulnerable (Divine Light) from DivineLightUnit[DivineLightLoop]
              • Set DivineLightDur[DivineLightLoop] = DivineLightDur[DivineLightIndex]
              • Set DivineLightUnit[DivineLightLoop] = DivineLightUnit[DivineLightIndex]
              • Set DivineLightIndex = (DivineLightIndex - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DivineLightIndex Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
            • Else - Actions
              • Set DivineLightDur[DivineLightLoop] = (DivineLightDur[DivineLightLoop] - 0.25)
 
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
This is some bad indexing design.

Let's assume you caught 100 units and made them invurneable. DivineLightIndex would be set to 100.
Now, when a unit gets removed from loop the trigger, the so mentioned DivineLightIndex will be decreased. E.g removal of first unit (DivineLightUnit[] with index 1) from the loop would effectively reduce iteration size to 99. That doesn't mean that unit DivineLightUnit[100] has been already removed, thus reference to such unit is lost (or rather its just omited, since it's still being stored in global array).

Fix your indexing method or use Bribe's UnitIndexer for GUI.
 
Level 19
Joined
Jul 14, 2011
Messages
875
This is some bad indexing design.

Let's assume you caught 100 units and made them invurneable. DivineLightIndex would be set to 100.
Now, when a unit gets removed from loop the trigger, the so mentioned DivineLightIndex will be decreased. E.g removal of first unit (DivineLightUnit[] with index 1) from the loop would effectively reduce iteration size to 99. That doesn't mean that unit DivineLightUnit[100] has been already removed, thus reference to such unit is lost (or rather its just omited, since it's still being stored in global array).

Fix your indexing method or use Bribe's UnitIndexer for GUI.

Well, unit variables dont cause leaks, afaik. Or is there some other reason it should be done in another way?

Also, when you reduce DivineLightIndex, you should reduce DivineLightLoop, too.

EDIT: Cant you just use a dummy unit with a spell based of Big Bad Voodoo, and add an expiration timer to him?
 
Level 14
Joined
Jul 19, 2007
Messages
770
Well, unit variables dont cause leaks, afaik. Or is there some other reason it should be done in another way?

Also, when you reduce DivineLightIndex, you should reduce DivineLightLoop, too.

EDIT: Cant you just use a dummy unit with a spell based of Big Bad Voodoo, and add an expiration timer to him?
lol that sounds abit to complicated and i'm not sure that will be very effective.. there should be some way to fix the trigger of it instead because 50% of the times it works like it should and 50% of the times it doesn't..
 
Level 18
Joined
Jul 3, 2010
Messages
536
I have provided the simplest solution I could think of. It will completely replace these, by the way, non-MUI and terribly scripted triggers.
I have also added detailed description of what each of these three triggers do so you can understand what you're doing, instead of adopting random triggers.

  • DivLight Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Divine Light
    • Actions
      • Set TempInteger = 0
      • For each (Integer A) from 1 to 10, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempInteger Equal to 0
              • (Number of units in Divine_Light_GROUP[(Integer A)]) Equal to 0
            • Then - Actions
              • Set TempInteger = (Integer A)
            • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • TempInteger Not equal to 0
        • Then - Actions
          • Set Divine_Light_GROUP[TempInteger] = (Units in (Playable map area) matching ((((Matching unit) belongs to an ally of (Owner of (Casting unit))) Equal to True) and (((Matching unit) is A Hero) Equal to True)))
          • Unit Group - Pick every unit in Divine_Light_GROUP[TempInteger] and do (Actions)
            • Loop - Actions
              • Unit - Make (Picked unit) Invulnerable
          • Countdown Timer - Start Divine_Light_TIMER[TempInteger] as a One-shot timer that will expire in (0.00 + (2.00 x (Real((Level of Divine Light for (Casting unit)))))) seconds
        • Else - Actions
  • The first part is a very basic GUI indexing system. We set TempInteger, a global integer, to 0. Then we iterate with A from 0 to 10. If we find a Divine_Light_GROUP, a global unit group, that is empty, we set TempInteger to whatever index that has, say 3. This in turn stops the loop because we loop only while TempInteger is 0.
  • To ensure we don't run out of indexes we check if TempInteger is really not 0.
  • We add all heroes on the map to our DivineLight_GROUP[TempInteger] and run a DivineLight_TIMER, a global timer.
  • DivLight Timer
    • Events
    • Conditions
    • Actions
      • For each (Integer A) from 1 to 10, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Remaining time for Divine_Light_TIMER[(Integer A)]) Equal to 0.00
              • (Number of units in Divine_Light_GROUP[(Integer A)]) Not equal to 0
            • Then - Actions
              • Unit Group - Pick every unit in Divine_Light_GROUP[(Integer A)] and do (Actions)
                • Loop - Actions
                  • Set TempBoo = True
                  • For each (Integer B) from 1 to 10, do (Actions)
                    • Loop - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Integer A) Not equal to (Integer B)
                          • ((Picked unit) is in Divine_Light_GROUP[(Integer B)]) Equal to True
                        • Then - Actions
                          • Set TempBoo = False
                        • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • TempBoo Equal to True
                    • Then - Actions
                      • Unit - Make (Picked unit) Vulnerable
                    • Else - Actions
              • Custom script: call DestroyGroup(udg_Divine_Light_GROUP[GetForLoopIndexA()])
            • Else - Actions
  • You may notice that I declared no event. The event is that any of my 10 Divine_Light_TIMERS expire. However, I'm lazy enough to do this instead. Adding them one by one also works, but this is much faster when you need 100 timers for example.
    • DivLight I
      • Events
        • Map initialization
      • Conditions
      • Actions
        • For each (Integer A) from 1 to 10, do (Actions)
          • Loop - Actions
            • Trigger - Add to DivLight Timer <gen> the event (Time - Divine_Light_TIMER[(Integer A)] expires)
  • I check to see if any of my Divine_Light_GROUPs are not empty while their timer expired, which would signal that it is time for them to turn vulnerable again.
  • However, I run a Loop with B to see if they are in another group making them invulnerable, as this would require them to stay invulnerable.
  • I null the group I loaded them into after turning them back vulnerable.
 
Level 14
Joined
Jul 19, 2007
Messages
770
I have provided the simplest solution I could think of. It will completely replace these, by the way, non-MUI and terribly scripted triggers.
I have also added detailed description of what each of these three triggers do so you can understand what you're doing, instead of adopting random triggers.

  • DivLight Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Divine Light
    • Actions
      • Set TempInteger = 0
      • For each (Integer A) from 1 to 10, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempInteger Equal to 0
              • (Number of units in Divine_Light_GROUP[(Integer A)]) Equal to 0
            • Then - Actions
              • Set TempInteger = (Integer A)
            • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • TempInteger Not equal to 0
        • Then - Actions
          • Set Divine_Light_GROUP[TempInteger] = (Units in (Playable map area) matching ((((Matching unit) belongs to an ally of (Owner of (Casting unit))) Equal to True) and (((Matching unit) is A Hero) Equal to True)))
          • Unit Group - Pick every unit in Divine_Light_GROUP[TempInteger] and do (Actions)
            • Loop - Actions
              • Unit - Make (Picked unit) Invulnerable
          • Countdown Timer - Start Divine_Light_TIMER[TempInteger] as a One-shot timer that will expire in (0.00 + (2.00 x (Real((Level of Divine Light for (Casting unit)))))) seconds
        • Else - Actions
  • The first part is a very basic GUI indexing system. We set TempInteger, a global integer, to 0. Then we iterate with A from 0 to 10. If we find a Divine_Light_GROUP, a global unit group, that is empty, we set TempInteger to whatever index that has, say 3. This in turn stops the loop because we loop only while TempInteger is 0.
  • To ensure we don't run out of indexes we check if TempInteger is really not 0.
  • We add all heroes on the map to our DivineLight_GROUP[TempInteger] and run a DivineLight_TIMER, a global timer.
  • DivLight Timer
    • Events
    • Conditions
    • Actions
      • For each (Integer A) from 1 to 10, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Remaining time for Divine_Light_TIMER[(Integer A)]) Equal to 0.00
              • (Number of units in Divine_Light_GROUP[(Integer A)]) Not equal to 0
            • Then - Actions
              • Unit Group - Pick every unit in Divine_Light_GROUP[(Integer A)] and do (Actions)
                • Loop - Actions
                  • Set TempBoo = True
                  • For each (Integer B) from 1 to 10, do (Actions)
                    • Loop - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Integer A) Not equal to (Integer B)
                          • ((Picked unit) is in Divine_Light_GROUP[(Integer B)]) Equal to True
                        • Then - Actions
                          • Set TempBoo = False
                        • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • TempBoo Equal to True
                    • Then - Actions
                      • Unit - Make (Picked unit) Vulnerable
                    • Else - Actions
              • Custom script: call DestroyGroup(udg_Divine_Light_GROUP[GetForLoopIndexA()])
            • Else - Actions
  • You may notice that I declared no event. The event is that any of my 10 Divine_Light_TIMERS expire. However, I'm lazy enough to do this instead. Adding them one by one also works, but this is much faster when you need 100 timers for example.
    • DivLight I
      • Events
        • Map initialization
      • Conditions
      • Actions
        • For each (Integer A) from 1 to 10, do (Actions)
          • Loop - Actions
            • Trigger - Add to DivLight Timer <gen> the event (Time - Divine_Light_TIMER[(Integer A)] expires)
  • I check to see if any of my Divine_Light_GROUPs are not empty while their timer expired, which would signal that it is time for them to turn vulnerable again.
  • However, I run a Loop with B to see if they are in another group making them invulnerable, as this would require them to stay invulnerable.
  • I null the group I loaded them into after turning them back vulnerable.
Oh that's a much more complicated triggers..but if it makes the ability work like it should then it's good. Btw I have an Invulnerable-ability for the spell, can I just replace the "Make (Picked Unit) Invulnerable" to "Add Invulnerable (Divine Light) to (Picked Unit)"?? Could also maybe post a map with the triggers so I can simply copy and paste them into my map?
 
Level 18
Joined
Jul 3, 2010
Messages
536
Yes, you can just replace it with an ability add line. I already did this in the map I'm posting for you.

I didn't feel like creating any abilities in the object editor, so I'm currently using Animate Dead as the base spell and Evasion as the spell that it gives.

You can very quickly change it to whatever you want. I added two more lines to Divine Light INIT.
  • Divine Light INIT
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set zz_DivLight_SPELL = Animate Dead
      • Set zz_DivLight_SPELL_DUMMY = Evasion (Neutral Hostile)
      • For each (Integer A) from 1 to 10, do (Actions)
        • Loop - Actions
          • Trigger - Add to Divine Light DONE <gen> the event (Time - Divine_Light_TIMER[(Integer A)] expires)
Simply replace these values:
  • zz_DivLight_SPELL should point to the ability the hero casts.
  • zz_DivLight_SPELL_DUMMY should point to the ability that you made to make the heroes invulnerable.

EDIT
If you want to perhaps change how long the spell lasts, you can change the very last line of Divine Light cast.
  • Countdown Timer - Start Divine_Light_TIMER[TempInteger] as a One-shot timer that will expire in (0.00 + (2.00 x (Real((Level of zz_DivLight_SPELL for (Casting unit)))))) seconds
It currently lasts 2/4/6 seconds. If you wanted it to last 5/8/11 seconds instead, it would look like this:
  • Countdown Timer - Start Divine_Light_TIMER[TempInteger] as a One-shot timer that will expire in (2.00 + (3.00 x (Real((Level of zz_DivLight_SPELL for (Casting unit)))))) seconds
 

Attachments

  • divinelight.w3x
    19.1 KB · Views: 59
Well, unit variables dont cause leaks, afaik. Or is there some other reason it should be done in another way?

Also, when you reduce DivineLightIndex, you should reduce DivineLightLoop, too.

EDIT: Cant you just use a dummy unit with a spell based of Big Bad Voodoo, and add an expiration timer to him?

Actually unit variables do indeed cause leaks, GUI'ers need to learn to set their unit variables to no unit.
 
Level 14
Joined
Jul 19, 2007
Messages
770
Yes, you can just replace it with an ability add line. I already did this in the map I'm posting for you.

I didn't feel like creating any abilities in the object editor, so I'm currently using Animate Dead as the base spell and Evasion as the spell that it gives.

You can very quickly change it to whatever you want. I added two more lines to Divine Light INIT.
  • Divine Light INIT
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set zz_DivLight_SPELL = Animate Dead
      • Set zz_DivLight_SPELL_DUMMY = Evasion (Neutral Hostile)
      • For each (Integer A) from 1 to 10, do (Actions)
        • Loop - Actions
          • Trigger - Add to Divine Light DONE <gen> the event (Time - Divine_Light_TIMER[(Integer A)] expires)
Simply replace these values:
  • zz_DivLight_SPELL should point to the ability the hero casts.
  • zz_DivLight_SPELL_DUMMY should point to the ability that you made to make the heroes invulnerable.

EDIT
If you want to perhaps change how long the spell lasts, you can change the very last line of Divine Light cast.
  • Countdown Timer - Start Divine_Light_TIMER[TempInteger] as a One-shot timer that will expire in (0.00 + (2.00 x (Real((Level of zz_DivLight_SPELL for (Casting unit)))))) seconds
It currently lasts 2/4/6 seconds. If you wanted it to last 5/8/11 seconds instead, it would look like this:
  • Countdown Timer - Start Divine_Light_TIMER[TempInteger] as a One-shot timer that will expire in (2.00 + (3.00 x (Real((Level of zz_DivLight_SPELL for (Casting unit)))))) seconds
Oh thank so much for the help! The spell works like it should now and I hope it will not cause any more bugs.
 
Status
Not open for further replies.
Top