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

[Solved] How to people make this spells work for more than one player?

Status
Not open for further replies.
Level 17
Joined
Jun 2, 2009
Messages
1,141
  • Arcane Explosion
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Arcane Explosion // arcane
    • Actions
      • Set TempUnitArcane = (Casting unit)
      • Set TempPointArcane = (Target point of ability being cast)
      • Animation - Reset TempUnitArcane's animation
      • Special Effect - Create a special effect at TempPointArcane using Abilities\Spells\Orc\StasisTrap\StasisTotemTarget.mdl
      • Set TempEffectArcane = (Last created special effect)
      • Wait 1.50 seconds
      • Special Effect - Destroy TempEffectArcane
      • Special Effect - Create a special effect at TempPointArcane using war3mapImported\Arcane Nova.mdx
      • Unit Group - Pick every unit in (Units within 250.00 of TempPointArcane matching ((((Matching unit) belongs to an enemy of (Owner of TempUnitArcane)) Equal to True) and ((((Matching unit) is A structure) Equal to False) and (((Matching unit) is Mechanical) Equal to False)))) and do (Actions)
        • Loop - Actions
          • Unit - Cause TempUnitArcane to damage (Picked unit), dealing (50.00 + ((Real((Level of Arcane Explosion // arcane for TempUnitArcane))) x 50.00)) damage of attack type Chaos and damage type Unknown
      • Set TempEffectArcane = (Last created special effect)
      • Wait 1.00 seconds
      • Special Effect - Destroy TempEffectArcane
      • Custom script: call RemoveLocation(udg_TempPointArcane)
People told me this topic helps me a lot. But the problem is, my math is terrible and i don't understand "anything" about this system. Visualize: Dynamic Indexing i am trying to learn it but my brain can't handle. I feel myself like just started to create triggers...

How can i make my spell for mui? Or is there an easy way to learn it except of this Dynamic Indexing topic?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
Just to clarify since your thread title makes it a little unclear as to what you want. There's MPI and MUI.

MPI = Multi-Player-Instanceable
MUI = Multi-Unit-Instanceable

MPI means that the spell (or triggers in general) will work for multiple players at the same time. However, it doesn't guarantee that multiple instances of the same trigger run by the same player will work.

MUI means that the spell will work for multiple units at the same time regardless of who owns them. This is generally what people want since it has little to no limitations.

So which of these solutions should you use for your spell?

For MPI, these are the main requirements I can think of:
  • Each player can only cast one instance of the spell at a time. This generally means that they have a single hero with the spell.
  • The spell's duration is shorter than it's cooldown. In your case the duration is ~2.50 seconds with all of the Waits.
  • Things like Tome of Retraining or "Reset all ability cooldowns" can't be used.
If you meet those requirements then the MPI solution could work. How I would do it is add Arrays to all of your variables and use the function "Player number of (Owner of (Triggering unit))" as their [index]. For example:
  • Special Effect - Create a special effect at TempPointArcane[Player number of (Owner of (Triggering unit))] using war3mapImported\Arcane Nova.mdx
  • Set TempEffectArcane[Player number of (Owner of (Triggering unit))] = (Last created special effect)
(Triggering unit) is MUI friendly and will be maintained throughout the trigger even after Waits.

So since Player 1's player number is equal to 1, and Player 2's player number is equal to 2, etc. you know for a fact that each player will have their own set of variables when using these numbers as the [index] in the Arrays. For example:
TempUnitArcane[1] = Player 1's casting unit, TempUnitArcane[2] = Player 2's casting unit, TempUnitArcane[3] = Player 3's casting unit


An MUI solution would be to use Dynamic Indexing, which I know you said was confusing. Here is how you would do it:
  • Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Arcane Explosion
    • Actions
      • Set VariableSet AE_Index = (AE_Index + 1)
      • Set VariableSet AE_Caster[AE_Index] = (Triggering unit)
      • Set VariableSet AE_Player[AE_Index] = (Owner of AE_Caster[AE_Index])
      • Set VariableSet AE_Point[AE_Index] = (Target point of ability being cast)
      • Set VariableSet AE_Damage[AE_Index] = (50.00 + (50.00 x (Real((Level of (Ability being cast) for AE_Caster[AE_Index])))))
      • Special Effect - Create a special effect at AE_Point[AE_Index] using Abilities\Spells\Human\MassTeleport\MassTeleportTo.mdl
      • Set VariableSet AE_Effect[AE_Index] = (Last created special effect)
      • Set VariableSet AE_Duration[AE_Index] = 0
      • Trigger - Turn on Loop <gen>
  • Loop
    • Events
      • Time - Every 0.02 seconds of game time
    • Conditions
    • Actions
      • For each (Integer AE_Loop) from 1 to AE_Index, do (Actions)
        • Loop - Actions
          • -------- Track the amount of time that has passed (1 = 0.02 seconds): --------
          • Set VariableSet AE_Duration[AE_Loop] = (AE_Duration[AE_Loop] + 1)
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • AE_Duration[AE_Loop] Equal to 75
            • Then - Actions
              • -------- Deal damage to nearby organic enemy units: --------
              • Custom script: set bj_wantDestroyGroup = true
              • Unit Group - Pick every unit in (Units within 250.00 of AE_Point[AE_Loop].) and do (Actions)
                • Loop - Actions
                  • Set VariableSet AE_Picked = (Picked unit)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (AE_Picked belongs to an enemy of AE_Player[AE_Loop].) Equal to True
                      • (AE_Picked is alive) Equal to True
                      • (AE_Picked is A structure) Equal to False
                      • (AE_Picked is Mechanical) Equal to False
                    • Then - Actions
                      • Unit - Cause AE_Caster[AE_Loop] to damage AE_Picked, dealing AE_Damage[AE_Loop] damage of attack type Chaos and damage type Unknown
                    • Else - Actions
              • -------- --------
              • -------- Destroy previous special effect / Create new special effect: --------
              • Special Effect - Destroy AE_Effect[AE_Loop]
              • Special Effect - Create a special effect at AE_Point[AE_Loop] using Abilities\Weapons\GreenDragonMissile\GreenDragonMissile.mdl
              • Set VariableSet AE_Effect[AE_Loop] = (Last created special effect)
              • -------- --------
              • -------- We can Remove the Point now since it's no longer used: --------
              • Custom script: call RemoveLocation(udg_AE_Point[udg_AE_Loop])
            • Else - Actions
              • -------- End the spell: --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • AE_Duration[AE_Loop] Equal to 125
                • Then - Actions
                  • -------- Finish up whatever you need to do here: --------
                  • Special Effect - Destroy AE_Effect[AE_Loop]
                  • -------- --------
                  • -------- Here is the last step of Dynamic Indexing where you swap the current variables with the last [index]'s variables: --------
                  • Set VariableSet AE_Caster[AE_Loop] = AE_Caster[AE_Index]
                  • Set VariableSet AE_Player[AE_Loop] = AE_Player[AE_Index]
                  • Set VariableSet AE_Point[AE_Loop] = AE_Point[AE_Index]
                  • Set VariableSet AE_Damage[AE_Loop] = AE_Damage[AE_Index]
                  • Set VariableSet AE_Effect[AE_Loop] = AE_Effect[AE_Index]
                  • Set VariableSet AE_Duration[AE_Loop] = AE_Duration[AE_Index]
                  • -------- --------
                  • -------- And then trim the last [index] off and move the Loop back one cycle to compensate: --------
                  • Set VariableSet AE_Loop = (AE_Loop - 1)
                  • Set VariableSet AE_Index = (AE_Index - 1)
                  • -------- --------
                  • -------- Then check to see if AE_Index is equal to 0 which means that all instances of this spell are finished: --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • AE_Index Equal to 0
                    • Then - Actions
                      • Trigger - Turn off (This trigger)
                    • Else - Actions
                • Else - Actions
The Loop trigger should be Initially Off so that it isn't running by default.

I attached a map with a working example of this which requires the latest patch to open.
 

Attachments

  • Arcane Explosion Dynamic Indexing 1.w3m
    20.3 KB · Views: 7
Last edited:

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,891
The most important aspects of dynamic indexing are the casting event (obviously), specifically the incrementation of the index and turning the loop on. And in the periodic loop, you need to study how the deindexing part (end of spell) works, and WHY it is that way and what it is doing.
Learning a concept can be quite mind-boggling but I assure you that tutorial is very detailed and you need to be more resilient on it.
 
Level 17
Joined
Jun 2, 2009
Messages
1,141
Just to clarify since your thread title makes it a little unclear as to what you want. There's MPI and MUI.

MPI = Multi-Player-Instanceable
MUI = Multi-Unit-Instanceable

MPI means that the spell (or triggers in general) will work for multiple players at the same time. However, it doesn't guarantee that multiple instances of the same trigger run by the same player will work.

MUI means that the spell will work for multiple units at the same time regardless of who owns them. This is generally what people want since it has little to no limitations.

So which of these solutions should you use for your spell?

For MPI, these are the main requirements I can think of:
  • Each player can only cast one instance of the spell at a time. This generally means that they have a single hero with the spell.
  • The spell's duration is shorter than it's cooldown. In your case the duration is ~2.50 seconds with all of the Waits.
  • Things like Tome of Retraining or "Reset all ability cooldowns" can't be used.
If you meet those requirements then the MPI solution could work. How I would do it is add Arrays to all of your variables and use the function "Player number of (Owner of (Triggering unit))" as their [index]. For example:
  • Special Effect - Create a special effect at TempPointArcane[Player number of (Owner of (Triggering unit))] using war3mapImported\Arcane Nova.mdx
  • Set TempEffectArcane[Player number of (Owner of (Triggering unit))] = (Last created special effect)
(Triggering unit) is MUI friendly and will be maintained throughout the trigger even after Waits.

So since Player 1's player number is equal to 1, and Player 2's player number is equal to 2, etc. you know for a fact that each player will have their own set of variables when using these numbers as the [index] in the Arrays. For example:
TempUnitArcane[1] = Player 1's casting unit, TempUnitArcane[2] = Player 2's casting unit, TempUnitArcane[3] = Player 3's casting unit


An MUI solution would be to use Dynamic Indexing, which I know you said was confusing. Here is how you would do it:
  • Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Arcane Explosion
    • Actions
      • Set VariableSet AE_Index = (AE_Index + 1)
      • Set VariableSet AE_Caster[AE_Index] = (Triggering unit)
      • Set VariableSet AE_Player[AE_Index] = (Owner of AE_Caster[AE_Index])
      • Set VariableSet AE_Point[AE_Index] = (Target point of ability being cast)
      • Set VariableSet AE_Damage[AE_Index] = (50.00 + (50.00 x (Real((Level of (Ability being cast) for AE_Caster[AE_Index])))))
      • Special Effect - Create a special effect at AE_Point[AE_Index] using Abilities\Spells\Human\MassTeleport\MassTeleportTo.mdl
      • Set VariableSet AE_Effect[AE_Index] = (Last created special effect)
      • Set VariableSet AE_Duration[AE_Index] = 0
      • Trigger - Turn on Loop <gen>
  • Loop
    • Events
      • Time - Every 0.02 seconds of game time
    • Conditions
    • Actions
      • For each (Integer AE_Loop) from 1 to AE_Index, do (Actions)
        • Loop - Actions
          • -------- Track the amount of time that has passed (1 = 0.02 seconds): --------
          • Set VariableSet AE_Duration[AE_Loop] = (AE_Duration[AE_Loop] + 1)
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • AE_Duration[AE_Loop] Equal to 75
            • Then - Actions
              • -------- Deal damage to nearby organic enemy units: --------
              • Custom script: set bj_wantDestroyGroup = true
              • Unit Group - Pick every unit in (Units within 250.00 of AE_Point[AE_Loop].) and do (Actions)
                • Loop - Actions
                  • Set VariableSet AE_Picked = (Picked unit)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (AE_Picked belongs to an enemy of AE_Player[AE_Loop].) Equal to True
                      • (AE_Picked is alive) Equal to True
                      • (AE_Picked is A structure) Equal to False
                      • (AE_Picked is Mechanical) Equal to False
                    • Then - Actions
                      • Unit - Cause AE_Caster[AE_Loop] to damage AE_Picked, dealing AE_Damage[AE_Loop] damage of attack type Chaos and damage type Unknown
                    • Else - Actions
              • -------- --------
              • -------- Destroy previous special effect / Create new special effect: --------
              • Special Effect - Destroy AE_Effect[AE_Loop]
              • Special Effect - Create a special effect at AE_Point[AE_Loop] using Abilities\Weapons\GreenDragonMissile\GreenDragonMissile.mdl
              • Set VariableSet AE_Effect[AE_Loop] = (Last created special effect)
              • -------- --------
              • -------- We can Remove the Point now since it's no longer used: --------
              • Custom script: call RemoveLocation(udg_AE_Point[udg_AE_Loop])
            • Else - Actions
              • -------- End the spell: --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • AE_Duration[AE_Loop] Equal to 125
                • Then - Actions
                  • -------- Finish up whatever you need to do here: --------
                  • Special Effect - Destroy AE_Effect[AE_Loop]
                  • -------- --------
                  • -------- Here is the last step of Dynamic Indexing where you swap the current variables with the last [index]'s variables: --------
                  • Set VariableSet AE_Caster[AE_Loop] = AE_Caster[AE_Index]
                  • Set VariableSet AE_Player[AE_Loop] = AE_Player[AE_Index]
                  • Set VariableSet AE_Point[AE_Loop] = AE_Point[AE_Index]
                  • Set VariableSet AE_Damage[AE_Loop] = AE_Damage[AE_Index]
                  • Set VariableSet AE_Effect[AE_Loop] = AE_Effect[AE_Index]
                  • Set VariableSet AE_Duration[AE_Loop] = AE_Duration[AE_Index]
                  • -------- --------
                  • -------- And then trim the last [index] off and move the Loop back one cycle to compensate: --------
                  • Set VariableSet AE_Loop = (AE_Loop - 1)
                  • Set VariableSet AE_Index = (AE_Index - 1)
                  • -------- --------
                  • -------- Then check to see if AE_Index is equal to 0 which means that all instances of this spell are finished: --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • AE_Index Equal to 0
                    • Then - Actions
                      • Trigger - Turn off (This trigger)
                    • Else - Actions
                • Else - Actions
The Loop trigger should be Initially Off so that it isn't running by default.

I attached a map with a working example of this which requires the latest patch to open.
You are amazing as always. When you reply my posts, i don't know how can i thank. But sadly still i don't understand and i don't think i will understand this system. Because i am a a stupid person (for real) and after age 37 no more my brain can handle. I have created many maps, still active and still there are many things i don't understand.
Being stupid, old, sucking at math and intermediate english prevents me learning complex systems like this.

@Wrda Sadly this exceeding my limits. Still i don't understand any parts of this trigger. Somethings increasing and decreasing and i would like to understand what is going on. Is there any easy tutorial about this system? Maybe i can understand when the things are simple.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
This is how I came to understand this system and others like it:
Step 1: Learn how Arrays work.
Step 2: Learn how For Loops work.
Step 3: Create a For Loop that loops over an Array using a Loop variable. Understand what's happening here.

Tips:
  • Text messages can tell you the exact value of a variable and the order of how things happen. For example, in your For Loop you can Display a text message of your Loop variable which helps visualize the Loop's cycles.
  • Special Effects / Units can help you visualize where things are happening in the game world. This can be useful if you have a Point variable and want to see exactly where it is.

Complete those 3 steps and then revisit the Dynamic Indexing tutorial. Following the tutorial, recreate it's triggers and get them working properly. Once working, use my Tips so that you can see what's actually happening in-game when the triggers run. The important variables to display in a text message would be the Index / Loop variables. Also, anything that increases/decreases like a Duration variable and add text messages when things start and stop.

Here's an EXAMPLE of what your in-game message log might look like:

"Arcane Explosion was cast -> Current Index = 3"
"Current Loop = 1"
"This loop's Duration = 0.75"
"Current Loop = 2"
"This loop's Duration = 0.50"
"Current Loop = 3"
"This loop's Duration = 0.25"
(You can see here that the For Loop is repeating a second time since it's starting from the beginning again)
"Current Loop = 1"
"This loop's Duration = 1.00"
"Current Loop = 2"
"This loop's Duration = 0.75"
"Current Loop = 3"
"This loop's Duration = 0.50"

This example shows us that we have 3 instances of the spell currently running. So the For Loop is cycling 3 times, once per instance. Each For Loop cycle increases the Duration of that instance by 0.25 seconds. This shows us how our Arrays such as Duration are being tracked over time.


You don't have to understand everything at first. I learned how to use Dynamic Indexing without knowing why I had to do any of those weird steps at the end where you change all of the variables to something else. I understand it now, but my point is that you can learn and master this design pattern without needing to know every single detail at first (or sometimes ever).
 
Last edited:
Status
Not open for further replies.
Top