]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_freezetag.qc
Merge branch 'master' into terencehill/ca_arena_freezetag_bugfixes
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_freezetag.qc
index ca453c5c0145fe3023927620dda8221835884733..7e068e954687e855d3b54fea4c3d0869feb8de32 100644 (file)
@@ -1,9 +1,12 @@
+float freezetag_CheckTeams();
+float freezetag_CheckWinner();
 void freezetag_Initialize()
 {
        precache_model("models/ice/ice.md3");
-       warmup = max(time, game_starttime) + autocvar_g_freezetag_warmup;
        ScoreRules_freezetag();
 
+       round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, 5, autocvar_g_freezetag_warmup);
+
        addstat(STAT_REDALIVE, AS_INT, redalive_stat);
        addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
        addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
@@ -13,21 +16,83 @@ void freezetag_Initialize()
        addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
 }
 
-void freezetag_CheckWinner()
+void freezetag_count_alive_players()
 {
-       if(time <= game_starttime || inWarmupStage)
-               return;
-
-       if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup))
-               return; // already waiting for next round to start
+       entity e;
+       total_players = redalive = bluealive = yellowalive = pinkalive = 0;
+       FOR_EACH_PLAYER(e) {
+               if(e.team == COLOR_TEAM1 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++redalive;
+               }
+               else if(e.team == COLOR_TEAM2 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++bluealive;
+               }
+               else if(e.team == COLOR_TEAM3 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++yellowalive;
+               }
+               else if(e.team == COLOR_TEAM4 && e.health >= 1)
+               {
+                       ++total_players;
+                       if (!e.freezetag_frozen) ++pinkalive;
+               }
+       }
+       FOR_EACH_REALCLIENT(e) {
+               e.redalive_stat = redalive;
+               e.bluealive_stat = bluealive;
+               e.yellowalive_stat = yellowalive;
+               e.pinkalive_stat = pinkalive;
+       }
+}
 
+float freezetag_TeamsCanPlay()
+{
        if((redalive >= 1 && bluealive >= 1)
                || (redalive >= 1 && yellowalive >= 1)
                || (redalive >= 1 && pinkalive >= 1)
                || (bluealive >= 1 && yellowalive >= 1)
                || (bluealive >= 1 && pinkalive >= 1)
                || (yellowalive >= 1 && pinkalive >= 1))
-               return; // we still have active players on two or more teams, nobody won yet
+               return 1; // we still have active players on two or more teams, nobody won yet
+       return 0;
+}
+
+float prev_total_players;
+float freezetag_CheckTeams()
+{
+       entity e;
+       if(freezetag_TeamsCanPlay())
+       {
+               if(prev_total_players != -1)
+               {
+                       FOR_EACH_REALCLIENT(e)
+                               Send_CSQC_Centerprint_Generic_Expire(e, CPID_WAITING_PLAYERS);
+               }
+               prev_total_players = -1;
+               return 1;
+       }
+       if(prev_total_players != total_players)
+       {
+               string teams_missing;
+               if(!redalive)   teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM1), ", ");
+               if(!bluealive)  teams_missing = strcat(teams_missing, ColoredTeamName(COLOR_TEAM2), ", ");
+               teams_missing = substring(teams_missing, 0, strlen(teams_missing)-2);
+
+               FOR_EACH_REALCLIENT(e)
+                       Send_CSQC_Centerprint_Generic(e, CPID_WAITING_PLAYERS, strcat("Waiting for players to join...\n\nNeed active players for: ", teams_missing), -1, 0);
+               prev_total_players = total_players;
+       }
+       return 0;
+}
+float freezetag_CheckWinner()
+{
+       if(freezetag_TeamsCanPlay())
+               return 0;
 
        entity e, winner;
        string teamname;
@@ -44,22 +109,14 @@ void freezetag_CheckWinner()
 
        if(winner != world) // just in case a winner wasn't found
        {
-               if(winner.team == COLOR_TEAM1)
-                       teamname = "^1Red Team";
-               else if(winner.team == COLOR_TEAM2)
-                       teamname = "^4Blue Team";
-               else if(winner.team == COLOR_TEAM3)
-                       teamname = "^3Yellow Team";
-               else
-                       teamname = "^6Pink Team";
-               FOR_EACH_PLAYER(e) {
-                       centerprint(e, strcat(teamname, "^5 wins the round, all other teams were frozen.\n"));
-               }
+               teamname = ColoredTeamName(winner.team);
+               FOR_EACH_REALCLIENT(e)
+                       centerprint(e, strcat(teamname, "^5 wins the round, all other teams were frozen."));
                bprint(teamname, "^5 wins the round since all the other teams were frozen.\n");
                TeamScore_AddToTeam(winner.team, ST_SCORE, +1);
        }
 
-       next_round = time + 5;
+       return 1;
 }
 
 // this is needed to allow the player to turn his view around (fixangle can't
@@ -71,38 +128,22 @@ void freezetag_Ice_Think()
        self.nextthink = time;
 }
 
-void freezetag_count_alive_players()
+void freezetag_Add_Score(entity attacker)
 {
-       entity e;
-       total_players = redalive = bluealive = yellowalive = pinkalive = 0;
-       FOR_EACH_PLAYER(e) {
-               if(e.team == COLOR_TEAM1 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++redalive;
-               }
-               else if(e.team == COLOR_TEAM2 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++bluealive;
-               }
-               else if(e.team == COLOR_TEAM3 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++yellowalive;
-               }
-               else if(e.team == COLOR_TEAM4 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++pinkalive;
-               }
+       if(attacker == self)
+       {
+               // you froze your own dumb self
+               // counted as "suicide" already
+               PlayerScore_Add(self, SP_SCORE, -1);
        }
-       FOR_EACH_REALCLIENT(e) {
-               e.redalive_stat = redalive;
-               e.bluealive_stat = bluealive;
-               e.yellowalive_stat = yellowalive;
-               e.pinkalive_stat = pinkalive;
+       else if(attacker.classname == "player")
+       {
+               // got frozen by an enemy
+               // counted as "kill" and "death" already
+               PlayerScore_Add(self, SP_SCORE, -1);
+               PlayerScore_Add(attacker, SP_SCORE, +1);
        }
+       // else nothing - got frozen by the game type rules themselves
 }
 
 void freezetag_Freeze(entity attacker)
@@ -112,8 +153,8 @@ void freezetag_Freeze(entity attacker)
        self.freezetag_frozen = 1;
        self.freezetag_revive_progress = 0;
        self.health = 1;
-       if(inWarmupStage)
-               self.freezetag_frozen_timeout = time + 5;
+       // if(inWarmupStage)
+               // self.freezetag_frozen_timeout = time + 5;
 
        freezetag_count_alive_players();
 
@@ -137,23 +178,7 @@ void freezetag_Freeze(entity attacker)
        // add waypoint
        WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
 
-       if(attacker == self)
-       {
-               // you froze your own dumb self
-               // counted as "suicide" already
-               PlayerScore_Add(self, SP_SCORE, -1);
-       }
-       else if(attacker.classname == "player")
-       {
-               // got frozen by an enemy
-               // counted as "kill" and "death" already
-               PlayerScore_Add(self, SP_SCORE, -1);
-               PlayerScore_Add(attacker, SP_SCORE, +1);
-       }
-       else
-       {
-               // nothing - got frozen by the game type rules themselves
-       }
+       freezetag_Add_Score(attacker);
 }
 
 void freezetag_Unfreeze(entity attacker)
@@ -288,33 +313,35 @@ MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
 {
        self.health = 0; // neccessary to update correctly alive stats
        freezetag_Unfreeze(world);
-
        freezetag_count_alive_players();
-
-       if(total_players > 1) // only check for winners if we had more than two players (one of them left, don't let the other player win just because of that)
-               freezetag_CheckWinner();
-
        return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 {
-       // let the player die in these cases
-       if(time <= game_starttime)
-               return 1;
-       if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup))
-               return 1;
+       if(round_handler_IsActive())
+       if(round_handler_CountdownRunning())
+       {
+               if(self.freezetag_frozen)
+                       freezetag_Unfreeze(world);
+               freezetag_count_alive_players();
+               return 1; // let the player die so that he can respawn whenever he wants
+       }
 
-       if(frag_deathtype == DEATH_HURTTRIGGER)
+       // Cases DEATH_TEAMCHANGE and DEATH_AUTOTEAMCHANGE are needed to fix a bug whe
+       // you succeed changing team through the menu: you both really die (gibbing) and get frozen
+       if(ITEM_DAMAGE_NEEDKILL(frag_deathtype)
+               || frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE)
        {
+               // let the player die, he will be automatically frozen when he respawns
                if(!self.freezetag_frozen)
                {
-                       freezetag_Freeze(world);
-                       freezetag_CheckWinner();
+                       freezetag_Add_Score(frag_attacker);
+                       freezetag_count_alive_players();
                }
-               PutClientInServer(); // respawn the player
-               self.health = 1;
-               self.armorvalue = 0;
+               else
+                       freezetag_Unfreeze(world); // remove ice
+               self.freezetag_frozen_timeout = -2; // freeze on respawn
                return 1;
        }
 
@@ -340,29 +367,24 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 
        frag_target.health = 1; // "respawn" the player :P
 
-       freezetag_CheckWinner();
-
        return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
 {
-       freezetag_count_alive_players();
-
-       if(self.freezetag_frozen) // stay frozen if respawning after death (DEATH_HURTTRIGGER)
-               return 1;
-
-       if(time <= game_starttime || inWarmupStage || total_players == 1)
-               return 1;
+       if(self.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players
+               return 1; // do nothing, round is starting right now
 
-       if(total_players == 2) // only one player active on server, start a new match immediately
-       if(!next_round && warmup && (time < warmup - autocvar_g_freezetag_warmup || time > warmup)) // not awaiting next round
+       if(self.freezetag_frozen_timeout == -2) // player was dead
        {
-               next_round = time;
+               freezetag_Freeze(world);
                return 1;
        }
 
-       if(warmup && time > warmup) // spawn too late, freeze player
+       freezetag_count_alive_players();
+
+       if(round_handler_IsActive())
+       if(round_handler_IsRoundStarted())
        {
                centerprint(self, "^1Round already started, you spawn as frozen.");
                freezetag_Freeze(world);
@@ -371,23 +393,17 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
        return 1;
 }
 
-MUTATOR_HOOKFUNCTION(freezetag_reset_map_global)
-{
-       redalive = bluealive = yellowalive = pinkalive = 0;
-       warmup = max(time, game_starttime);
-       if(autocvar_g_freezetag_warmup > 0)
-               warmup += autocvar_g_freezetag_warmup;
-       return 1;
-}
-
 MUTATOR_HOOKFUNCTION(freezetag_reset_map_players)
 {
        FOR_EACH_PLAYER(self)
        {
                if (self.freezetag_frozen)
                        freezetag_Unfreeze(world);
+               self.freezetag_frozen_timeout = -1;
                PutClientInServer();
+               self.freezetag_frozen_timeout = 0;
        }
+       freezetag_count_alive_players();
        return 1;
 }
 
@@ -410,7 +426,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
                // keep health = 1
                self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
 
-               if(self.freezetag_frozen_timeout && time >= self.freezetag_frozen_timeout)
+               if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
                {
                        self.health = autocvar_g_balance_health_start;
                        freezetag_Unfreeze(world);
@@ -418,8 +434,10 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
                        return 1;
                }
        }
-       if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup))
-               return 1; // already waiting for next round to start
+
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+               return 1;
 
        revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
 
@@ -545,20 +563,27 @@ MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
        return TRUE;
 }
 
+MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
+{
+       self.freezetag_frozen = other.freezetag_frozen;
+       self.freezetag_revive_progress = other.freezetag_revive_progress;
+       return 0;
+}
+
 MUTATOR_DEFINITION(gamemode_freezetag)
 {
        MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(ClientDisconnect, freezetag_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerDies, freezetag_PlayerDies, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, freezetag_PlayerSpawn, CBC_ORDER_ANY);
-       MUTATOR_HOOK(reset_map_global, freezetag_reset_map_global, CBC_ORDER_ANY);
        MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY);
        MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_FIRST); //first, last or any? dunno.
+       MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY);
+       MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {
@@ -567,9 +592,17 @@ MUTATOR_DEFINITION(gamemode_freezetag)
                freezetag_Initialize();
        }
 
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back freezetag_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
        MUTATOR_ONREMOVE
        {
-               error("This is a game type and it cannot be removed at runtime.");
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
        }
 
        return 0;