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

Help Spawning units with arrays using loops

Status
Not open for further replies.
Level 3
Joined
Sep 5, 2019
Messages
33
I am trying to have creeps regularly respawn (every minute), if the hero has defeated that group. The first group spawns in the first region, and after that nothing happens. What am i doing wrong? Thanks!
Screenshot (61).png
Screenshot (62).png
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Unit Group and Player Group arrays need to have their Size initialized. So open the Variable Editor and set the Size of your Unit Group to however many of these there will be.

That being said, your triggers looks a bit weird. What are you trying to achieve exactly?

I feel like it'd be easier to just catch when a creep dies, remove it from the unit group that it belongs to, subtract from an integer counter that keeps track of the total units in the unit group (more efficient than re-counting the total number of units in said group each time), and if that integer is equal to 0 you know that the group is empty and you can respawn the creeps.

Also, be careful with using For Loops that all share the same Integer variable. IntegerA is a global variable and each of your nested Loops (loop within a loop) is using it, thus changing it's value for ALL of them.
 
Last edited:
Level 3
Joined
Sep 5, 2019
Messages
33
Unit Group and Player Group arrays need to have their Size initialized. So open the Variable Editor and set the Size of your Unit Group to however many of these there will be.

That being said, your triggers looks a bit weird. What are you trying to achieve exactly?

I feel like it'd be easier to just catch when a creep dies, remove it from the unit group that it belongs to, subtract from an integer counter that keeps track of the total units in the unit group (more efficient than re-counting the total number of units in said group each time), and if that integer is equal to 0 you know that the group is empty and you can respawn the creeps.

Also, be careful with using For Loops that all share the same Integer variable. IntegerA is a global variable and each of your nested Loops (loop within a loop) is using it, thus changing it's value for ALL of them.
I have their size set to thirty, although there is no initial value (empty group). At the moment I am not using all thirty, although I plan to. Do they all need to be filled for it to work?

My goal here is to respawn creeps (unless that group is still there), and spawn more creeps per group the higher my hero is leveled. Also I have exceeded my unit limit in world editor (its a big map) which is why I'm spawning them from the start, instead of placing them in editor.

You're right, I could add to an integer with each spawned unit, instead of counting unit groups, ill change that, thanks! But that will just make it simpler, I don't think its causing an error.

Ahh I didn't know that about integer A, so for the second loop I should use 'for each integer b'? What does the variable even mean there, how does it affect it?

Thank you so much!
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Regarding the Unit Group:
The Size that you set is the limit to how many Unit Groups you can create. I would always keep the Size at the exact amount that you need (if possible) as this is more efficient.


Regarding For Loops:
The variable in the loop is an Integer that represents the current iteration of the loop. So if you loop from 1 to 5 using Integer A:
First cycle, Integer A = 1, the actions run...
Next cycle, Integer A = 2, the actions run...
Next cycle, Integer A = 3, the actions run...
Next cycle, Integer A = 4, the actions run...
Final cycle, Integer A = 5, the actions run...

Integer A provides us with a way to reference the current iteration of the loop, which can be very useful.
For example, let's say your map has 5 players in it, and you wanted to create a Hero for each of them and track that Hero using a Unit array:
  • For each (Integer A) from 1 to 5 do (Actions)
  • Unit - Create 1 Paladin for Player(Integer A) at some point...
  • Set Variable PlayerHero[Integer A] = Last created unit
Here we're using Integer A to represent the Player in our Create unit function (using convert player index) and the Player Number in the [index] of our unit array, PlayerHero[].

Use cases aside, the general rule of thumb is to create new Integer variables for your For Loops and use those instead of Integer A/B, this way you avoid overwriting their values and can nest as many loops as you'd like. To do this look for the For Loop action that lets you choose an Integer variable instead of having to use A/B.


Regarding your respawn system:
Another benefit of doing it the way I described is that the time before the creeps respawn would be constant.
Kill last creep -> Wait 60.00 seconds -> Respawn creeps.

With your design, there's a chance that you kill the last creep a second before the Periodic Interval occurs, causing the creeps to respawn almost immediately.
 
Last edited:
Level 3
Joined
Sep 5, 2019
Messages
33
Regarding the Unit Group:
The Size that you set is the limit to how many Unit Groups you can create. I would always keep the Size at the exact amount that you need (if possible) as this is more efficient.


Regarding For Loops:
The variable is an Integer that represents the current iteration of the loop. So if you loop from 1 to 5 using Integer A:
First cycle, Integer A = 1, the actions run...
Next cycle, Integer A = 2, the actions run...
Next cycle, Integer A = 3, the actions run...
Next cycle, Integer A = 4, the actions run...
Final cycle, Integer A = 5, the actions run...

So Integer A can be referenced if you wanted to take advantage of that number. For example, let's say your map has 5 players in it, and you wanted to create a Hero for each of them and track that Hero using a Unit array:
  • For each (Integer A) from 1 to 5 do (Actions)
  • Unit - Create 1 Paladin for Player(Integer A) at some point...
  • Set Variable PlayerHero[Integer A] = Last created unit
Here we're using Integer A to represent the Player in our Create unit function (using convert player index) and the Player Number in the [index] of our unit array, PlayerHero[].

Use case aside, the general rule of thumb is to create new Integer variables for your For Loops and use those instead of Integer A/B, this way you avoid overwriting their values and can nest as many loops as you'd like. To do this look for the For Loop action that lets you choose an Integer variable of your liking.


Regarding your respawn system:
Another benefit of doing it the way I described is that the time before the creeps respawn would always be the same.
Kill last creep -> Wait 60.00 seconds -> Respawn creeps.

With your design, there's a chance that you kill the last creep a second before the Periodic Interval occurs, causing the creeps to respawn almost immediately.
Will do! Thank you for all the help (=
 
Level 3
Joined
Sep 5, 2019
Messages
33
Regarding the Unit Group:
The Size that you set is the limit to how many Unit Groups you can create. I would always keep the Size at the exact amount that you need (if possible) as this is more efficient.


Regarding For Loops:
The variable in the loop is an Integer that represents the current iteration of the loop. So if you loop from 1 to 5 using Integer A:
First cycle, Integer A = 1, the actions run...
Next cycle, Integer A = 2, the actions run...
Next cycle, Integer A = 3, the actions run...
Next cycle, Integer A = 4, the actions run...
Final cycle, Integer A = 5, the actions run...

Integer A provides us with a way to reference the current iteration of the loop, which can be very useful.
For example, let's say your map has 5 players in it, and you wanted to create a Hero for each of them and track that Hero using a Unit array:
  • For each (Integer A) from 1 to 5 do (Actions)
  • Unit - Create 1 Paladin for Player(Integer A) at some point...
  • Set Variable PlayerHero[Integer A] = Last created unit
Here we're using Integer A to represent the Player in our Create unit function (using convert player index) and the Player Number in the [index] of our unit array, PlayerHero[].

Use cases aside, the general rule of thumb is to create new Integer variables for your For Loops and use those instead of Integer A/B, this way you avoid overwriting their values and can nest as many loops as you'd like. To do this look for the For Loop action that lets you choose an Integer variable instead of having to use A/B.


Regarding your respawn system:
Another benefit of doing it the way I described is that the time before the creeps respawn would be constant.
Kill last creep -> Wait 60.00 seconds -> Respawn creeps.

With your design, there's a chance that you kill the last creep a second before the Periodic Interval occurs, causing the creeps to respawn almost immediately.
Hey! Sorry one more question, you said "catch when a creep dies, remove it from the unit group that it belongs to", what's an easy way to do that? The trigger I'm using right now feels ridiculously overcomplicated, not to mention inefficient.

Screenshot (66).png
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
So I created a system that I think is more what you're aiming for. Here are the triggers:

  • Setup Creep Respawn
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Create the hashtable that will store all of this data: --------
      • Hashtable - Create a hashtable
      • Set VariableSet CreepHashtable = (Last created hashtable)
      • -------- --------
      • -------- Keep track of the creep camp triggers that run when you want to respawn a specific group (do this before creating any): --------
      • Set VariableSet CreepCampTriggers[1] = Creep Camp 1 <gen>
      • Set VariableSet CreepCampTriggers[2] = Creep Camp 2 <gen>
      • -------- --------
      • -------- Create the initial set of creeps (you can use this boolean to determine whether or not you want to Wait): --------
      • Set VariableSet CampIgnoreTheWait = True
      • Trigger - Run Create Initial Creeps <gen> (ignoring conditions)
      • Set VariableSet CampIgnoreTheWait = False
  • Create Initial Creeps
    • Events
    • Conditions
    • Actions
      • -------- Don't forget to adjust the Array Size of your CreepCampGroup variable to fit all of these camps! --------
      • For each (Integer CampIndex) from 1 to 30, do (Actions)
        • Loop - Actions
          • Trigger - Run CreepCampTriggers[CampIndex] (ignoring conditions)
Here's an example of spawning a set of creeps at index 1:
  • Creep Camp 1
    • Events
    • Conditions
    • Actions
      • Custom script: local integer udg_CampIndexLocal = udg_CampIndex
      • -------- --------
      • -------- Delay the respawn IF you want to. Waits can be risky if not used properly but this is fine: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CampIgnoreTheWait Equal to False
        • Then - Actions
          • Wait 60.00 seconds
          • Set VariableSet CampIndex = CampIndexLocal
        • Else - Actions
      • -------- --------
      • -------- Respawn creep camp: --------
      • For each (Integer CampLoopA) from 1 to 5, do (Actions)
        • Loop - Actions
          • Unit - Create 1 Footman for Neutral Hostile at (Center of (Playable map area)) facing Default building facing degrees
          • -------- --------
          • -------- Save the Unit Group and it's Camp Index to the creep: --------
          • Hashtable - Save Handle OfCreepCampGroup[CampIndex] as (Key (Last created unit).) of 0 in CreepHashtable.
          • Hashtable - Save CampIndex as (Key (Last created unit).) of 1 in CreepHashtable.
          • -------- --------
          • -------- Keep track of how many of these creeps exist: --------
          • Set VariableSet CreepCount[CampIndex] = (CreepCount[CampIndex] + 1)
      • For each (Integer CampLoopB) from 1 to 5, do (Actions)
        • Loop - Actions
          • Unit - Create 1 Knight for Neutral Hostile at (Center of (Playable map area)) facing Default building facing degrees
          • -------- --------
          • -------- Save the Unit Group and it's Camp Index to the creep: --------
          • Hashtable - Save Handle OfCreepCampGroup[CampIndex] as (Key (Last created unit).) of 0 in CreepHashtable.
          • Hashtable - Save CampIndex as (Key (Last created unit).) of 1 in CreepHashtable.
          • -------- --------
          • -------- Keep track of how many of these creeps exist: --------
          • Set VariableSet CreepCount[CampIndex] = (CreepCount[CampIndex] + 1)
Here's an example of spawning a different set of creeps and keeping track of them at index 2:
  • Creep Camp 2
    • Events
    • Conditions
    • Actions
      • Custom script: local integer udg_CampIndexLocal = udg_CampIndex
      • -------- --------
      • -------- Delay the respawn IF you want to. Waits can be risky if not used properly but this is fine: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CampIgnoreTheWait Equal to False
        • Then - Actions
          • Wait 60.00 seconds
          • Set VariableSet CampIndex = CampIndexLocal
        • Else - Actions
      • -------- --------
      • -------- Respawn creep camp: --------
      • For each (Integer CampLoopA) from 1 to 5, do (Actions)
        • Loop - Actions
          • Unit - Create 1 Tauren for Neutral Hostile at (Center of (Playable map area)) facing Default building facing degrees
          • -------- --------
          • -------- Save the Unit Group and it's Camp Index to the creep: --------
          • Hashtable - Save Handle OfCreepCampGroup[CampIndex] as (Key (Last created unit).) of 0 in CreepHashtable.
          • Hashtable - Save CampIndex as (Key (Last created unit).) of 1 in CreepHashtable.
          • -------- --------
          • -------- Keep track of how many of these creeps exist: --------
          • Set VariableSet CreepCount[CampIndex] = (CreepCount[CampIndex] + 1)
      • For each (Integer CampLoopB) from 1 to 5, do (Actions)
        • Loop - Actions
          • Unit - Create 1 Wind Rider for Neutral Hostile at (Center of (Playable map area)) facing Default building facing degrees
          • -------- --------
          • -------- Save the Unit Group and it's Camp Index to the creep: --------
          • Hashtable - Save Handle OfCreepCampGroup[CampIndex] as (Key (Last created unit).) of 0 in CreepHashtable.
          • Hashtable - Save CampIndex as (Key (Last created unit).) of 1 in CreepHashtable.
          • -------- --------
          • -------- Keep track of how many of these creeps exist: --------
          • Set VariableSet CreepCount[CampIndex] = (CreepCount[CampIndex] + 1)
  • Creep Dies
    • Events
      • Unit - A unit owned by Neutral Hostile Dies
    • Conditions
    • Actions
      • -------- Load the index of the camp that the dying creep belonged to: --------
      • Set VariableSet CampIndex = (Load (Key (Triggering unit).) of 1 from CreepHashtable.)
      • -------- --------
      • -------- If it doesn't belong to any then skip the rest of the trigger: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CampIndex Equal to 0
        • Then - Actions
          • Skip remaining actions
        • Else - Actions
      • -------- --------
      • -------- Remove it from the camp group: --------
      • Unit Group - Remove (Triggering unit) from (Load (Key (Triggering unit).) of 0 in CreepHashtable.).
      • -------- --------
      • -------- This removes the dead unit's data from the Hashtable. Do this after loading all necessary data: --------
      • Hashtable - Clear all child hashtables of child (Key (Triggering unit).) in CreepHashtable.
      • -------- --------
      • -------- Finally, subtract the creep count and check if it's now 0 and the respawn should occur: --------
      • Set VariableSet CreepCount[CampIndex] = (CreepCount[CampIndex] - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CreepCount[CampIndex] Equal to 0
        • Then - Actions
          • Trigger - Run CreepCampTriggers[CampIndex] (ignoring conditions)
        • Else - Actions

I tested it and it works great. Now each group of creeps is it's own entity that will respawn separately from the others 60.00 seconds (adjustable) after the last of it's group has died.

Variables:
CampIndex = Integer
CampIndexLocal = Integer
CampLoopA = Integer
CampLoopB = Integer
CampIgnoreTheWait = Boolean
CreepCampGroup = Unit Group - Array (Size 30)
CreepHashtable = Hashtable
CreepCount = Integer - Array
CreepCampTriggers = Trigger - Array
 

Attachments

  • Creep Camp.w3m
    20.1 KB · Views: 9
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Just to get your attention, I fixed some bugs and edited my previous post. I confirmed that everything works now. I think you'll like this setup a lot more than what you were attempting to do previously, it's a lot more modular.

If you do implement it I recommend creating all of the Variables first before recreating my triggers.
 
Status
Not open for further replies.
Top