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

[Trigger] Adding/Removing players from player group

Status
Not open for further replies.
Level 5
Joined
Jun 25, 2005
Messages
92
The event fires. Why doesn't the victory screen show for Player 1?

  • VictoryTimer Expired
    • Events
      • Time - HumanWinTimer expires
    • Conditions
    • Actions
      • Player Group - Remove all players from SideHumans
      • Player Group - Add Player 1 (Red) to SideHumans
      • Set TempPlayerGroup = SideHumans
      • Player Group - Pick every player in TempPlayerGroup and do (Actions)
        • Loop - Actions
          • Game - Victory (Picked player) (Show dialogs, Show scores)
      • Custom script: call DestroyForce(udg_TempPlayerGroup)
 

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,183
Honestly, I think it should work.

But on the other hands, you make it needlessly complex unless that is a WIP.
Could just put victory for player 1 without the complications.

But if this indeed is a wip..
My best guess is that something is wrong with "SideHumans"
Can you use a player group and write out all players in that group to prove that it is not equal to null?

I think that it is a group you have used before, and used a similar method as you did in this trigger in order to not destroy it.
But both variables point to the same force, so even if you use another variable the force is still destroyed.
 
Level 5
Joined
Jun 25, 2005
Messages
92
Printing the names of the players in SideHumans returned nothing, and printing the names of the allies of a player that should belong to that group does work, so indeed there is something wrong with SideHumans.

I replaced SideHumans like following, and the victory trigger works now:
  • Set TempPlayerGroup = (All players matching (((Matching player) is an ally of Neutral Passive) Equal to True))
In fact, I've been suspecting that SideHumans didn't work for a while now, but was under the false impression that using the temporary player group would prevent SideHumans from being destroyed. So, if I set TempPlayerGroup to AllPlayers and destroyed TempPlayerGroup, would Allplayers be gone, too, because they point to the same force?

This trigger below is part of my map initialization, which uses these temporary player groups several times. Should I just remove the "call DestroyForce(udg_TempPlayerGroup)" custom scripts?

  • Init during mapinit
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Initial Visibility --------
      • Visibility - Disable fog of war
      • Visibility - Disable black mask
      • For each (Integer A) from 1 to 9, do (Actions)
        • Loop - Actions
          • Visibility - Create an initially Enabled visibility modifier for (Player((Integer A))) emitting Black mask across (Entire map)
      • Game - Hide creep camps on the minimap
      • -------- Player relations, with additional player slots --------
      • Player Group - Add Player 1 (Red) to SideHumans
      • Player Group - Add Player 2 (Blue) to SideHumans
      • Player Group - Add Player 3 (Teal) to SideHumans
      • Player Group - Add Player 4 (Purple) to SideHumans
      • Player Group - Add Player 5 (Yellow) to SideHumans
      • Player Group - Add Player 6 (Orange) to SideHumans
      • Player Group - Add Player 7 (Green) to SideHumans
      • Player Group - Add Player 8 (Pink) to SideHumans
      • Player Group - Add Player 9 (Gray) to SideHumans
      • Player Group - Add Neutral Passive to SideHumans
      • Player Group - Add Neutral Victim to SideHumans
      • Player Group - Add Player 10 (Light Blue) to SideZombies
      • Player Group - Add Player 11 (Dark Green) to SideZombies
      • Player Group - Add Player 12 (Brown) to SideZombies
      • Player Group - Add Neutral Extra to SideZombies
      • Set TempPlayerGroup = SideHumans
      • Player Group - Pick every player in TempPlayerGroup and do (Actions)
        • Loop - Actions
          • Player - Turn Gives bounty On for (Picked player)
          • -------- Allies --------
          • For each (Integer A) from 1 to 9, do (Actions)
            • Loop - Actions
              • Player - Make (Picked player) treat (Player((Integer A))) as an Ally
              • Player - Make (Picked player) treat Neutral Passive as an Ally
              • Player - Make (Picked player) treat Neutral Victim as an Ally
              • Player - Make (Player((Integer A))) treat (Picked player) as an Ally
              • Player - Make (Player((Integer A))) treat Neutral Passive as an Ally
              • Player - Make (Player((Integer A))) treat Neutral Victim as an Ally
              • Player - For (Picked player), turn Shared vision Off toward (Player((Integer A)))
              • Player - For Neutral Passive, turn Shared vision Off toward (Player((Integer A)))
              • Player - For Neutral Victim, turn Shared vision Off toward (Player((Integer A)))
              • -------- extra --------
          • Player - For (Picked player), turn Alliance (non-aggression) On toward Neutral Passive
          • Player - For (Picked player), turn Shared vision Off toward Neutral Passive
          • Player - For (Picked player), turn Alliance (non-aggression) On toward Neutral Victim
          • Player - For (Picked player), turn Shared vision Off toward Neutral Victim
          • -------- Enemies --------
          • Player - Make (Picked player) treat Player 10 (Light Blue) as an Enemy
          • Player - Make (Picked player) treat Player 11 (Dark Green) as an Enemy
          • Player - Make (Picked player) treat Player 12 (Brown) as an Enemy
          • Player - Make (Picked player) treat Neutral Extra as an Enemy
      • Custom script: call DestroyForce(udg_TempPlayerGroup)
      • Set TempPlayerGroup = Player Group - Neutral Passive
      • Player Group - Make TempPlayerGroup treat (All allies of Neutral Passive) as an Ally
      • Custom script: call DestroyForce(udg_TempPlayerGroup)
      • Set TempPlayerGroup = SideZombies
      • Player Group - Pick every player in TempPlayerGroup and do (Actions)
        • Loop - Actions
          • Player - Turn Gives bounty On for (Picked player)
          • -------- Allies --------
          • Player - Make (Picked player) treat Player 10 (Light Blue) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 11 (Dark Green) as an Ally with shared vision
          • Player - Make (Picked player) treat Player 12 (Brown) as an Ally with shared vision
          • Player - Make (Picked player) treat Neutral Extra as an Ally with shared vision
          • -------- Enemies --------
          • Player - Make (Picked player) treat Player 1 (Red) as an Enemy
          • Player - Make (Picked player) treat Player 2 (Blue) as an Enemy
          • Player - Make (Picked player) treat Player 3 (Teal) as an Enemy
          • Player - Make (Picked player) treat Player 4 (Purple) as an Enemy
          • Player - Make (Picked player) treat Player 5 (Yellow) as an Enemy
          • Player - Make (Picked player) treat Player 6 (Orange) as an Enemy
          • Player - Make (Picked player) treat Player 7 (Green) as an Enemy
          • Player - Make (Picked player) treat Player 8 (Pink) as an Enemy
          • Player - Make (Picked player) treat Player 9 (Gray) as an Enemy
          • Player - Make (Picked player) treat Neutral Passive as an Enemy
          • Player - Make (Picked player) treat Neutral Victim as an Enemy
      • Custom script: call DestroyForce(udg_TempPlayerGroup)
      • Custom script: call DestroyTrigger(GetTriggeringTrigger())
By the way, the reason that I'm using player groups here in the first place is because who is part of SideHumans can change over the course of the map (due to readjusting for leavers).
 
Last edited:
Level 13
Joined
May 10, 2009
Messages
868
When your map loads, each variable points to a new created force. Let's call them A and B. So:
JASS:
set TempPlayerGroup = CreateForce() // A
set SideHumans = CreateForce() // B

Your trigger contains this:
  • Set TempPlayerGroup = SideHumans
  • Player Group - Pick every player in TempPlayerGroup and do (Actions)
    • Loop - Actions
      • -------- some stuff --------
  • Custom script: call DestroyForce(udg_TempPlayerGroup)
Doing this
  • Set TempPlayerGroup = SideHumans
will make TempPlayerGroup point to force "B". If you destroy it, "SideHumans" is affected too. Also, force "A" becomes a leak, because you can't reference it anymore, making it useless.

You should just use "SideHumans" directly.
 
Last edited:
Level 13
Joined
Jul 15, 2007
Messages
763
In Blizzard's consistent style, two variables of the same type are usually fine when you set them like A = B. A stays A, and B stays B. If you turn A into C afterwards, B doesn't turn into C, it stays as B. If you destroy A, B is still there.

For Unit Groups and Player Groups however (and i'm guessing other things), when you set a logic like A = B, they both become contingent on each other. In some sense, they essentially become A = A (or B = B) and if you modify one of them, the other is also affected. In your case, if you destroy it, you destroy both of them. Even if you cleared your temp group instead of destroying it, you would also inadvertently clear SideHumans. If you added another player to TempGroup, that player would also be added to SideHumans.

I wonder if anyone knows why it might work like that :p
 
Level 5
Joined
Jun 16, 2004
Messages
108
In Blizzard's consistent style, two variables of the same type are usually fine when you set them like A = B. A stays A, and B stays B. If you turn A into C afterwards, B doesn't turn into C, it stays as B. If you destroy A, B is still there.

For Unit Groups and Player Groups however (and i'm guessing other things), when you set a logic like A = B, they both become contingent on each other. In some sense, they essentially become A = A (or B = B) and if you modify one of them, the other is also affected. In your case, if you destroy it, you destroy both of them. Even if you cleared your temp group instead of destroying it, you would also inadvertently clear SideHumans. If you added another player to TempGroup, that player would also be added to SideHumans.

I wonder if anyone knows why it might work like that :p

What you are referring to is basically the difference between value semantics and reference semantics. If you know a language that has pointer/reference types and value types, like C++, then the parallel is easy to see.

If you think of a JASS variable as being like a piece of paper, we can make up some analogies which make the difference apparent.

With value semantics, we might be working with a value type like integer. If you start with two blank pieces of paper and write 5 on one of them, then one of the papers would have 5 and the other would be blank. If you did the equivalent of "set blank_paper = paper_with_5_on_it", that would be like you just writing 5 on the blank paper. Now both papers would have 5 on them. You could even do arithmetic with one of the papers and change its value to something else, like 13. Then you would have a paper with 13 and a paper with 5. It is apparent that changing one paper does not affect the other, just as you would expect.

With reference semantics the situation can change. Bring up two new pieces of paper, and now we will write something that sounds more reference-like, such as the address to your house. Write the address of your house on one paper and you will end up with a blank paper and a paper with your house's address on it. Copy the address to the other paper and you end up with two papers that have your house's address on it. So far this is like the above scenario with numbers, modifying one paper does not modify the other, just as you would expect. The situation becomes different if you give one of your papers with your address on it to your local friendly house demolishers guild, however. Both papers still have your house's address on it, but the difference is that they both now refer to a destroyed house. Acting on the house referred to on one paper can mean changing the same house another paper refers to, sounds reasonable right?


I imagine the situation in this post would be more apparent if it happened to a tangible type, such as a unit. If you had one variable that tracked your unit, then assigned another variable to it called temp_unit, then decided to do some operations on temp_unit and then clean it up by destroying the unit, it would more readily be apparent what is happening. Assigning temp_unit to your other variable did not make a copy of your unit. The unit goes away whether you called for its untimely demise with temp_unit or the other variable. As a matter of fact, most variable types in JASS have this same kind of behavior. There are only a few exceptions (like integer, real, and boolean, which you do not need to clean up).

You basically should never assume something new was introduced that you need to clean up with a Remove/Destroy call, if all you did was assign one variable to another variable. Assigning a unit variable to 10,000 other unit variables still would be just one RemoveUnit call when it comes down to it.
 
Level 13
Joined
Jul 15, 2007
Messages
763
Mmmmm semantics, my favorite.

To my mind (maybe i'm just stupid), if Variable A references an object (such as a unit) and you set Variable B equals Variable A, then it makes sense if you perform actions on Variable B then Variable A gets affected too. But in this case, Player Groups aren't really objects... at least it's not obvious they are. If i have a list of players (variable A) and make a copy of that list (variable B) then it's not obvious at all that editing B will also change A. Unless player/unit groups aren't lists/collections of things but are actual objects, but i don't know that much about the structure of WC3.
 
Every variable in Warcraft III, except integers, reals, strings and booleans, is a handle. A handle is simply a pointer to an object. Thus a player group variable, being a reference to a player group object, will operate with reference semantics. If you destroy an object, all references to that object will now point to "null".

This is the correct way to copy a player group:

  • Actions
    • Custom script: set udg_PlayerGroup = CreateForce()
    • Player Group - Pick every player in OtherPlayerGroup and do (Actions)
      • Loop - Actions
        • Player Group - Add (Picked player) to PlayerGroup
    • -------- DO STUFF --------
    • Custom script: call DestroyForce(udg_PlayerGroup)
Of course, in the OP's trigger, there is no reason to create a temporary group and then delete it, since he is not modifying the groups at all. He can just use a "Pick every player in ..." function, there are no leaks since the player groups are still being referenced.
 
Status
Not open for further replies.
Top