• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Spell doesnt work twice on units

Level 3
Joined
Oct 9, 2023
Messages
20
So i'm creating custom spells for heroes in my map.
I wanted to create a madness spell which would make mad units attack random units in their range for 5,10 or 17 seconds depending on the ability level.
This is achieved by changing their owner then ordering them to attack a different random unit in range each 1 seconds then changing owner to the owner they were at first.
It seems to work, i tried it on creeps, but theres an issue : when i use it on a unit, i can't re use it on this unit. It'll change their owner, but then the unit won't attack the other units, and will even keep the changed owner. idk why but the second time i use the spell on a unit, all of the actions that goes after the "change owner to player 7 (green)" don't work.
  • Events : a unit starts the effect of an ability
  • Conditions : Ability being cast = Madness
  • Actions :
  • If : Level of madness for (Casting unit) = 1
  • Then : Pick every unit in (Region centered at (Target point of Ability being cast) with size 200.00,200.00 and do (actions)
  • If : Owner of picked unit ≠ Owner of casting unit
  • Then :
  • If owner of picked unit = Player 9 (grey)
  • Unit - Change ownership of picked unit to Player 7 green and keep colour
  • For each integer from 0 to 5:
  • [order picked unit to attack (random unit in range: 200)
  • Wait 1 second]
  • Change ownership of picked unit to Player 9 (grey) and keep colour
I did this for every player (except player 7 green, neutral victim and neutral ),and for each ability level.
the range of the ability changes too.
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
  • Post your actual triggers: How To Post Your Trigger

  • Waits in loops are an extremely bad idea and will inevitably cause massive problems for you in the future. Not only can another trigger break this trigger's loop but this trigger can break any other trigger loop that uses a wait and the same integer variable. Do not do this. It is likely the source of the 'things not working' when this spell is used twice simultaneously.

  • Looks like you need to understand memory leaks (but I can't be sure because you didn't show the actual trigger): Things That Leak

  • Why are you checking the level and then not doing anything different for other levels?

  • Units that have an attack speed slower than 1.0 (or close to 1.0 but with bad rng or slow turn rates, or melee units that are slow enough not to reach their targets) will never actually attack anything during this madness period because they keep getting redirected. I think a better and simpler solution (though perhaps not as cool) is to change the units to be owned by Neutral Hostile and order them to attack-move to their current position. This will cause them to attack whatever is nearby and be hostile to everything (except other neutral hostile units, which I guess could be undesirable).

  • You gave no information about players/teams in your map so it's hard to know if simply changing ownership to player 9 then back to 7 will work for all cases, but in general I do not expect that to be a viable solution at all. Multiple different units owned by different players might be affected, and once changed how do you know who they should go back to? This will require either some creative use of unit groups (one for each player to hold units that were 'originally' theirs), some cleverly applied and leveled invisible useless passive abilities (whose level corresponds to the players that originally owned each unit), or attaching information about the original owners to the units in a hashtable or some other method like a unit indexer.

  • Unless you are careful, multiple casts of this ability that overlap and hit the same units will overwrite this ownership information, so you will have to be prepared for that if you want it to be possible. If you want to be lazy you could limit this spell to being cast by only one unit on the map and giving it a cooldown longer than its duration, which would eliminate this issue. That's not resolving the problem, but it does avoid it. Any of the methods I referenced in my above bullet point can properly account for this, but it will require thinking about it properly to do so.
 
Level 3
Joined
Oct 9, 2023
Messages
20
Hey, thanks for telling me about memory leaks, i didn't show the entire trigger , the range of the spell and the duration changes depending on the ability level. also, i can't turn them to neutral hostile because they wouldn't attack other neutral hostile units and also because i want the spell to be effectife on neutral hostile, also because neutral hostile units can't be given orders using triggers (for some reason)

The only viable solution i could think of is creating variables attatched to specific units, but idk if it's possible with another way than "unit custom variable" which only stores an integer and i already use this for another unit so it would cause a lot of bugs.
If you know how to create a variable stored into a unit tell me bc it's such a pain to create triggers without this.
heres the actual trigger :
  • Madness
    • Evénements
      • Unité - A unit Initie l'effet d'une compétence
    • Conditions
      • (Ability being cast) Egal à Folie
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • Si - Conditions
          • (Level of Folie for (Casting unit)) Egal à 1
        • Alors - Actions
          • Groupe unité - Pick every unit in (Units in (Region centered at (Target point of ability being cast) with size (200.00, 200.00))) and do (Actions)
            • Boucle - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • Si - Conditions
                  • (Owner of (Picked unit)) Différent de (Owner of (Casting unit))
                • Alors - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 9 (gris)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 5, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 9 (gris) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 4 (Pourpre)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 5, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 4 (Pourpre) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 3 (Cyan)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 5, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 3 (Cyan) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 2 (Bleu)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 5, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 2 (Bleu) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Neutre Hostile
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Changer couleur
                      • For each (Integer A) from 0 to 5, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Neutre Hostile and Changer couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 1 (Rouge)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 5, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 1 (Rouge) and Garder couleur
                    • Sinon - Actions
                • Sinon - Actions
        • Sinon - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • Si - Conditions
          • (Level of Folie for (Casting unit)) Egal à 2
        • Alors - Actions
          • Groupe unité - Pick every unit in (Units in (Region centered at (Target point of ability being cast) with size (300.00, 300.00))) and do (Actions)
            • Boucle - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • Si - Conditions
                  • (Owner of (Picked unit)) Différent de (Owner of (Casting unit))
                • Alors - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 9 (gris)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 10, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 9 (gris) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 4 (Pourpre)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 10, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 4 (Pourpre) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 3 (Cyan)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 10, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 3 (Cyan) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 2 (Bleu)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 10, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 2 (Bleu) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Neutre Hostile
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Changer couleur
                      • For each (Integer A) from 0 to 10, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Neutre Hostile and Changer couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 1 (Rouge)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 10, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 1 (Rouge) and Garder couleur
                    • Sinon - Actions
                • Sinon - Actions
        • Sinon - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • Si - Conditions
          • (Level of Folie for (Casting unit)) Egal à 3
        • Alors - Actions
          • Groupe unité - Pick every unit in (Units in (Region centered at (Target point of ability being cast) with size (400.00, 400.00))) and do (Actions)
            • Boucle - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • Si - Conditions
                  • (Owner of (Picked unit)) Différent de (Owner of (Casting unit))
                • Alors - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 9 (gris)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 17, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 9 (gris) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 4 (Pourpre)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 17, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 4 (Pourpre) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 3 (Cyan)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 17, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 3 (Cyan) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 2 (Bleu)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 17, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 2 (Bleu) and Garder couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Neutre Hostile
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Changer couleur
                      • For each (Integer A) from 0 to 17, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Neutre Hostile and Changer couleur
                    • Sinon - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • Si - Conditions
                      • (Owner of (Picked unit)) Egal à Joueur 1 (Rouge)
                    • Alors - Actions
                      • Unité - Change ownership of (Picked unit) to Joueur 7 (Vert) and Garder couleur
                      • For each (Integer A) from 0 to 17, do (Actions)
                        • Boucle - Actions
                          • Unité - Order (Picked unit) to Attaquer (Random unit from (Units within 200.00 of (Position of (Picked unit))))
                          • Wait 1.00 seconds
                      • Unité - Change ownership of (Picked unit) to Joueur 1 (Rouge) and Garder couleur
                    • Sinon - Actions
                • Sinon - Actions
        • Sinon - Actions
Issue is my world editor is in french idk how to swich languages
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
Issue is my world editor is in french idk how to swich languages
That is annoying, but seeing the full structure with words I have to translate (I studied french four years in high school) is much more valuable than pseudo-triggers.
idk if it's possible with another way than "unit custom variable" which only stores an integer and i already use this for another unit so it would cause a lot of bugs.
or attaching information about the original owners to the units in a hashtable or some other method like a unit indexer.
Making that easy is the whole point of such an indexer. It gives each unit a unique number for its CV and then you can use that as the index of an array variable to store data specific to that unit in that array slot. You will have to change the one thing already using custom value to instead set/read/use SomeNewIntegerVariable[(Custom value of <the unit>)] rather than using CV directly, but that is a simple change.

Instead of repeating huge sections of your triggers with different If/Then/Else blocks, you can use a small few if-blocks to set some variables and then use those variables to determine how to proceed. This avoids code duplication which can be annoying to change or debug later. An example to show how you might increase the duration or area with spell level:
  • Set lvl = (Level of (Ability being cast) for (Triggering Unit))
  • Set SpellArea = (100.00 + (100.00 x (Real(lvl))))
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • lvl equal to 1
    • Then - Actions
      • Set SpellDurationInt = 5
    • Else - Actions
  • -------- Note that these don't have to be nested --------
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • lvl equal to 2
    • Then - Actions
      • Set SpellDurationInt= 10
    • Else - Actions
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • lvl greater than or equal to 3
    • Then - Actions
      • Set SpellDurationInt= 17
    • Else - Actions
  • Unit Group - Pick every unit within SpellArea of...
    • Loop - Actions
      • -------- etc. --------
      • For each (Integer A) from 1 to SpellDurationInt do (Actions)
        • Loop - Actions
          • -------- etc. --------
Don't do what you're doing with regions to find units. Regions are rectangles rather than circles and you are dynamically creating new regions with each search (which would need to be destroyed and cleaned up before they become memory leaks). Instead you should use Units In Range of Point.

You cannot put a Wait inside of a Unit Group - Pick... loop or a Player Group - Pick... loop. When such functions are used it starts an individual process thread for each unit, and when one such thread encounters a Wait it will simply crash and nothing after that (in the Loop) will execute. Put messages after your waits and you will see they never happen. The trigger execution before/after the Pick is not affected because only the sub-threads crash not the main one. This is likely why the units never seem to return to their original owner.

In order to periodically order the units to attack something nearby every 1 second you will need to store the units in some group variable and then save a reference to that group variable somewhere that you can access it periodically. This could be done with a periodic trigger and some dynamic indexing (though it should probably have a frequency higher than 1/s), by using a timer array and attaching information about the group to that specific timer instance, or by some other method to store data I haven't considered. Those are ultimately not difficult methods, but if you are unfamiliar with the process they may seem daunting.

Something nearly as effective and much simpler is to abuse JASS's ability to shadow GUI global variables locally to make them act as though they are local to the function. For something like this to work you need to consider/understand when and how GUI creates and uses a sub-function, because inside of those sub functions the local variable won't exist! Luckily here you won't need to refer to the integer counters or unit group we'll shadow, and GUI doesn't use sub-functions for integer loops anyway:
  • Actions
    • Custom script: local integer udg_CounterVariable //these MUST go first in the actions
    • Custom script: local integer udg_SpellDurationInt //match the name to the name of your variable but keep the udg_ prefx
    • Custom script: local group udg_GroupVariable
    • -------- do your other stuff --------
    • Set GroupVariable = (Units within ...)
    • Set CounterVariable = 0
    • Set SpellDurationInt = <whatever>
    • For each Integer CounterVariable from 1 to SpellDurationInt do (Actions)
      • Loop - Actions
        • Unit Group - Pick every unit in GroupVariable and do (Actions)
          • Loop - Actions
            • -------- stuff --------
            • -------- since this is a new thread for each unit, the local GroupVariable would NOT 'exist' here --------
        • Wait 1.00 seconds
    • -------- at end of trigger --------
    • Custom script: call DestroyGroup(udg_GroupVariable)
    • Custom script: set udg_GroupVariable = null //integers don't need to be nulled and destroyed, only the group because it is a handle
Note that the wait in the loop is okay here because while the wait is happening there's no way to overwrite the relevant variables (they're local!). This means any number of instances of this spell can be active at once without breaking the looping process for any of them (and they can loop any number of times each, too). It's still not good practice but it can be acceptably done with care.

This, however, will not work for multiple overlapping casts. In such a case a unit affected by a second cast would prematurely return to its original owner before the second cast finished (since the end of the first cast would return it). The simplest solution I see is to attach two bits of data to each unit when this spell is cast on them: their owning player and the number of instances of this spell they are currently affected by. But you'll only want to store the owning player if an owning player isn't already stored for this unit, and you'll only change the ownership back if the instance count is 0 (this is the last one to finish). This information will also have to be nulled when the final instance affecting the unit ends:
  • Set CV = (Custom value of (Picked unit))
  • -------- when the unit is changed to the temporary owner --------
  • Set InstanceCount[CV] = (InstanceCount[CV] + 1)
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • OriginalOwner[CV] equal to No Player
    • Then - Actions
      • Set OriginalOwner[CV] = (Owner of (Picked Unit))
    • Else - Actions
  • -------- when the instance ends --------
  • Set InstanceCount[CV] = (InstanceCount[CV] - 1)
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • InstanceCount[CV] less than or equal to 0
    • Then - Actions
      • Unit - Change owner of (Picked unit) to OriginalOwner[CV] ...
      • Set OriginalOwner[CV] = No Player
    • Else - Actions
Ask any further questions or for clarification if these methods don't make sense.
 
Level 3
Joined
Oct 9, 2023
Messages
20
That is annoying, but seeing the full structure with words I have to translate (I studied french four years in high school) is much more valuable than pseudo-triggers.


Making that easy is the whole point of such an indexer. It gives each unit a unique number for its CV and then you can use that as the index of an array variable to store data specific to that unit in that array slot. You will have to change the one thing already using custom value to instead set/read/use SomeNewIntegerVariable[(Custom value of <the unit>)] rather than using CV directly, but that is a simple change.

Instead of repeating huge sections of your triggers with different If/Then/Else blocks, you can use a small few if-blocks to set some variables and then use those variables to determine how to proceed. This avoids code duplication which can be annoying to change or debug later. An example to show how you might increase the duration or area with spell level:
  • Set lvl = (Level of (Ability being cast) for (Triggering Unit))
  • Set SpellArea = (100.00 + (100.00 x (Real(lvl))))
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • lvl equal to 1
    • Then - Actions
      • Set SpellDurationInt = 5
    • Else - Actions
  • -------- Note that these don't have to be nested --------
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • lvl equal to 2
    • Then - Actions
      • Set SpellDurationInt= 10
    • Else - Actions
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • lvl greater than or equal to 3
    • Then - Actions
      • Set SpellDurationInt= 17
    • Else - Actions
  • Unit Group - Pick every unit within SpellArea of...
    • Loop - Actions
      • -------- etc. --------
      • For each (Integer A) from 1 to SpellDurationInt do (Actions)
        • Loop - Actions
          • -------- etc. --------
Don't do what you're doing with regions to find units. Regions are rectangles rather than circles and you are dynamically creating new regions with each search (which would need to be destroyed and cleaned up before they become memory leaks). Instead you should use Units In Range of Point.

You cannot put a Wait inside of a Unit Group - Pick... loop or a Player Group - Pick... loop. When such functions are used it starts an individual process thread for each unit, and when one such thread encounters a Wait it will simply crash and nothing after that (in the Loop) will execute. Put messages after your waits and you will see they never happen. The trigger execution before/after the Pick is not affected because only the sub-threads crash not the main one. This is likely why the units never seem to return to their original owner.

In order to periodically order the units to attack something nearby every 1 second you will need to store the units in some group variable and then save a reference to that group variable somewhere that you can access it periodically. This could be done with a periodic trigger and some dynamic indexing (though it should probably have a frequency higher than 1/s), by using a timer array and attaching information about the group to that specific timer instance, or by some other method to store data I haven't considered. Those are ultimately not difficult methods, but if you are unfamiliar with the process they may seem daunting.

Something nearly as effective and much simpler is to abuse JASS's ability to shadow GUI global variables locally to make them act as though they are local to the function. For something like this to work you need to consider/understand when and how GUI creates and uses a sub-function, because inside of those sub functions the local variable won't exist! Luckily here you won't need to refer to the integer counters or unit group we'll shadow, and GUI doesn't use sub-functions for integer loops anyway:
  • Actions
    • Custom script: local integer udg_CounterVariable //these MUST go first in the actions
    • Custom script: local integer udg_SpellDurationInt //match the name to the name of your variable but keep the udg_ prefx
    • Custom script: local group udg_GroupVariable
    • -------- do your other stuff --------
    • Set GroupVariable = (Units within ...)
    • Set CounterVariable = 0
    • Set SpellDurationInt = <whatever>
    • For each Integer CounterVariable from 1 to SpellDurationInt do (Actions)
      • Loop - Actions
        • Unit Group - Pick every unit in GroupVariable and do (Actions)
          • Loop - Actions
            • -------- stuff --------
            • -------- since this is a new thread for each unit, the local GroupVariable would NOT 'exist' here --------
        • Wait 1.00 seconds
    • -------- at end of trigger --------
    • Custom script: call DestroyGroup(udg_GroupVariable)
    • Custom script: set udg_GroupVariable = null //integers don't need to be nulled and destroyed, only the group because it is a handle
Note that the wait in the loop is okay here because while the wait is happening there's no way to overwrite the relevant variables (they're local!). This means any number of instances of this spell can be active at once without breaking the looping process for any of them (and they can loop any number of times each, too). It's still not good practice but it can be acceptably done with care.

This, however, will not work for multiple overlapping casts. In such a case a unit affected by a second cast would prematurely return to its original owner before the second cast finished (since the end of the first cast would return it). The simplest solution I see is to attach two bits of data to each unit when this spell is cast on them: their owning player and the number of instances of this spell they are currently affected by. But you'll only want to store the owning player if an owning player isn't already stored for this unit, and you'll only change the ownership back if the instance count is 0 (this is the last one to finish). This information will also have to be nulled when the final instance affecting the unit ends:
  • Set CV = (Custom value of (Picked unit))
  • -------- when the unit is changed to the temporary owner --------
  • Set InstanceCount[CV] = (InstanceCount[CV] + 1)
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • OriginalOwner[CV] equal to No Player
    • Then - Actions
      • Set OriginalOwner[CV] = (Owner of (Picked Unit))
    • Else - Actions
  • -------- when the instance ends --------
  • Set InstanceCount[CV] = (InstanceCount[CV] - 1)
  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • InstanceCount[CV] less than or equal to 0
    • Then - Actions
      • Unit - Change owner of (Picked unit) to OriginalOwner[CV] ...
      • Set OriginalOwner[CV] = No Player
    • Else - Actions
Ask any further questions or for clarification if these methods don't make sense.

I really thank you for this. I'll be more careful about memory leaks and stuff like that in the future
 
Top