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

[Solved] Random N units from unit group help

Status
Not open for further replies.
Level 5
Joined
Dec 30, 2022
Messages
36
I have the below trigger that is selecting 3 random units around my voidwalker and spawns a dummy unit to attack them. The dummy unit / attacking works fine.

HOWEVER
I can't for the life of me get the "Random N" to stop selecting duplicate units. I tried implementing the below code to no avail. Can anyone help me here? I want the code to select three unique units every time. The below seems to at least 1 out of 5 times, select and attack the same unit three times (I put ~20 units a circle within the dummy attack range around the voidwalker, no issues there.)

  • Shadow Strikes
    • Events
      • Time - Every 4.00 seconds of game time
    • Conditions
    • Actions
      • Set VariableSet i = 1
      • For each (Integer i) from 1 to 3, do (Actions)
        • Loop - Actions
          • Unit Group - Pick every unit in (Random 1 units from (Units within 700.00 of (Position of Greater Voidwalker 0014 <gen>).)) and do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ((Picked unit) is in shadowStrikePicks.) Equal to False
                  • ((Picked unit) is in (Units owned by Neutral Hostile.).) Equal to True
                  • ((Picked unit) is dead) Equal to False
                • Then - Actions
                  • Unit Group - Add (Picked unit) to shadowStrikePicks
                • Else - Actions
                  • Set VariableSet i = (i - 1)
                  • Unit Group - Remove (Picked unit) from shadowStrikePicks.
                  • Game - Display to (All players) the text: duplicate found
      • Unit Group - Pick every unit in shadowStrikePicks and do (Actions)
        • Loop - Actions
          • Unit - Create 1 Shadowstrikedummy for Player 1 (Red) at (Position of Greater Voidwalker 0014 <gen>) facing (Position of (Picked unit))
          • Unit - Add a 0.50 second Generic expiration timer to (Last created unit)
          • Unit - Order (Last created unit) to Attack (Picked unit)
          • Game - Display to (All players) the text: attack committed
      • Unit Group - Remove all units from shadowStrikePicks.
 
Last edited:

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,641
When you subtract 1 from i you're setting the For Loop back a cycle so it repeats an additional time.

Here's a cleaner solution that I recommend:
  • Shadow Strikes
    • Events
      • Time - Every 4.00 seconds of game time
    • Conditions
    • Actions
      • -------- Setup variables: --------
      • Set VariableSet SS_Point[0] = (Position of Paladin 0001 <gen>)
      • Set VariableSet SS_UnitGroup = (Units within 700.00 of SS_Point[0].)
      • Set VariableSet SS_Count = 0
      • -------- --------
      • -------- Remove unwanted units from the unit group: --------
      • Unit Group - Pick every unit in SS_UnitGroup and do (Actions)
        • Loop - Actions
          • Set VariableSet SS_Unit = (Picked unit)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of SS_Unit) Equal to Neutral Hostile
              • (SS_Unit is alive) Equal to True
            • Then - Actions
              • Set VariableSet SS_Count = (SS_Count + 1)
            • Else - Actions
              • Unit Group - Remove SS_Unit from SS_UnitGroup.
      • -------- --------
      • -------- Limit the number of targets hit: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SS_Count Greater than 3
        • Then - Actions
          • Set VariableSet SS_Count = 3
        • Else - Actions
      • -------- --------
      • -------- Attack X random targets: --------
      • For each (Integer SS_Loop) from 1 to SS_Count, do (Actions)
        • Loop - Actions
          • Set VariableSet SS_Unit = (Random unit from SS_UnitGroup)
          • Set VariableSet SS_Point[1] = (Position of SS_Unit)
          • -------- --------
          • -------- This guarantees that the same unit won't get picked twice: --------
          • Unit Group - Remove SS_Unit from SS_UnitGroup.
          • -------- --------
          • Unit - Create 1 Footman for (Owner of Paladin 0001 <gen>) at SS_Point[0] facing SS_Point[1]
          • Unit - Add a 0.50 second Generic expiration timer to (Last created unit)
          • Unit - Order (Last created unit) to Attack SS_Unit
          • -------- --------
          • Custom script: call RemoveLocation(udg_SS_Point[1])
      • -------- --------
      • -------- Clean up memory leaks: --------
      • Custom script: call RemoveLocation(udg_SS_Point[0])
      • Custom script: call DestroyGroup(udg_SS_UnitGroup)
 
Level 39
Joined
Feb 27, 2007
Messages
5,054
Since Uncle didn't explain the usage of the custom script lines: it's to prevent memory leaks from the point and group variables. If you don't know what that means, read these:
 
Level 5
Joined
Dec 30, 2022
Messages
36
When you subtract 1 from i you're setting the For Loop back a cycle so it repeats an additional time.

Here's a cleaner solution that I recommend:
  • Shadow Strikes
    • Events
      • Time - Every 4.00 seconds of game time
    • Conditions
    • Actions
      • -------- Setup variables: --------
      • Set VariableSet SS_Point[0] = (Position of Paladin 0001 <gen>)
      • Set VariableSet SS_UnitGroup = (Units within 700.00 of SS_Point[0].)
      • Set VariableSet SS_Count = 0
      • -------- --------
      • -------- Remove unwanted units from the unit group: --------
      • Unit Group - Pick every unit in SS_UnitGroup and do (Actions)
        • Loop - Actions
          • Set VariableSet SS_Unit = (Picked unit)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of SS_Unit) Equal to Neutral Hostile
              • (SS_Unit is alive) Equal to True
            • Then - Actions
              • Set VariableSet SS_Count = (SS_Count + 1)
            • Else - Actions
              • Unit Group - Remove SS_Unit from SS_UnitGroup.
      • -------- --------
      • -------- Limit the number of targets hit: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SS_Count Greater than 3
        • Then - Actions
          • Set VariableSet SS_Count = 3
        • Else - Actions
      • -------- --------
      • -------- Attack X random targets: --------
      • For each (Integer SS_Loop) from 1 to SS_Count, do (Actions)
        • Loop - Actions
          • Set VariableSet SS_Unit = (Random unit from SS_UnitGroup)
          • Set VariableSet SS_Point[1] = (Position of SS_Unit)
          • -------- --------
          • -------- This guarantees that the same unit won't get picked twice: --------
          • Unit Group - Remove SS_Unit from SS_UnitGroup.
          • -------- --------
          • Unit - Create 1 Footman for (Owner of Paladin 0001 <gen>) at SS_Point[0] facing SS_Point[1]
          • Unit - Add a 0.50 second Generic expiration timer to (Last created unit)
          • Unit - Order (Last created unit) to Attack SS_Unit
          • -------- --------
          • Custom script: call RemoveLocation(udg_SS_Point[1])
      • -------- --------
      • -------- Clean up memory leaks: --------
      • Custom script: call RemoveLocation(udg_SS_Point[0])
      • Custom script: call DestroyGroup(udg_SS_UnitGroup)

Thanks for this Uncle, gave me a much better understanding of best practices with variables that I didn't know I didn't have..

Since Uncle didn't explain the usage of the custom script lines: it's to prevent memory leaks from the point and group variables. If you don't know what that means, read these:
Also extremely helpful. I have a decent understanding of how mem leaks work but this is great.

+rep to both
 
Status
Not open for further replies.
Top