• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Victory/Defeat conditions without buildings

I'm struggling to accomplish this. I'm working on a little project where each player has 1 hero, and no buildings. I want the standard defeat message and dialog to appear when that hero dies, and the standard victory dialog when all enemy players/teams have been defeated. I've tried several things but it doesn't seem to work the way I want to.

Any pointers? Thanks!
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,884
Use a Unit array variable to track each Player's hero:
  • Events
    • Unit - A Hero comes into play // replace with an actual Event that makes sense
  • Conditions
  • Actions
    • Set Variable PN = (Player number of (Owner of (Triggering unit))
    • Set Variable Player_Hero[PN] = (Triggering unit)
Use Player Group variables to track each active User as well as their Hero status (Alive/Dead):
  • Events
    • Time - Elapsed game time is 0.01 seconds
  • Conditions
  • Actions
    • Set Variable Player_Group_Playing = (All players)
    • Player Group - Pick every player in Player_Group_Playing and do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • ((Picked player) controller) Equal to User
            • ((Picked player) slot status) Equal to Is playing
          • Then - Actions
            • -------- (Picked player) is indeed an active user --------
            • Player Group - Add (Picked player) to Player_Group_Alive
          • Else - Actions
            • Player Group - Remove (Picked player) from Player_Group_Playing
Detect when a player's Hero dies and manage the Player Groups to see if the game is over:
  • Events
    • Unit - A unit Dies
  • Conditions
    • ((Triggering unit) is a Hero) Equal to True
    • ((Triggering unit) Equal to Player_Hero[(Player number of (Triggering player)]) Equal to True
  • Actions
    • Player Group - Remove (Triggering player) from Player_Group_Alive
    • Player Group - Add (Triggering player) to Player_Group_Dead
    • -------- --------
    • -------- Check to see if there is a winning player --------
    • Set Variable Players_Alive = (Number of players in Player_Group_Alive)
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • Players_Alive Greater than 1
      • Then - Actions
        • Skip remaining actions
      • Else - Actions
    • -------- --------
    • -------- If we made it this far then someone has won the game! --------
    • Set Variable Player_Winner = (Random player from Player_Group_Alive)
    • Game - Victory Player_Winner (Show dialogs, Show scores)
    • Player Group - Pick every player in Player_Group_Dead and do (Actions)
      • Loop - Actions
        • Game - Defeat (Picked player) with the message: Defeat!
Note that this current implementation only works for a FFA style match. But when working with Teams you can still use most of this logic, you just need to modify the "A unit Dies" trigger to check for how many Teams are alive instead of checking for how many Players are alive. This can be achieved by using more Player Group variables (Arrays come to mind to track the different teams) as well some Pick Every Player loops to check the status of each Player inside of each Team.

You can also use Unit Group variables to track a team's Heroes, this helps with the whole process:
  • Events
    • Unit - A unit Dies
  • Conditions
    • ((Triggering unit) is a Hero) Equal to True
    • ((Triggering unit) Equal to Player_Hero[(Player number of (Triggering player)]) Equal to True
  • Actions
    • Player Group - Remove (Triggering player) from Player_Group_Alive
    • Player Group - Add (Triggering player) to Player_Group_Dead
    • -------- --------
    • Set Variable Team_Number = (Team number of (Triggering player))
    • Set Variable Team_Is_Dead = True
    • Unit Group - Pick every unit in Team_Heroes[Team_Number] and do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Player_Hero[(Player number of (Picked player)] is Alive) Equal to True
          • Then - Actions
            • Set Variable Team_Is_Dead = False
          • Else - Actions
    • -------- --------
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • Team_Is_Dead Equal to False
      • Then - Actions
        • Skip remaining actions
      • Else - Actions
    • -------- --------
    • -------- Subtract the total number of team's remaining and see if we're down to one team left --------
    • Set Variable Teams_Remaining = (Teams_Remaining - 1)
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • Teams_Remaining Greater than 1
      • Then - Actions
        • Skip remaining actions
      • Else - Actions
    • -------- --------
    • Player Group - Pick every player in Player_Group_Alive and do (Actions)
      • Loop - Actions
        • Game - Victory (Picked player) (Show dialogs, Show scores)
    • Player Group - Pick every player in Player_Group_Dead and do (Actions)
      • Loop - Actions
        • Game - Defeat (Picked player) with the message: Defeat!
Teams_Remaining would be set at the start of the game to be equal to the total number of active teams. This could easily be determined in my 2nd trigger. Remember to set the Size of the Unit Group array variable to support the total number of possible teams. Do the same for your Player Group array variable(s) if you end up using them.
 
Last edited:
I did it in vJass, but you definitely put me on the right track!

In my library initializer (indexPlayer is Player(index) inside a loop):
JASS:
                set trg = CreateTrigger()
                call TriggerRegisterPlayerUnitEvent(trg, indexPlayer, EVENT_PLAYER_UNIT_DEATH, null)
                call TriggerAddCondition(trg, function HeroDies)

And then (survivingPlayers is a global force variable):
JASS:
    private function VictorySurvivors takes nothing returns nothing
        call CustomVictoryBJ(GetEnumPlayer(), true, true )
    endfunction
   
    // Hero dies
    private function HeroDies takes nothing returns boolean
        local player owningPlayer = GetOwningPlayer(GetDyingUnit())
        local mapcontrol playerController = GetPlayerController(owningPlayer)
        local integer index = 0
        local integer team = -1
        local boolean differentTeams = false
       
        if playerController == MAP_CONTROL_USER or playerController == MAP_CONTROL_COMPUTER then
            if AllowVictoryDefeat( PLAYER_GAME_RESULT_DEFEAT ) then
                call RemovePlayer( owningPlayer, PLAYER_GAME_RESULT_DEFEAT )
                call DisplayTimedTextFromPlayer(owningPlayer, 0, 0, 60, GetLocalizedString( "PLAYER_DEFEATED" ) )

                // UI only needs to be displayed to users.
                if (GetPlayerController(owningPlayer) == MAP_CONTROL_USER) then
                    call CustomDefeatDialogBJ( owningPlayer, "Defeat!" )
                endif
               
                call ForceRemovePlayer(survivingPlayers, owningPlayer)
               
                // Check surviving players
                loop
                    if IsPlayerInForce(Player(index), survivingPlayers) then
                        if team == -1 then
                            set team = GetPlayerTeam(Player(index))
                        else
                            set differentTeams = GetPlayerTeam(Player(index)) != team
                        endif
                    endif
               
                    set index = index + 1
                    exitwhen differentTeams or index == bj_MAX_PLAYER_SLOTS
                endloop
               
                if not differentTeams then
                    call ForForce(survivingPlayers, function VictorySurvivors)
                endif
            endif
        endif
       
        set owningPlayer = null
        set playerController = null
       
        return false
    endfunction

Haven't tested extensively, but it appears to be working. Thanks again!

Edit: well I definitely need to check that the dying unit is a hero, I didn't account for summoned units.
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,884
I've recently heard that these contol checks can cause desyncs and should be avoided, unsure if it's true or not:
vJASS:
playerController == MAP_CONTROL_USER or playerController == MAP_CONTROL_COMPUTER
I heard in the past that these were unsafe when used in Map Initialization and very early on when Players were still loading in - which made sense, but recently I heard that these can even cause problems beyond that point in time. Again, I cannot confirm this but it's an easy enough thing to avoid.

Anyway, shouldn't you just remove any non-User, non-Computer units from the game? It seems like these checks are unnecessary, although maybe your map requires them. I know that in most maps there won't ever be units belonging to "nobody" in the first place, they're either removed from the start, never created, and/or removed after a Player leaves the game.
 
I heard in the past that these were unsafe when used in Map Initialization and very early on when Players were still loading in - which made sense, but recently I heard that these can even cause problems beyond that point in time. Again, I cannot confirm this but it's an easy enough thing to avoid.
MAP_CONTROL_COMPUTER at least is used in the standard Melee Initialization triggers, so it would be surprise me if that one is a problem. In my early tests (before this thread), I would get defeated messages from neutral hostile units dying, that's why I added this, but I'll do some testing if I really need them.

Anyway, shouldn't you just remove any non-User, non-Computer units from the game? It seems like these checks are unnecessary, although maybe your map requires them. I know that in most maps there won't ever be units belonging to "nobody" in the first place, they're either removed from the start, never created, and/or removed after a Player leaves the game.
There are neutral hostile units on the map which are created through triggers. I was just looking through the standard triggers, I think I'm using the wrong constant in player loops, I should be using bj_MAX_PLAYERS instead of bj_MAX_PLAYER_SLOTS. Fixing that might eliminate the need for these checks.
Edit: yeah that fixed it lol, oops.
 
Last edited:
Top