]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/mutator/gamemode_freezetag.qc
Fix number of teams defaulting to 2 for some game modes (CA, FT and KH)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator / gamemode_freezetag.qc
index bee23d10148ca298da4e52bdab45b95bc0b213c3..cec7d950e9076cdecfc573fdaeafe9be16f409e2 100644 (file)
@@ -1,68 +1,13 @@
 #include "gamemode_freezetag.qh"
-#ifndef GAMEMODE_FREEZETAG_H
-#define GAMEMODE_FREEZETAG_H
-
-int autocvar_g_freezetag_point_limit;
-int autocvar_g_freezetag_point_leadlimit;
-bool autocvar_g_freezetag_team_spawns;
-void freezetag_Initialize();
-
-REGISTER_MUTATOR(ft, false)
-{
-       MUTATOR_ONADD
-       {
-               if (time > 1) // game loads at time 1
-                       error("This is a game type and it cannot be added at runtime.");
-               freezetag_Initialize();
-
-               ActivateTeamplay();
-               SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, -1, -1);
-
-               if (autocvar_g_freezetag_team_spawns)
-                       have_team_spawns = -1; // request team spawns
-       }
-
-       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
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
-       return 0;
-}
-
-.float freezetag_frozen_time;
-.float freezetag_frozen_timeout;
-const float ICE_MAX_ALPHA = 1;
-const float ICE_MIN_ALPHA = 0.1;
-float freezetag_teams;
-
-.float reviving; // temp var
-
-float autocvar_g_freezetag_revive_extra_size;
-float autocvar_g_freezetag_revive_speed;
-bool autocvar_g_freezetag_revive_nade;
-float autocvar_g_freezetag_revive_nade_health;
-
-#endif
-#ifdef IMPLEMENTATION
 
 float autocvar_g_freezetag_frozen_maxtime;
 float autocvar_g_freezetag_revive_clearspeed;
 float autocvar_g_freezetag_round_timelimit;
-int autocvar_g_freezetag_teams;
+//int autocvar_g_freezetag_teams;
 int autocvar_g_freezetag_teams_override;
 float autocvar_g_freezetag_warmup;
 
-const float SP_FREEZETAG_REVIVALS = 4;
-void freezetag_ScoreRules(float teams)
+void freezetag_ScoreRules(int teams)
 {
        ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); // SFL_SORT_PRIO_PRIMARY
        ScoreInfo_SetLabel_PlayerScore(SP_FREEZETAG_REVIVALS, "revivals", 0);
@@ -91,7 +36,7 @@ void freezetag_count_alive_players()
        eliminatedPlayers.SendFlags |= 1;
 }
 #define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
-#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
+#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == NumTeams(freezetag_teams))
 
 float freezetag_CheckTeams()
 {
@@ -99,23 +44,29 @@ float freezetag_CheckTeams()
        if(FREEZETAG_ALIVE_TEAMS_OK())
        {
                if(prev_missing_teams_mask > 0)
-                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER, CPID_MISSING_TEAMS);
+                       Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS);
                prev_missing_teams_mask = -1;
                return 1;
        }
        if(total_players == 0)
        {
                if(prev_missing_teams_mask > 0)
-                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER, CPID_MISSING_TEAMS);
+                       Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, 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;
+       int missing_teams_mask = 0;
+       if(freezetag_teams & BIT(0))
+               missing_teams_mask += (!redalive) * 1;
+       if(freezetag_teams & BIT(1))
+               missing_teams_mask += (!bluealive) * 2;
+       if(freezetag_teams & BIT(2))
+               missing_teams_mask += (!yellowalive) * 4;
+       if(freezetag_teams & BIT(3))
+               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);
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
                prev_missing_teams_mask = missing_teams_mask;
        }
        return 0;
@@ -146,12 +97,15 @@ float freezetag_getWinnerTeam()
        return -1; // no player left
 }
 
+void nades_Clear(entity);
+void nades_GiveBonus(entity player, float score);
+
 float freezetag_CheckWinner()
 {
        if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
        {
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER);
                FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
                        it.freezetag_frozen_timeout = 0;
                        nades_Clear(it);
@@ -166,14 +120,14 @@ float freezetag_CheckWinner()
        int winner_team = freezetag_getWinnerTeam();
        if(winner_team > 0)
        {
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
                TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
        }
        else if(winner_team == -1)
        {
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
        }
 
        FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
@@ -184,71 +138,71 @@ float freezetag_CheckWinner()
        return 1;
 }
 
-entity freezetag_LastPlayerForTeam()
-{SELFPARAM();
-       entity last_pl = world;
-       FOREACH_CLIENT(IS_PLAYER(it) && it != self, LAMBDA(
+entity freezetag_LastPlayerForTeam(entity this)
+{
+       entity last_pl = NULL;
+       FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA(
                if(it.health >= 1)
                if(!STAT(FROZEN, it))
-               if(SAME_TEAM(it, self))
+               if(SAME_TEAM(it, this))
                if(!last_pl)
                        last_pl = it;
                else
-                       return world;
+                       return NULL;
        ));
        return last_pl;
 }
 
-void freezetag_LastPlayerForTeam_Notify()
+void freezetag_LastPlayerForTeam_Notify(entity this)
 {
        if(round_handler_IsActive())
        if(round_handler_IsRoundStarted())
        {
-               entity pl = freezetag_LastPlayerForTeam();
+               entity pl = freezetag_LastPlayerForTeam(this);
                if(pl)
                        Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
        }
 }
 
-void freezetag_Add_Score(entity attacker)
-{SELFPARAM();
-       if(attacker == self)
+void freezetag_Add_Score(entity targ, entity attacker)
+{
+       if(attacker == targ)
        {
-               // you froze your own dumb self
+               // you froze your own dumb targ
                // counted as "suicide" already
-               PlayerScore_Add(self, SP_SCORE, -1);
+               PlayerScore_Add(targ, SP_SCORE, -1);
        }
        else if(IS_PLAYER(attacker))
        {
                // got frozen by an enemy
                // counted as "kill" and "death" already
-               PlayerScore_Add(self, SP_SCORE, -1);
+               PlayerScore_Add(targ, SP_SCORE, -1);
                PlayerScore_Add(attacker, SP_SCORE, +1);
        }
        // else nothing - got frozen by the game type rules themselves
 }
 
-void freezetag_Freeze(entity attacker)
-{SELFPARAM();
-       if(STAT(FROZEN, self))
+void freezetag_Freeze(entity targ, entity attacker)
+{
+       if(STAT(FROZEN, targ))
                return;
 
        if(autocvar_g_freezetag_frozen_maxtime > 0)
-               self.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
+               targ.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
 
-       Freeze(self, 0, 1, true);
+       Freeze(targ, 0, 1, true);
 
        freezetag_count_alive_players();
 
-       freezetag_Add_Score(attacker);
+       freezetag_Add_Score(targ, attacker);
 }
 
-void freezetag_Unfreeze(entity attacker)
-{SELFPARAM();
-       self.freezetag_frozen_time = 0;
-       self.freezetag_frozen_timeout = 0;
+void freezetag_Unfreeze(entity this)
+{
+       this.freezetag_frozen_time = 0;
+       this.freezetag_frozen_timeout = 0;
 
-       Unfreeze(self);
+       Unfreeze(this);
 }
 
 float freezetag_isEliminated(entity e)
@@ -299,7 +253,7 @@ void havocbot_role_ft_offense(entity this)
        // If only one left on team or if role has timed out then start trying to free players.
        if (((unfrozen == 0) && (!STAT(FROZEN, this))) || (time > this.havocbot_role_timeout))
        {
-               LOG_TRACE("changing role to freeing\n");
+               LOG_TRACE("changing role to freeing");
                this.havocbot_role = havocbot_role_ft_freeing;
                this.havocbot_role_timeout = 0;
                return;
@@ -328,7 +282,7 @@ void havocbot_role_ft_freeing(entity this)
 
        if (time > this.havocbot_role_timeout)
        {
-               LOG_TRACE("changing role to offense\n");
+               LOG_TRACE("changing role to offense");
                this.havocbot_role = havocbot_role_ft_offense;
                this.havocbot_role_timeout = 0;
                return;
@@ -352,37 +306,43 @@ void havocbot_role_ft_freeing(entity this)
 // Hook Functions
 // ==============
 
-void ft_RemovePlayer()
-{SELFPARAM();
-       self.health = 0; // neccessary to update correctly alive stats
-       if(!STAT(FROZEN, self))
-               freezetag_LastPlayerForTeam_Notify();
-       freezetag_Unfreeze(world);
+void ft_RemovePlayer(entity this)
+{
+       this.health = 0; // neccessary to update correctly alive stats
+       if(!STAT(FROZEN, this))
+               freezetag_LastPlayerForTeam_Notify(this);
+       freezetag_Unfreeze(this);
        freezetag_count_alive_players();
 }
 
 MUTATOR_HOOKFUNCTION(ft, ClientDisconnect)
 {
-       ft_RemovePlayer();
-       return 1;
+       entity player = M_ARGV(0, entity);
+
+       ft_RemovePlayer(player);
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ft, MakePlayerObserver)
 {
-       ft_RemovePlayer();
-       return false;
+       entity player = M_ARGV(0, entity);
+
+       ft_RemovePlayer(player);
 }
 
 MUTATOR_HOOKFUNCTION(ft, PlayerDies)
 {
-    SELFPARAM();
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_deathtype = M_ARGV(3, float);
+
        if(round_handler_IsActive())
        if(round_handler_CountdownRunning())
        {
                if(STAT(FROZEN, frag_target))
-                       WITH(entity, self, frag_target, freezetag_Unfreeze(world));
+                       freezetag_Unfreeze(frag_target);
                freezetag_count_alive_players();
-               return 1; // let the player die so that he can respawn whenever he wants
+               return true; // let the player die so that he can respawn whenever he wants
        }
 
        // Cases DEATH_TEAMCHANGE and DEATH_AUTOTEAMCHANGE are needed to fix a bug whe
@@ -393,50 +353,48 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies)
                // let the player die, he will be automatically frozen when he respawns
                if(STAT(FROZEN, frag_target) != 1)
                {
-                       freezetag_Add_Score(frag_attacker);
+                       freezetag_Add_Score(frag_target, frag_attacker);
                        freezetag_count_alive_players();
-                       freezetag_LastPlayerForTeam_Notify();
+                       freezetag_LastPlayerForTeam_Notify(frag_target);
                }
                else
-                       WITH(entity, self, frag_target, freezetag_Unfreeze(world)); // remove ice
+                       freezetag_Unfreeze(frag_target); // remove ice
                frag_target.health = 0; // Unfreeze resets health
                frag_target.freezetag_frozen_timeout = -2; // freeze on respawn
-               return 1;
+               return true;
        }
 
        if(STAT(FROZEN, frag_target))
-               return 1;
+               return true;
 
-       WITH(entity, self, frag_target, freezetag_Freeze(frag_attacker));
-       freezetag_LastPlayerForTeam_Notify();
+       freezetag_Freeze(frag_target, frag_attacker);
+       freezetag_LastPlayerForTeam_Notify(frag_target);
 
-       if(frag_attacker == frag_target || frag_attacker == world)
+       if(frag_attacker == frag_target || frag_attacker == NULL)
        {
                if(IS_PLAYER(frag_target))
                        Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_SELF);
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_SELF, frag_target.netname);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_SELF, frag_target.netname);
        }
        else
        {
-               if(IS_PLAYER(frag_target))
-                       Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_FROZEN, frag_attacker.netname);
-               if(IS_PLAYER(frag_attacker))
-                       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_FREEZETAG_FREEZE, frag_target.netname);
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname);
        }
 
-       return 1;
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ft, PlayerSpawn)
-{SELFPARAM();
-       if(self.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players
-               return 1; // do nothing, round is starting right now
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players
+               return true; // do nothing, round is starting right now
 
-       if(self.freezetag_frozen_timeout == -2) // player was dead
+       if(player.freezetag_frozen_timeout == -2) // player was dead
        {
-               freezetag_Freeze(world);
-               return 1;
+               freezetag_Freeze(player, NULL);
+               return true;
        }
 
        freezetag_count_alive_players();
@@ -444,11 +402,11 @@ MUTATOR_HOOKFUNCTION(ft, PlayerSpawn)
        if(round_handler_IsActive())
        if(round_handler_IsRoundStarted())
        {
-               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_SPAWN_LATE);
-               freezetag_Freeze(world);
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_SPAWN_LATE);
+               freezetag_Freeze(player, NULL);
        }
 
-       return 1;
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ft, reset_map_players)
@@ -456,58 +414,50 @@ MUTATOR_HOOKFUNCTION(ft, reset_map_players)
        FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
                it.killcount = 0;
                it.freezetag_frozen_timeout = -1;
-               setself(it);
-               PutClientInServer();
+               PutClientInServer(it);
                it.freezetag_frozen_timeout = 0;
        ));
        freezetag_count_alive_players();
-       return 1;
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ft, GiveFragsForKill, CBC_ORDER_FIRST)
 {
-       frag_score = 0; // no frags counted in Freeze Tag
-       return 1;
+       M_ARGV(2, float) = 0; // no frags counted in Freeze Tag
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
-{SELFPARAM();
+{
        if(gameover)
-               return 1;
-
-       if(STAT(FROZEN, self) == 1)
-       {
-               // keep health = 1
-               self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
-       }
+               return true;
 
        if(round_handler_IsActive())
        if(!round_handler_IsRoundStarted())
-               return 1;
+               return true;
 
        int n;
+       entity o = NULL;
+       entity player = M_ARGV(0, entity);
+       //if(STAT(FROZEN, player))
+       //if(player.freezetag_frozen_timeout > 0 && time < player.freezetag_frozen_timeout)
+               //player.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (player.freezetag_frozen_timeout - time) / (player.freezetag_frozen_timeout - player.freezetag_frozen_time);
 
-       entity o;
-       o = world;
-       //if(STAT(FROZEN, self))
-       //if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
-               //self.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
-
-       if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+       if(player.freezetag_frozen_timeout > 0 && time >= player.freezetag_frozen_timeout)
                n = -1;
        else
        {
                vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
                n = 0;
-               FOREACH_CLIENT(IS_PLAYER(it) && it != self, LAMBDA(
+               FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA(
                        if(STAT(FROZEN, it) == 0)
                        if(!IS_DEAD(it))
-                       if(SAME_TEAM(it, self))
-                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, it.absmin, it.absmax))
+                       if(SAME_TEAM(it, player))
+                       if(boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax))
                        {
                                if(!o)
                                        o = it;
-                               if(STAT(FROZEN, self) == 1)
+                               if(STAT(FROZEN, player) == 1)
                                        it.reviving = true;
                                ++n;
                        }
@@ -515,21 +465,21 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
 
        }
 
-       if(n && STAT(FROZEN, self) == 1) // OK, there is at least one teammate reviving us
+       if(n && STAT(FROZEN, player) == 1) // OK, there is at least one teammate reviving us
        {
-               self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
-               self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
+               player.revive_progress = bound(0, player.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
+               player.health = max(1, player.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
 
-               if(self.revive_progress >= 1)
+               if(player.revive_progress >= 1)
                {
-                       freezetag_Unfreeze(self);
+                       freezetag_Unfreeze(player);
                        freezetag_count_alive_players();
 
                        if(n == -1)
                        {
-                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime);
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, self.netname, autocvar_g_freezetag_frozen_maxtime);
-                               return 1;
+                               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime);
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, player.netname, autocvar_g_freezetag_frozen_maxtime);
+                               return true;
                        }
 
                        // EVERY team mate nearby gets a point (even if multiple!)
@@ -539,27 +489,27 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
                                nades_GiveBonus(it,autocvar_g_nades_bonus_score_low);
                        ));
 
-                       Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
-                       Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
-                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED, self.netname, o.netname);
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
+                       Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname);
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED, player.netname, o.netname);
                }
 
                FOREACH_CLIENT(IS_PLAYER(it) && it.reviving, LAMBDA(
-                       it.revive_progress = self.revive_progress;
+                       it.revive_progress = player.revive_progress;
                        it.reviving = false;
                ));
        }
-       else if(!n && STAT(FROZEN, self) == 1) // only if no teammate is nearby will we reset
+       else if(!n && STAT(FROZEN, player) == 1) // only if no teammate is nearby will we reset
        {
-               self.revive_progress = bound(0, self.revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
-               self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
+               player.revive_progress = bound(0, player.revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
+               player.health = max(1, player.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
        }
-       else if(!n && !STAT(FROZEN, self))
+       else if(!n && !STAT(FROZEN, player))
        {
-               self.revive_progress = 0; // thawing nobody
+               player.revive_progress = 0; // thawing nobody
        }
 
-       return 1;
+       return true;
 }
 
 MUTATOR_HOOKFUNCTION(ft, SetStartItems)
@@ -573,43 +523,66 @@ MUTATOR_HOOKFUNCTION(ft, SetStartItems)
        start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_lms_start_ammo_cells");
        start_ammo_plasma  = warmup_start_ammo_plasma  = cvar("g_lms_start_ammo_plasma");
        start_ammo_fuel    = warmup_start_ammo_fuel    = cvar("g_lms_start_ammo_fuel");
-
-       return 0;
 }
 
 MUTATOR_HOOKFUNCTION(ft, HavocBot_ChooseRole)
-{SELFPARAM();
-       if (!IS_DEAD(self))
+{
+       entity bot = M_ARGV(0, entity);
+
+       if (!IS_DEAD(bot))
        {
                if (random() < 0.5)
-                       self.havocbot_role = havocbot_role_ft_freeing;
+                       bot.havocbot_role = havocbot_role_ft_freeing;
                else
-                       self.havocbot_role = havocbot_role_ft_offense;
+                       bot.havocbot_role = havocbot_role_ft_offense;
        }
 
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ft, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(ft, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
-       ret_float = freezetag_teams;
-       return false;
+       M_ARGV(0, float) = freezetag_teams;
 }
 
 MUTATOR_HOOKFUNCTION(ft, SetWeaponArena)
 {
        // most weapons arena
-       if(ret_string == "0" || ret_string == "")
-               ret_string = "most";
-       return false;
+       if(M_ARGV(0, string) == "0" || M_ARGV(0, string) == "")
+               M_ARGV(0, string) = "most";
+}
+
+MUTATOR_HOOKFUNCTION(ft, FragCenterMessage)
+{
+       entity frag_attacker = M_ARGV(0, entity);
+       entity frag_target = M_ARGV(1, entity);
+       //float frag_deathtype = M_ARGV(2, float);
+       int kill_count_to_attacker = M_ARGV(3, int);
+       int kill_count_to_target = M_ARGV(4, int);
+
+       if(STAT(FROZEN, frag_target))
+               return; // target was already frozen, so this is just pushing them off the cliff
+
+       Send_Notification(NOTIF_ONE, frag_attacker, MSG_CHOICE, CHOICE_FRAG_FREEZE, frag_target.netname, kill_count_to_attacker, (IS_BOT_CLIENT(frag_target) ? -1 : frag_target.ping));
+       Send_Notification(NOTIF_ONE, frag_target, MSG_CHOICE, CHOICE_FRAGGED_FREEZE, frag_attacker.netname, kill_count_to_target, frag_attacker.health, frag_attacker.armorvalue, (IS_BOT_CLIENT(frag_attacker) ? -1 : frag_attacker.ping));
+
+       return true;
 }
 
 void freezetag_Initialize()
 {
        freezetag_teams = autocvar_g_freezetag_teams_override;
        if(freezetag_teams < 2)
-               freezetag_teams = autocvar_g_freezetag_teams;
+               freezetag_teams = cvar("g_freezetag_teams"); // read the cvar directly as it gets written earlier in the same frame
        freezetag_teams = bound(2, freezetag_teams, 4);
+
+       int teams = 0;
+       if(freezetag_teams >= 1) teams |= BIT(0);
+       if(freezetag_teams >= 2) teams |= BIT(1);
+       if(freezetag_teams >= 3) teams |= BIT(2);
+       if(freezetag_teams >= 4) teams |= BIT(3);
+
+       freezetag_teams = teams; // now set it?
        freezetag_ScoreRules(freezetag_teams);
 
        round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null);
@@ -617,5 +590,3 @@ void freezetag_Initialize()
 
        EliminatedPlayers_Init(freezetag_isEliminated);
 }
-
-#endif