Merge branch 'master' into terencehill/ca_fixes
authorterencehill <piuntn@gmail.com>
Sun, 19 Oct 2014 16:14:04 +0000 (18:14 +0200)
committerterencehill <piuntn@gmail.com>
Sun, 19 Oct 2014 16:14:04 +0000 (18:14 +0200)
gamemodes.cfg
qcsrc/common/mapinfo.qh
qcsrc/common/notifications.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/mutators/gamemode_ca.qc
qcsrc/server/mutators/gamemode_freezetag.qc
qcsrc/server/mutators/gamemode_invasion.qc

index dfc2405..c5cfa05 100644 (file)
@@ -203,10 +203,10 @@ set g_assault 0 "Assault: attack the enemy base as fast as you can, then defend
 // ============
 //  clan arena
 // ============
-set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round."
-set g_ca_point_limit 10 "point limit 10 is standard for clan arena"
-set g_ca_point_leadlimit 0
-set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games."
+set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round"
+seta g_ca_point_limit -1 "Clan Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_ca_point_leadlimit -1 "Clan Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games"
 set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
 set g_ca_damage2score_multiplier 0.01
 set g_ca_round_timelimit 180 "round time limit in seconds"
index 3c0afec..e381513 100644 (file)
@@ -51,7 +51,7 @@ REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 p
 REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0");
 #define g_ctf IS_GAMETYPE(CTF)
 
-REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0");
+REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 teams=2 leadlimit=0");
 #define g_ca IS_GAMETYPE(CA)
 
 REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0");
index 117965f..d05c6ba 100644 (file)
@@ -429,6 +429,8 @@ void Send_Notification_WOCOVA(
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET,      2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_void",          _("^BG%s^K1 was in the wrong place%s%s"), "") \
        MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4,             3, 1, "s1 s2 s3loc spree_end", "s2 s1",  "notify_teamkill_%s",   _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE,                   0, 0, "", "",                            "",                     _("^F1Round already started, you will join the game in the next round"), "") \
+       MSG_INFO_NOTIF(1, INFO_CA_LEAVE,                       0, 0, "", "",                            "",                     _("^F2You will spectate in the next round"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K1 was frozen by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_FALL,         1, 0, "s1", "",                          "",                     _("^BG%s^K3 was revived by falling"), "") \
@@ -545,6 +547,7 @@ void Send_Notification_WOCOVA(
        MULTITEAM_CENTER##teams(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle)
 
 #define MSG_CENTER_NOTIFICATIONS \
+       MSG_CENTER_NOTIF(1, CENTER_ALONE,                       0, 0, "",             NO_CPID,             "0 0", _("^F4You are now alone!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ASSAULT_ATTACKING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are attacking!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ASSAULT_DEFENDING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are defending!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN,             0, 0, "",             CPID_ROUND,          "2 0", _("^F4Begin!"), "") \
@@ -645,8 +648,8 @@ void Send_Notification_WOCOVA(
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART,          0, 1, "",              CPID_KEYHUNT_OTHER,    "1 f1", _("^F4Round will start in ^COUNT"), "") \
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN,                0, 1, "",              CPID_KEYHUNT_OTHER,    "f1 0", _("^BGScanning frequency range..."), "") \
        MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4,           0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 4, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 4, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 1, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 1, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS,             0, 1, "f1",            CPID_MISSING_PLAYERS,  "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
        MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO,             0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO_FIRST,       0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
@@ -976,7 +979,7 @@ string arg_slot[NOTIF_MAX_ARGS];
     ARG_CASE(ARG_CS_SV_HA,  "f3race_time",   mmssss(f3)) \
     ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
     ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
-    ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
+    ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1)) \
     ARG_CASE(ARG_CS,        "pass_key",      ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
     ARG_CASE(ARG_CS,        "frag_ping",     notif_arg_frag_ping(TRUE, f2)) \
     ARG_CASE(ARG_CS,        "frag_stats",    notif_arg_frag_stats(f2, f3, f4)) \
@@ -1020,26 +1023,26 @@ string notif_arg_frag_stats(float fhealth, float farmor, float fping)
                return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(FALSE, fping));
 }
 
-string notif_arg_missing_teams(float f1, float f2, float f3, float f4)
+string notif_arg_missing_teams(float f1)
 {
        return sprintf("%s%s%s%s",
-               (f1 ?
-                       sprintf("%s%s", Team_ColoredFullName(f1 - 1), ((f2 + f3 + f4) ? ", " : ""))
+               ((f1 & 1) ?
+                       sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_1), ((f1 & (2 + 4 + 8)) ? ", " : ""))
                        :
                        ""
                ),
-               (f2 ?
-                       sprintf("%s%s", Team_ColoredFullName(f2 - 1), ((f3 + f4) ? ", " : ""))
+               ((f1 & 2) ?
+                       sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_2), ((f1 & (4 + 8)) ? ", " : ""))
                        :
                        ""
                ),
-               (f3 ?
-                       sprintf("%s%s", Team_ColoredFullName(f3 - 1), (f4 ? ", " : ""))
+               ((f1 & 4) ?
+                       sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_3), ((f1 & 8) ? ", " : ""))
                        :
                        ""
                ),
-               (f4 ?
-                       Team_ColoredFullName(f4 - 1)
+               ((f1 & 8) ?
+                       Team_ColoredFullName(NUM_TEAM_4)
                        :
                        ""
                )
index 4ab1836..7185182 100644 (file)
@@ -265,6 +265,7 @@ void PutObserverInServer (void)
        self.punchvector = '0 0 0';
        self.oldvelocity = self.velocity;
        self.fire_endtime = -1;
+       self.event_damage = func_null;
 }
 
 .float model_randomizer;
@@ -786,8 +787,8 @@ void ClientKill_Now()
        if(self.killindicator_teamchange)
                ClientKill_Now_TeamChange();
 
-       // in any case:
-       Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
+       if(IS_PLAYER(self))
+               Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
 
        // now I am sure the player IS dead
 }
@@ -1991,7 +1992,7 @@ float nJoinAllowed(entity ignore) {
 
        float currentlyPlaying = 0;
        FOR_EACH_REALCLIENT(e)
-               if(IS_PLAYER(e) || e.caplayer == 1)
+               if(IS_PLAYER(e) || e.caplayer)
                        currentlyPlaying += 1;
 
        if(currentlyPlaying < autocvar_g_maxplayers)
@@ -2005,7 +2006,10 @@ float nJoinAllowed(entity ignore) {
  * g_maxplayers_spectator_blocktime seconds
  */
 void checkSpectatorBlock() {
-       if(IS_SPEC(self) || IS_OBSERVER(self)) {
+       if(IS_SPEC(self) || IS_OBSERVER(self))
+       if(!self.caplayer)
+       if(IS_REAL_CLIENT(self))
+       {
                if( time > (self.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
                        Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
                        dropclient(self);
index 0fe71f1..8c61d6f 100644 (file)
@@ -336,6 +336,9 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float
 
 void calculate_player_respawn_time()
 {
+       if(g_ca)
+               return;
+
        float gametype_setting_tmp;
        float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
        float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
@@ -708,6 +711,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                self.flags &= ~FL_ONGROUND;
                // dying animation
                self.deadflag = DEAD_DYING;
+
                // when to allow respawn
                calculate_player_respawn_time();
 
index 7842448..cb6884b 100644 (file)
@@ -152,6 +152,8 @@ void ClientCommand_join(float request)
                        {
                                if(!IS_PLAYER(self) && !lockteams)
                                {
+                                       if(self.caplayer)
+                                               return;
                                        if(nJoinAllowed(self))
                                        {
                                                if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
@@ -580,15 +582,11 @@ void ClientCommand_spectate(float request)
                                        }
                                }
 
-                               if(IS_PLAYER(self) && autocvar_sv_spectate == 1)
-                                       ClientKill_TeamChange(-2); // observe
-
-                               // in CA, allow a dead player to move to spectators (without that, caplayer!=0 will be moved back to the player list)
-                               // note: if arena game mode is ever done properly, this needs to be removed.
-                               if(self.caplayer && (IS_SPEC(self) || IS_OBSERVER(self)))
+                               if((IS_PLAYER(self) || self.caplayer) && autocvar_sv_spectate == 1)
                                {
-                                       sprint(self, "WARNING: you will spectate in the next round.\n");
-                                       self.caplayer = 0;
+                                       if(self.caplayer && (IS_SPEC(self) || IS_OBSERVER(self)))
+                                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CA_LEAVE);
+                                       ClientKill_TeamChange(-2); // observe
                                }
                        }
                        return; // never fall through to usage
index c0da029..edeb571 100644 (file)
@@ -213,6 +213,8 @@ void GameCommand_allspec(float request, float argc)
                        FOR_EACH_REALPLAYER(client)
                        {
                                self = client;
+                               if(self.caplayer)
+                                       self.caplayer = 0;
                                PutObserverInServer();
                                ++i;
                        }
@@ -1045,6 +1047,8 @@ void GameCommand_moveplayer(float request, float argc)
                                                if(!IS_SPEC(client) && !IS_OBSERVER(client))
                                                {
                                                        self = client;
+                                                       if(self.caplayer)
+                                                               self.caplayer = 0;
                                                        PutObserverInServer();
 
                                                        successful = strcat(successful, (successful ? ", " : ""), client.netname);
@@ -1150,9 +1154,10 @@ void GameCommand_nospectators(float request)
                {
                        blockSpectators = 1;
                        entity plr;
-                       FOR_EACH_CLIENT(plr) //give every spectator <g_maxplayers_spectator_blocktime> seconds time to become a player
+                       FOR_EACH_REALCLIENT(plr) //give every spectator <g_maxplayers_spectator_blocktime> seconds time to become a player
                        {
                                if(IS_SPEC(plr) || IS_OBSERVER(plr))
+                               if(!plr.caplayer)
                                {
                                        plr.spectatortime = time;
                                        Send_Notification(NOTIF_ONE_ONLY, plr, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
index 8a6315c..76419d8 100644 (file)
@@ -106,29 +106,32 @@ void CA_RoundStart()
                allowed_to_spawn = FALSE;
 }
 
-float prev_total_players;
+float prev_missing_teams_mask;
 float CA_CheckTeams()
 {
        allowed_to_spawn = TRUE;
        CA_count_alive_players();
        if(CA_ALIVE_TEAMS_OK())
        {
-               if(prev_total_players > 0)
+               if(prev_missing_teams_mask > 0)
                        Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
-               prev_total_players = -1;
+               prev_missing_teams_mask = -1;
                return 1;
        }
-       if(prev_total_players != total_players)
+       if(total_players == 0)
        {
-               float p1 = 0, p2 = 0, p3 = 0, p4 = 0;
-               if(!redalive) p1 = NUM_TEAM_1;
-               if(!bluealive) p2 = NUM_TEAM_2;
-               if(ca_teams >= 3)
-               if(!yellowalive) p3 = NUM_TEAM_3;
-               if(ca_teams >= 4)
-               if(!pinkalive) p4 = NUM_TEAM_4;
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4);
-               prev_total_players = total_players;
+               if(prev_missing_teams_mask > 0)
+                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+               prev_missing_teams_mask = -1;
+               return 0;
+       }
+       float missing_teams_mask = (!redalive) + (!bluealive) * 2;
+       if(ca_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
+       if(ca_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+       if(prev_missing_teams_mask != missing_teams_mask)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
+               prev_missing_teams_mask = missing_teams_mask;
        }
        return 0;
 }
@@ -142,6 +145,7 @@ MUTATOR_HOOKFUNCTION(ca_PlayerSpawn)
 MUTATOR_HOOKFUNCTION(ca_PutClientInServer)
 {
        if(!allowed_to_spawn)
+       if(IS_PLAYER(self)) // this is true even when player is trying to join
        {
                self.classname = "observer";
                if(self.jointime != time) //not when connecting
@@ -149,7 +153,7 @@ MUTATOR_HOOKFUNCTION(ca_PutClientInServer)
                {
                        self.caplayer = 0.5;
                        if(IS_REAL_CLIENT(self))
-                               sprint(self, "You will join the game in the next round.\n");
+                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CA_JOIN_LATE);
                }
        }
        return 1;
@@ -160,6 +164,11 @@ MUTATOR_HOOKFUNCTION(ca_reset_map_players)
        FOR_EACH_CLIENT(self)
        {
                self.killcount = 0;
+               if(!self.caplayer && IS_BOT_CLIENT(self))
+               {
+                       self.team = -1;
+                       self.caplayer = 1;
+               }
                if(self.caplayer)
                {
                        self.classname = "player";
@@ -188,13 +197,48 @@ MUTATOR_HOOKFUNCTION(ca_GetTeamCount)
        return 0;
 }
 
+entity ca_LastPlayerForTeam()
+{
+       entity pl, last_pl = world;
+       FOR_EACH_PLAYER(pl)
+       {
+               if(pl.health >= 1)
+               if(pl != self)
+               if(pl.team == self.team)
+               if(!last_pl)
+                       last_pl = pl;
+               else
+                       return world;
+       }
+       return last_pl;
+}
+
+void ca_LastPlayerForTeam_Notify()
+{
+       if(round_handler_IsActive())
+       if(round_handler_IsRoundStarted())
+       {
+               entity pl = ca_LastPlayerForTeam();
+               if(pl)
+                       Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
+       }
+}
+
 MUTATOR_HOOKFUNCTION(ca_PlayerDies)
 {
+       ca_LastPlayerForTeam_Notify();
        if(!allowed_to_spawn)
                self.respawn_flags =  RESPAWN_SILENT;
        return 1;
 }
 
+MUTATOR_HOOKFUNCTION(ca_ClientDisconnect)
+{
+       if(self.caplayer == 1)
+               ca_LastPlayerForTeam_Notify();
+       return 1;
+}
+
 MUTATOR_HOOKFUNCTION(ca_ForbidPlayerScore_Clear)
 {
        return 1;
@@ -202,6 +246,8 @@ MUTATOR_HOOKFUNCTION(ca_ForbidPlayerScore_Clear)
 
 MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver)
 {
+       if(self.caplayer == 1)
+               ca_LastPlayerForTeam_Notify();
        if(self.killindicator_teamchange == -2)
                self.caplayer = 0;
        if(self.caplayer)
@@ -304,6 +350,7 @@ MUTATOR_DEFINITION(gamemode_ca)
        MUTATOR_HOOK(reset_map_players, ca_reset_map_players, CBC_ORDER_ANY);
        MUTATOR_HOOK(GetTeamCount, ca_GetTeamCount, CBC_ORDER_EXCLUSIVE);
        MUTATOR_HOOK(PlayerDies, ca_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientDisconnect, ca_ClientDisconnect, CBC_ORDER_ANY);
        MUTATOR_HOOK(ForbidPlayerScore_Clear, ca_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
        MUTATOR_HOOK(ForbidThrowCurrentWeapon, ca_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
        MUTATOR_HOOK(GiveFragsForKill, ca_GiveFragsForKill, CBC_ORDER_FIRST);
index 26e1991..5280e55 100644 (file)
@@ -42,27 +42,30 @@ void freezetag_count_alive_players()
 #define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
 #define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
 
-float prev_total_players;
+float prev_missing_teams_mask;
 float freezetag_CheckTeams()
 {
        if(FREEZETAG_ALIVE_TEAMS_OK())
        {
-               if(prev_total_players > 0)
+               if(prev_missing_teams_mask > 0)
                        Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
-               prev_total_players = -1;
+               prev_missing_teams_mask = -1;
                return 1;
        }
-       if(prev_total_players != total_players)
+       if(total_players == 0)
        {
-               float p1 = 0, p2 = 0, p3 = 0, p4 = 0;
-               if(!redalive) p1 = NUM_TEAM_1;
-               if(!bluealive) p2 = NUM_TEAM_2;
-               if(freezetag_teams >= 3)
-               if(!yellowalive) p3 = NUM_TEAM_3;
-               if(freezetag_teams >= 4)
-               if(!pinkalive) p4 = NUM_TEAM_4;
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4);
-               prev_total_players = total_players;
+               if(prev_missing_teams_mask > 0)
+                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+               prev_missing_teams_mask = -1;
+               return 0;
+       }
+       float missing_teams_mask = (!redalive) + (!bluealive) * 2;
+       if(freezetag_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
+       if(freezetag_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+       if(prev_missing_teams_mask != missing_teams_mask)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
+               prev_missing_teams_mask = missing_teams_mask;
        }
        return 0;
 }
@@ -134,6 +137,34 @@ float freezetag_CheckWinner()
        return 1;
 }
 
+entity freezetag_LastPlayerForTeam()
+{
+       entity pl, last_pl = world;
+       FOR_EACH_PLAYER(pl)
+       {
+               if(pl.health >= 1)
+               if(!pl.freezetag_frozen)
+               if(pl != self)
+               if(pl.team == self.team)
+               if(!last_pl)
+                       last_pl = pl;
+               else
+                       return world;
+       }
+       return last_pl;
+}
+
+void freezetag_LastPlayerForTeam_Notify()
+{
+       if(round_handler_IsActive())
+       if(round_handler_IsRoundStarted())
+       {
+               entity pl = freezetag_LastPlayerForTeam();
+               if(pl)
+                       Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
+       }
+}
+
 // this is needed to allow the player to turn his view around (fixangle can't
 // be used to freeze his view, as that also changes the angles), while not
 // turning that ice object with the player
@@ -322,6 +353,8 @@ void havocbot_role_ft_freeing()
 MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
 {
        self.health = 0; // neccessary to update correctly alive stats
+       if(!self.freezetag_frozen)
+               freezetag_LastPlayerForTeam_Notify();
        freezetag_Unfreeze(world);
        freezetag_count_alive_players();
        return 1;
@@ -348,6 +381,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
                {
                        freezetag_Add_Score(frag_attacker);
                        freezetag_count_alive_players();
+                       freezetag_LastPlayerForTeam_Notify();
                }
                else
                        freezetag_Unfreeze(world); // remove ice
@@ -359,6 +393,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
                return 1;
 
        freezetag_Freeze(frag_attacker);
+       freezetag_LastPlayerForTeam_Notify();
 
        if(frag_attacker == frag_target || frag_attacker == world)
        {
index 8448a21..364cb7f 100644 (file)
@@ -416,8 +416,6 @@ void invasion_DelayedInit() // Do this check with a delay so we can wait for tea
        round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
        round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
 
-       allowed_to_spawn = TRUE;
-
        inv_roundcnt = 0;
        inv_maxrounds = 15; // 15?
 }