]> 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 116eecbbf6b773613f7b299568df3ee31a5817d6..7e068e954687e855d3b54fea4c3d0869feb8de32 100644 (file)
@@ -1,25 +1,98 @@
+float freezetag_CheckTeams();
+float freezetag_CheckWinner();
 void freezetag_Initialize()
 {
        precache_model("models/ice/ice.md3");
-       warmup = time + autocvar_g_start_delay + 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);
+       addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
+
+       addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
+       addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
 }
 
-void freezetag_CheckWinner()
+void freezetag_count_alive_players()
 {
-       if(time <= game_starttime) // game didn't even start yet! nobody can win in that case.
-               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;
@@ -36,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
@@ -63,6 +128,24 @@ void freezetag_Ice_Think()
        self.nextthink = time;
 }
 
+void freezetag_Add_Score(entity attacker)
+{
+       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
+}
+
 void freezetag_Freeze(entity attacker)
 {
        if(self.freezetag_frozen)
@@ -70,6 +153,10 @@ 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;
+
+       freezetag_count_alive_players();
 
        entity ice;
        ice = spawn();
@@ -91,30 +178,14 @@ 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)
 {
        self.freezetag_frozen = 0;
+       self.freezetag_frozen_timeout = 0;
        self.freezetag_revive_progress = 0;
-       self.health = autocvar_g_balance_health_start;
 
        // remove the ice block
        entity ice;
@@ -240,44 +311,45 @@ void havocbot_role_ft_freeing()
 
 MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
 {
-       if(self.freezetag_frozen == 0 && self.health >= 1)
-       {
-               if(self.team == COLOR_TEAM1)
-                       --redalive;
-               else if(self.team == COLOR_TEAM2)
-                       --bluealive;
-               else if(self.team == COLOR_TEAM3)
-                       --yellowalive;
-               else if(self.team == COLOR_TEAM4)
-                       --pinkalive;
-               --totalalive;
-       }
-
-       if(total_players > 2) // 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();
-
+       self.health = 0; // neccessary to update correctly alive stats
        freezetag_Unfreeze(world);
-
+       freezetag_count_alive_players();
        return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 {
-       if(self.freezetag_frozen == 0)
+       if(round_handler_IsActive())
+       if(round_handler_CountdownRunning())
        {
-               if(self.team == COLOR_TEAM1)
-                       --redalive;
-               else if(self.team == COLOR_TEAM2)
-                       --bluealive;
-               else if(self.team == COLOR_TEAM3)
-                       --yellowalive;
-               else if(self.team == COLOR_TEAM4)
-                       --pinkalive;
-               --totalalive;
+               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
+       }
 
-               freezetag_Freeze(frag_attacker);
+       // 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_Add_Score(frag_attacker);
+                       freezetag_count_alive_players();
+               }
+               else
+                       freezetag_Unfreeze(world); // remove ice
+               self.freezetag_frozen_timeout = -2; // freeze on respawn
+               return 1;
        }
 
+       if(self.freezetag_frozen)
+               return 1;
+
+       freezetag_Freeze(frag_attacker);
+
        if(frag_attacker == frag_target || frag_attacker == world)
        {
                if(frag_target.classname == STR_PLAYER)
@@ -295,30 +367,46 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 
        frag_target.health = 1; // "respawn" the player :P
 
-       freezetag_CheckWinner();
-
        return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
 {
-       freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed
+       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 == 1 && time > game_starttime) // 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, "^1You spawned after the round started, you'll spawn as frozen.\n");
+               centerprint(self, "^1Round already started, you spawn as frozen.");
                freezetag_Freeze(world);
        }
 
        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;
+}
+
 MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill)
 {
        frag_score = 0; // no frags counted in Freeze Tag
@@ -330,6 +418,27 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
        float n;
        vector revive_extra_size;
 
+       if(gameover)
+               return 1;
+
+       if(self.freezetag_frozen)
+       {
+               // keep health = 1
+               self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
+
+               if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+               {
+                       self.health = autocvar_g_balance_health_start;
+                       freezetag_Unfreeze(world);
+                       freezetag_count_alive_players();
+                       return 1;
+               }
+       }
+
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+               return 1;
+
        revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
 
        entity o;
@@ -359,6 +468,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
                if(self.freezetag_revive_progress >= 1)
                {
                        freezetag_Unfreeze(self);
+                       freezetag_count_alive_players();
 
                        // EVERY team mate nearby gets a point (even if multiple!)
                        FOR_EACH_PLAYER(other) if(self != other)
@@ -425,15 +535,12 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
 {
-    if(g_freezetag)
-    {
-        if(frag_target.freezetag_frozen == 1 && frag_deathtype != DEATH_HURTTRIGGER)
-        {
-            frag_damage = 0;
-            frag_force = frag_force * autocvar_g_freezetag_frozen_force;
-        }
-    }
-    return 1;
+       if(frag_target.freezetag_frozen == 1 && frag_deathtype != DEATH_HURTTRIGGER)
+       {
+               frag_damage = 0;
+               frag_force = frag_force * autocvar_g_freezetag_frozen_force;
+       }
+       return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
@@ -452,22 +559,31 @@ MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
                else
                        self.havocbot_role = havocbot_role_ft_offense;
        }
-       
+
        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_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
        {