]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into terencehill/ca_fixes
authorMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 17:47:08 +0000 (04:47 +1100)
committerMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 17:47:08 +0000 (04:47 +1100)
Conflicts:
qcsrc/common/mapinfo.qh
qcsrc/common/notifications.qh
qcsrc/server/mutators/gamemode_freezetag.qc

1  2 
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/mutators/gamemode_ca.qc
qcsrc/server/mutators/gamemode_freezetag.qc

diff --combined gamemodes.cfg
index c5cfa05291fe38e59aa5eb9e0b069c5e72fb2beb,4e14689e9ebfe9f280148c94c17cf13b8ff484ca..42226fad5a0de8d2ff1af31c1f166519919a5ce5
@@@ -62,6 -62,28 +62,28 @@@ alias sv_hook_gamerestar
  alias sv_hook_gameend
  
  
+ // =====================
+ //  gametype vote hooks
+ // =====================
+ // these are called when the mode is switched via gametype vote screen, earlier than gamestart hooks (useful for enabling per-gamemode mutators)
+ alias sv_vote_gametype_hook_all 
+ alias sv_vote_gametype_hook_as
+ alias sv_vote_gametype_hook_ca
+ alias sv_vote_gametype_hook_ctf
+ alias sv_vote_gametype_hook_cts
+ alias sv_vote_gametype_hook_dm
+ alias sv_vote_gametype_hook_dom
+ alias sv_vote_gametype_hook_ft
+ alias sv_vote_gametype_hook_inv
+ alias sv_vote_gametype_hook_ka
+ alias sv_vote_gametype_hook_kh
+ alias sv_vote_gametype_hook_lms
+ alias sv_vote_gametype_hook_nb
+ alias sv_vote_gametype_hook_ons
+ alias sv_vote_gametype_hook_rc
+ alias sv_vote_gametype_hook_tdm
  // ===========
  //  leadlimit
  // ===========
@@@ -203,10 -225,10 +225,10 @@@ set g_assault 0 "Assault: attack the en
  // ============
  //  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"
@@@ -314,6 -336,10 +336,10 @@@ set g_domination_point_fullbright        0 "do
  set g_domination_point_rate           0 "override: how often to give those points"
  set g_domination_point_capturetime    0.1 "how long it takes to capture a point (given no interference)"
  set g_domination_point_glow           0 "domination point glow (warning, slow)"
+ set g_domination_roundbased 0 "enable round-based domination (capture all control points to win the round)"
+ set g_domination_roundbased_point_limit 5 "capture limit in round-based domination mode"
+ set g_domination_round_timelimit 120
+ set g_domination_warmup 5
  //set g_domination_balance_team_points        1 "# of points received is based on team sizes"
  
  
@@@ -327,9 -353,12 +353,12 @@@ seta g_freezetag_point_leadlimit -1      "Fr
  set g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
  set g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
  set g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+ set g_freezetag_revive_nade 1 "Enable reviving from own nade explosion"
+ set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
  set g_freezetag_revive_falldamage 0 "Enable reviving from this amount of fall damage"
  set g_freezetag_revive_falldamage_health 40 "Amount of health player has if they revived from falling"
  set g_freezetag_round_timelimit 180 "round time limit in seconds"
+ set g_freezetag_frozen_damage_trigger 1 "if 1, frozen players falling into the void will die instead of teleporting to spawn"
  set g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
  set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
  seta g_freezetag_teams_override 0
diff --combined qcsrc/common/mapinfo.qh
index e381513ca57772ed59f7ad050cc7bd1a17a05273,aa1d8fd5e7fbd3c05756aed46131fb0bcd2cdd61..df5c7ff6454325172ac19c338bea6b388a967a45
@@@ -8,8 -8,9 +8,9 @@@ entity MapInfo_Type_last
  .string mdl; // game type short name
  .string message; // human readable name
  .string model2; // game type defaults
+ .string gametype_description; // game type description
  
- #define REGISTER_GAMETYPE(hname,sname,g_name,NAME,defaults) \
+ #define REGISTER_GAMETYPE(hname,sname,g_name,NAME,defaults,gdescription) \
        var float MAPINFO_TYPE_##NAME; \
        var entity MapInfo_Type##g_name; \
        void RegisterGametypes_##g_name() \
@@@ -22,6 -23,7 +23,7 @@@
                MapInfo_Type##g_name.mdl = #sname; \
                MapInfo_Type##g_name.message = hname; \
                MapInfo_Type##g_name.model2 = defaults; \
+               MapInfo_Type##g_name.gametype_description = gdescription; \
                if(!MapInfo_Type_first) \
                        MapInfo_Type_first = MapInfo_Type##g_name; \
                if(MapInfo_Type_last) \
  #define IS_GAMETYPE(NAME) \
        (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
  
- REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0");
+ REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0",_("Kill all enemies"));
  #define g_dm IS_GAMETYPE(DEATHMATCH)
  
- REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0");
+ REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left"));
  #define g_lms IS_GAMETYPE(LMS)
  
- REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0");
+ REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line"));
  #define g_race IS_GAMETYPE(RACE)
  
- REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1");
+ REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1",_("Race for fastest time"));
  #define g_cts IS_GAMETYPE(CTS)
  
- REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0");
+ REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Kill all enemy teammates"));
  #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH)
  
- REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0");
+ REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0",_("Find and bring the enemy flag to your base to capture it"));
  #define g_ctf IS_GAMETYPE(CTF)
  
- REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 teams=2 leadlimit=0");
 -REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0",_("Kill all enemy teammates to win the round"));
++REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round"));
  #define g_ca IS_GAMETYPE(CA)
  
- REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0");
+ REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win"));
  #define g_domination IS_GAMETYPE(DOMINATION)
  
- REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0");
+ REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round"));
  #define g_keyhunt IS_GAMETYPE(KEYHUNT)
  
- REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20");
+ REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out"));
  #define g_assault IS_GAMETYPE(ASSAULT)
  
- REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20");
+ REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20",_("Capture control points to reach and destroy the enemy generator"));
  #define g_onslaught IS_GAMETYPE(ONSLAUGHT)
  
- REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0");
+ REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0",_("XonSports"));
  #define g_nexball IS_GAMETYPE(NEXBALL)
  
- REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0");
+ REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them"));
  #define g_freezetag IS_GAMETYPE(FREEZETAG)
  
- REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30");
+ REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30",_("Hold the ball to get points for kills"));
  #define g_keepaway IS_GAMETYPE(KEEPAWAY)
  
- REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50 teams=0");
+ REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50 teams=0",_("Survive against waves of monsters"));
  #define g_invasion IS_GAMETYPE(INVASION)
  
  const float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for minstagib-only maps
@@@ -134,13 -136,14 +136,14 @@@ float MapInfo_CheckMap(string s); // re
  void MapInfo_LoadMap(string s, float reinit);
  
  // list all maps for the current game type
- string MapInfo_ListAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
+ string MapInfo_ListAllowedMaps(float type, float pFlagsRequired, float pFlagsForbidden);
  // list all allowed maps (for any game type)
  string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
  
  // gets a gametype from a string
  string _MapInfo_GetDefaultEx(float t);
  float MapInfo_Type_FromString(string t);
+ string MapInfo_Type_Description(float t);
  string MapInfo_Type_ToString(float t);
  string MapInfo_Type_ToText(float t);
  void MapInfo_SwitchGameType(float t);
index d05c6babda56b9e601a7031eb5483d23e95e8112,60cc622fa59509a87543ab82a18fe50a4d70ef47..3080bd6ca67d8201266f5928a4eec8ae4b3452c8
@@@ -360,7 -360,11 +360,11 @@@ void Send_Notification_WOCOVA
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FIRE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s"), _("^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_LAVA,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_lava",          _("^BG%s%s^K1 was cooked by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_MONSTER,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was pushed infront of a monster by ^BG%s^K1%s%s"), "") \
-       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade",          _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_NAPALM,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_napalm",   _("^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s"), _("^BG%s%s^K1 got too close to a napalm explosion%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_ice",      _("^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE_FREEZE,   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_ice",      _("^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_HEAL,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_heal",     _("^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SHOOTING_STAR,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_shootingstar",  _("^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SLIME,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was slimed by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SWAMP,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was preserved by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_DEATH,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_GUN,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_ROCKET,    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VENGEANCE,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was destroyed by the vengeful ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VOID,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_void",          _("^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_AUTOTEAMCHANGE,      2, 1, "s1 s2loc death_team", "",         "",                     _("^BG%s^K1 was moved into the %s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_BETRAYAL,            2, 1, "s1 s2loc spree_lost", "s1",       "notify_teamkill_red",  _("^BG%s^K1 became enemies with the Lord of Teamplay%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FIRE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 became a bit too crispy%s%s"), _("^BG%s^K1 felt a little hot%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_GENERIC,             2, 1, "s1 s2loc spree_lost", "s1",       "notify_selfkill",      _("^BG%s^K1 died%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_LAVA,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_lava",          _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s")) \
-       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_MAGE,                2, 1, "s1 s2loc spree_lost", "s1",           "notify_death",                 _("^BG%s^K1 was exploded by a Mage%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_CLAW,   2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_SMASH,  2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was smashed by a Shambler%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_WYVERN,          2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_JUMP,     2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 joins the Zombies%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_MELEE,    2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was given kung fu lessons by a Zombie%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade",          _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_NAPALM,         2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_napalm",   _("^BG%s^K1 was burned to death by their own Napalm Nade%s%s"), _("^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE,            2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_ice",      _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE_FREEZE,     2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_ice",      _("^BG%s^K1 was frozen to death by their own Ice Nade%s%s"), _("^BG%s^K1 felt a little chilly%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_HEAL,           2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_heal",     _("^BG%s^K1's Healing Nade didn't quite heal them%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NOAMMO,              2, 1, "s1 s2loc spree_lost", "s1",       "notify_outofammo",     _("^BG%s^K1 died%s%s. What's the point of living without ammo?"), _("^BG%s^K1 ran out of ammo%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_ROT,                 2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 rotted away%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_SHOOTING_STAR,       2, 1, "s1 s2loc spree_lost", "s1",       "notify_shootingstar",  _("^BG%s^K1 became a shooting star%s%s"), "") \
        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_DOMINATION_CAPTURE_TIME,        2, 2, "s1 s2 f1 f2", "",                 "",                     _("^BG%s^BG%s^BG (%s points every %s seconds)"), "") \
        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"), "") \
+       MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_NADE,         1, 0, "s1", "",                          "",                     _("^BG%s^K3 was revived by their Nade explosion"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED,         1, 1, "s1 f1", "",                       "",                     _("^BG%s^K3 was automatically revived after %s second(s)"), "") \
        MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4,             0, 0, "", "",                            "",                     _("^TC^TT^BG team wins the round"), "") \
        MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN,               1, 0, "s1", "",                          "",                     _("^BG%s^BG wins the round"), "") \
        MSG_INFO_NOTIF(1, INFO_ROUND_OVER,                     0, 0, "", "",                            "",                     _("^BGRound over, there's no winner"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF,                 1, 0, "s1", "",                          "",                     _("^BG%s^K1 froze themself"), "") \
        MSG_INFO_NOTIF(1, INFO_GODMODE_OFF,                    0, 1, "f1", "",                          "",                     _("^BGGodmode saved you %s units of damage, cheater!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF,                      1, 1, "s1 item_buffname", "",            "",                     _("^BG%s^BG got the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_LOST,                 1, 1, "s1 item_buffname", "",            "",                     _("^BG%s^BG lost the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_DROP,                 0, 1, "item_buffname", "",               "",                     _("^BGYou dropped the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_GOT,                  0, 1, "item_buffname", "",               "",                     _("^BGYou got the %s^BG Buff!"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DONTHAVE,           0, 1, "item_wepname", "",                      "",               _("^BGYou do not have the ^F1%s"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DROP,               1, 1, "item_wepname item_wepammo", "",         "",               _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_GOT,                0, 1, "item_wepname", "",                      "",               _("^BGYou got the ^F1%s"), "") \
        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!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE,  1, 4, "spree_cen s1 frag_stats",  NO_CPID, "0 0", _("^K1%sYou were typefragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing^BG%s")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE,     1, 2, "spree_cen s1 frag_ping",   NO_CPID, "0 0", _("^K1%sYou typefragged ^BG%s^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing^BG%s")) \
        MSG_CENTER_NOTIF(1, CENTER_NADE_THROW,                          0, 0, "",             CPID_NADES,          "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the nade!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_NADE_BONUS,                          0, 0, "",             CPID_NADES,          "0 0", _("^F2You got a ^K1BONUS GRENADE^F2!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_AUTOTEAMCHANGE,   0, 1, "death_team",   NO_CPID,             "0 0", _("^BGYou have been moved into a different team\nYou are now on: %s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_BETRAYAL,         0, 0, "",             NO_CPID,             "0 0", _("^K1Don't shoot your team mates!"), _("^K1Don't go against your team mates!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_CAMP,             0, 0, "",             NO_CPID,             "0 0", _("^K1Die camper!"), _("^K1Reconsider your tactics, camper!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_LAVA,             0, 0, "",             NO_CPID,             "0 0", _("^K1You couldn't stand the heat!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_MONSTER,          0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed by a monster!"), _("^K1You need to watch out for monsters!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE,                             0, 0, "",                         NO_CPID,                         "0 0", _("^K1You forgot to put the pin back in!"), _("^K1Tastes like chicken!")) \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_NAPALM,              0, 0, "",                         NO_CPID,                         "0 0", _("^K1Hanging around a napalm explosion is bad!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_ICE_FREEZE,  0, 0, "",                         NO_CPID,                         "0 0", _("^K1You got a little bit too cold!"), _("^K1You felt a little chilly!")) \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_HEAL,        0, 0, "",             NO_CPID,             "0 0", _("^K1Your Healing Nade is a bit defective"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NOAMMO,           0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed for running out of ammo..."), _("^K1You are respawning for running out of ammo...")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_ROT,              0, 0, "",             NO_CPID,             "0 0", _("^K1You grew too old without taking your medicine"), _("^K1You need to preserve your health")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_SHOOTING_STAR,    0, 0, "",             NO_CPID,             "0 0", _("^K1You became a shooting star!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You froze ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN,            1, 0, "s1",           NO_CPID,             "0 0", _("^K1You were frozen by ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You revived ^BG%s"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_FALL,       0, 0, "",             NO_CPID,             "0 0", _("^K3You revived yourself"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_SELF,       0, 0, "",             NO_CPID,             "0 0", _("^K3You revived yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED,           1, 0, "s1",           NO_CPID,             "0 0", _("^K3You were revived by ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED,      0, 1, "f1",           NO_CPID,             "0 0", _("^K3You were automatically revived after %s second(s)"), "") \
        MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4,          0, 0, "",             CPID_ROUND,          "0 0", _("^TC^TT^BG team wins the round"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF,              0, 0, "",             NO_CPID,             "0 0", _("^K1You froze yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE,        0, 0, "",             NO_CPID,             "0 0", _("^K1Round already started, you spawn as frozen"), "") \
        MSG_CENTER_NOTIF(1, CENTER_INVASION_SUPERMONSTER,       1, 0, "s1",           NO_CPID,             "0 0", _("^K1A %s has arrived!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_DROP,              0, 1, "item_buffname",                     CPID_ITEM, "item_centime 0", _("^BGYou dropped the %s^BG Buff!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_GOT,               0, 1, "item_buffname",                     CPID_ITEM, "item_centime 0", _("^BGYou got the %s^BG Buff!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE,        0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP,            1, 1, "item_wepname item_wepammo",         CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT,             0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
        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!")) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA,                    NO_MSG,        INFO_DEATH_MURDER_LAVA,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_MONSTER,                 NO_MSG,        INFO_DEATH_MURDER_MONSTER,                 CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE,                    NO_MSG,        INFO_DEATH_MURDER_NADE,                    NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_NAPALM,             NO_MSG,        INFO_DEATH_MURDER_NADE_NAPALM,             NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE,                NO_MSG,        INFO_DEATH_MURDER_NADE_ICE,                NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE_FREEZE,         NO_MSG,        INFO_DEATH_MURDER_NADE_ICE_FREEZE,         NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_HEAL,               NO_MSG,        INFO_DEATH_MURDER_NADE_HEAL,               NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR,           NO_MSG,        INFO_DEATH_MURDER_SHOOTING_STAR,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME,                   NO_MSG,        INFO_DEATH_MURDER_SLIME,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SWAMP,                   NO_MSG,        INFO_DEATH_MURDER_SWAMP,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_DEATH,           NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_DEATH,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_GUN,             NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_GUN,             NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_ROCKET,          NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_ROCKET,          NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_VENGEANCE,               NO_MSG,        INFO_DEATH_MURDER_VENGEANCE,               NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VOID,                    NO_MSG,        INFO_DEATH_MURDER_VOID,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_AUTOTEAMCHANGE,            NO_MSG,        INFO_DEATH_SELF_AUTOTEAMCHANGE,            CENTER_DEATH_SELF_AUTOTEAMCHANGE) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_BETRAYAL,                  NO_MSG,        INFO_DEATH_SELF_BETRAYAL,                  CENTER_DEATH_SELF_BETRAYAL) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_JUMP,                   NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_JUMP,                   CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_MELEE,                  NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_MELEE,                  CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NADE,                                              NO_MSG,                INFO_DEATH_SELF_NADE,                                      CENTER_DEATH_SELF_NADE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_NAPALM,                               NO_MSG,                INFO_DEATH_SELF_NADE_NAPALM,                       CENTER_DEATH_SELF_NADE_NAPALM) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE,                                  NO_MSG,                INFO_DEATH_SELF_NADE_ICE,                                  CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE_FREEZE,           NO_MSG,                INFO_DEATH_SELF_NADE_ICE_FREEZE,           CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_HEAL,                 NO_MSG,        INFO_DEATH_SELF_NADE_HEAL,                 CENTER_DEATH_SELF_NADE_HEAL) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO,                    NO_MSG,        INFO_DEATH_SELF_NOAMMO,                    CENTER_DEATH_SELF_NOAMMO) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_ROT,                       NO_MSG,        INFO_DEATH_SELF_ROT,                       CENTER_DEATH_SELF_ROT) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_SHOOTING_STAR,             NO_MSG,        INFO_DEATH_SELF_SHOOTING_STAR,             CENTER_DEATH_SELF_SHOOTING_STAR) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_DEATH,             NO_MSG,        INFO_DEATH_SELF_VH_WAKI_DEATH,             CENTER_DEATH_SELF_VH_WAKI_DEATH) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_ROCKET,            NO_MSG,        INFO_DEATH_SELF_VH_WAKI_ROCKET,            CENTER_DEATH_SELF_VH_WAKI_ROCKET) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VOID,                      NO_MSG,        INFO_DEATH_SELF_VOID,                      CENTER_DEATH_SELF_VOID) \
+       MSG_MULTI_NOTIF(1, ITEM_BUFF_DROP,                       NO_MSG,        INFO_ITEM_BUFF_DROP,                       CENTER_ITEM_BUFF_DROP) \
+       MSG_MULTI_NOTIF(1, ITEM_BUFF_GOT,                        NO_MSG,        INFO_ITEM_BUFF_GOT,                        CENTER_ITEM_BUFF_GOT) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_DONTHAVE,                 NO_MSG,        INFO_ITEM_WEAPON_DONTHAVE,                 CENTER_ITEM_WEAPON_DONTHAVE) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_DROP,                     NO_MSG,        INFO_ITEM_WEAPON_DROP,                     CENTER_ITEM_WEAPON_DROP) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_GOT,                      NO_MSG,        INFO_ITEM_WEAPON_GOT,                      CENTER_ITEM_WEAPON_GOT) \
@@@ -937,6 -966,7 +969,7 @@@ var float autocvar_notification_show_sp
      item_wepname: return full name of a weapon from weaponid
      item_wepammo: ammo display for weapon from string
      item_centime: amount of time to display weapon message in centerprint
+     item_buffname: return full name of a buff from buffid
      death_team: show the full name of the team a player is switching from
  */
  
@@@ -979,7 -1009,7 +1012,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)) \
      ARG_CASE(ARG_CS_SV,     "spree_end",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
      ARG_CASE(ARG_CS_SV,     "spree_lost",    (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
      ARG_CASE(ARG_CS_SV,     "item_wepname",  W_Name(f1)) \
+     ARG_CASE(ARG_CS_SV,     "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f1)), Buff_PrettyName(f1))) \
      ARG_CASE(ARG_CS_SV,     "item_wepammo",  (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \
      ARG_CASE(ARG_DC,        "item_centime",  ftos(autocvar_notification_item_centerprinttime)) \
      ARG_CASE(ARG_SV,        "death_team",    Team_ColoredFullName(f1)) \
@@@ -1023,26 -1054,26 +1057,26 @@@ string notif_arg_frag_stats(float fheal
                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 718518231fb6bb9d95d4bc8f471102b35637efcb,b3c6b65775d5121c8a6394cd199b51ff3b08363a..6490073d5f78d8785e1f1d523979db7acaac0e20
@@@ -1,6 -1,3 +1,3 @@@
- void race_send_recordtime(float msg);
- void race_SendRankings(float pos, float prevpos, float del, float msg);
  void send_CSQC_teamnagger() {
        WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
        WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
@@@ -140,7 -137,6 +137,6 @@@ void PutObserverInServer (void
  {
        entity  spot;
      self.hud = HUD_NORMAL;
-       race_PreSpawnObserver();
  
        spot = SelectSpawnPoint (TRUE);
        if(!spot)
                WriteEntity(MSG_ONE, self);
        }
  
-       if((g_race && g_race_qualifying) || g_cts)
-       {
-               if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
-                       self.frags = FRAGS_LMS_LOSER;
-               else
-                       self.frags = FRAGS_SPECTATOR;
-       }
-       else
-               self.frags = FRAGS_SPECTATOR;
+       self.frags = FRAGS_SPECTATOR;
  
        MUTATOR_CALLHOOK(MakePlayerObserver);
  
        Portal_ClearAll(self);
  
+       Unfreeze(self);
        if(self.alivetime)
        {
                if(!warmup_stage)
        self.angles_z = 0;
        self.fixangle = TRUE;
        self.crouch = FALSE;
+       self.revival_time = 0;
  
        setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
        self.prevorigin = self.origin;
        self.punchvector = '0 0 0';
        self.oldvelocity = self.velocity;
        self.fire_endtime = -1;
 +      self.event_damage = func_null;
  }
  
  .float model_randomizer;
@@@ -392,8 -382,6 +383,6 @@@ void PutClientInServer (void
                if(self.team < 0)
                        JoinBestTeam(self, FALSE, TRUE);
  
-               race_PreSpawn();
                spot = SelectSpawnPoint (FALSE);
                if(!spot)
                {
                self.punchvector = '0 0 0';
                self.oldvelocity = self.velocity;
                self.fire_endtime = -1;
+               self.revival_time = 0;
  
                entity spawnevent = spawn();
                spawnevent.owner = self;
                Net_LinkEntity(spawnevent, FALSE, 0.5, SpawnEvent_Send);
  
+               // Cut off any still running player sounds.
+               stopsound(self, CH_PLAYER_SINGLE);
                self.model = "";
                FixPlayermodel();
                self.drawonlytoclient = world;
  
                self.speedrunning = FALSE;
  
-               race_PostSpawn(spot);
                //stuffcmd(self, "chase_active 0");
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
  
                        activator = world;
                self = oldself;
  
+               Unfreeze(self);
                spawn_spot = spot;
                MUTATOR_CALLHOOK(PlayerSpawn);
  
@@@ -787,8 -779,8 +780,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
  }
@@@ -942,7 -934,7 +935,7 @@@ void ClientKill (void
  {
        if(gameover) return;
        if(self.player_blocked) return;
-       if(self.freezetag_frozen) return;
+       if(self.frozen) return;
  
        ClientKill_TeamChange(0);
  }
@@@ -1058,8 -1050,6 +1051,6 @@@ void ClientConnect (void
  
        anticheat_init();
  
-       race_PreSpawnObserver();
        // identify the right forced team
        if(autocvar_g_campaign)
        {
        else
                self.hitplotfh = -1;
  
-       if(g_race || g_cts) {
-               string rr;
-               if(g_cts)
-                       rr = CTS_RECORD;
-               else
-                       rr = RACE_RECORD;
-               msg_entity = self;
-               race_send_recordtime(MSG_ONE);
-               race_send_speedaward(MSG_ONE);
-               speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed")));
-               speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp")));
-               race_send_speedaward_alltimebest(MSG_ONE);
-               float i;
-               for (i = 1; i <= RANKINGS_CNT; ++i) {
-                       race_SendRankings(i, 0, 0, MSG_ONE);
-               }
-       }
-       else if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca) // teamnagger is currently bad for ca
+       if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
                send_CSQC_teamnagger();
  
        CheatInitClient();
@@@ -1293,6 -1263,8 +1264,8 @@@ void ClientDisconnect (void
  
        Portal_ClearAll(self);
  
+       Unfreeze(self);
        RemoveGrapplingHook(self);
  
        // Here, everything has been done that requires this player to be a client.
@@@ -1586,17 -1558,27 +1559,27 @@@ float CalcRotRegen(float current, floa
  
  void player_regen (void)
  {
+       float max_mod, regen_mod, rot_mod, limit_mod;
+       max_mod = regen_mod = rot_mod = limit_mod = 1;
+       regen_mod_max = max_mod;
+       regen_mod_regen = regen_mod;
+       regen_mod_rot = rot_mod;
+       regen_mod_limit = limit_mod;
        if(!MUTATOR_CALLHOOK(PlayerRegen))
+       if(!self.frozen)
        {
-               float minh, mina, maxh, maxa, limith, limita, max_mod, regen_mod, rot_mod, limit_mod;
+               float minh, mina, maxh, maxa, limith, limita;
                maxh = autocvar_g_balance_health_rotstable;
                maxa = autocvar_g_balance_armor_rotstable;
                minh = autocvar_g_balance_health_regenstable;
                mina = autocvar_g_balance_armor_regenstable;
                limith = autocvar_g_balance_health_limit;
                limita = autocvar_g_balance_armor_limit;
-               max_mod = regen_mod = rot_mod = limit_mod = 1;
+               
+               max_mod = regen_mod_max;
+               regen_mod = regen_mod_regen;
+               rot_mod = regen_mod_rot;
+               limit_mod = regen_mod_limit;
  
                maxh = maxh * max_mod;
                minh = minh * max_mod;
@@@ -1732,6 -1714,8 +1715,8 @@@ void SpectateCopy(entity spectatee) 
        self.dmg_inflictor = spectatee.dmg_inflictor;
        self.v_angle = spectatee.v_angle;
        self.angles = spectatee.v_angle;
+       self.frozen = spectatee.frozen;
+       self.revive_progress = spectatee.revive_progress;
        if(!self.BUTTON_USE)
                self.fixangle = TRUE;
        setorigin(self, spectatee.origin);
@@@ -1937,6 -1921,7 +1922,7 @@@ void LeaveSpectatorMode(
                if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
                {
                        self.classname = "player";
+                       nades_RemoveBonus(self);
  
                        if(autocvar_g_campaign || autocvar_g_balance_teams)
                                { JoinBestTeam(self, FALSE, TRUE); }
@@@ -1992,7 -1977,7 +1978,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)
   * 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);
@@@ -2245,6 -2227,30 +2231,30 @@@ void PlayerPreThink (void
                return;
  #endif
  
+       if(self.frozen == 2)
+       {
+               self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
+               self.health = max(1, self.revive_progress * start_health);
+               self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+               if(self.revive_progress >= 1)
+                       Unfreeze(self);
+       }
+       else if(self.frozen == 3)
+       {
+               self.revive_progress = bound(0, self.revive_progress - frametime * self.revive_speed, 1);
+               self.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * self.revive_progress );
+               
+               if(self.health < 1)
+               {
+                       if(self.vehicle)
+                               vehicles_exit(VHEF_RELESE);
+                       self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+               }
+               else if ( self.revive_progress <= 0 )
+                       Unfreeze(self);
+       }
        MUTATOR_CALLHOOK(PlayerPreThink);
  
        if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
                        do_crouch = 0;
                if(self.vehicle)
                        do_crouch = 0;
-               if(self.freezetag_frozen)
+               if(self.frozen)
                        do_crouch = 0;
                if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
                        do_crouch = 0;
        if(self.spectatee_status != oldspectatee_status)
        {
                ClientData_Touch(self);
-               if(g_race || g_cts)
-                       race_InitSpectator();
        }
  
        if(self.teamkill_soundtime)
@@@ -2619,22 -2623,5 +2627,5 @@@ void PlayerPostThink (void
  
        playerdemo_write();
  
-       if((g_cts || g_race) && self.cvar_cl_allow_uidtracking == 1 && self.cvar_cl_allow_uid2name == 1)
-       {
-               if (!self.stored_netname)
-                       self.stored_netname = strzone(uid2name(self.crypto_idfp));
-               if(self.stored_netname != self.netname)
-               {
-                       db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname);
-                       strunzone(self.stored_netname);
-                       self.stored_netname = strzone(self.netname);
-               }
-       }
-       /*
-       if(g_race)
-               dprintf("%f %.6f\n", time, race_GetFractionalLapCount(self));
-       */
        CSQCMODEL_AUTOUPDATE();
  }
index 8c61d6f5682531d89e139ccd62167710a6f44c0c,c11e92051074b7fcbf15b208b774f14d96c00d88..a15fa048edb6141f01309f7ab9c4f111591226c0
@@@ -246,7 -246,7 +246,7 @@@ void player_anim (void
                else
                        deadbits = ANIMSTATE_DEAD2;
        float animbits = deadbits;
-       if(self.freezetag_frozen)
+       if(self.frozen)
                animbits |= ANIMSTATE_FROZEN;
        if(self.crouch)
                animbits |= ANIMSTATE_DUCK;
@@@ -336,9 -336,6 +336,9 @@@ void PlayerCorpseDamage (entity inflict
  
  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);
        else
                self.respawn_countdown = -1; // do not count down
  
-       if(g_cts || autocvar_g_forced_respawn)
+       if(autocvar_g_forced_respawn)
                self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
  }
  
@@@ -655,7 -652,6 +655,6 @@@ void PlayerDamage (entity inflictor, en
  
                // print an obituary message
                Obituary (attacker, inflictor, self, deathtype);
-               race_PreDie();
  
          // increment frag counter for used weapon type
          float w;
  
                // when we get here, player actually dies
  
+               Unfreeze(self); // remove any icy remains
+               self.health = 0; // Unfreeze resets health, so we need to set it back
                // clear waypoints
                WaypointSprite_PlayerDead();
                // throw a weapon
                self.flags &= ~FL_ONGROUND;
                // dying animation
                self.deadflag = DEAD_DYING;
 +
                // when to allow respawn
                calculate_player_respawn_time();
  
index cb6884b0f570567d40e776ddf35ebd37e59c8bf5,51a04b59135e01e3ac0fd9b6bd62c1d25271651f..7d67c4f30d621c751f0cb5ed9c627451f9a71a77
@@@ -152,8 -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; }
@@@ -291,7 -289,7 +291,7 @@@ void ClientCommand_mobspawn(float reque
                        else if(MUTATOR_CALLHOOK(AllowMobSpawning)) { sprint(self, "Monster spawning is currently disabled by a mutator.\n"); return; }
                        else if(!autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); return; }
                        else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); return; }
-                       else if(self.freezetag_frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
+                       else if(self.frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
                        else if(autocvar_g_campaign) { sprint(self, "You can't spawn monsters in campaign mode.\n"); return; }
                        else if(self.deadflag != DEAD_NO) { sprint(self, "You can't spawn monsters while dead.\n"); return; }
                        else if(monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; }
@@@ -582,11 -580,15 +582,11 @@@ void ClientCommand_spectate(float reque
                                        }
                                }
  
 -                              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 76419d8387039d086dbff3f7e8780ff1b6cc2e47,8b38ceb096a8cd537eda2238aad552d6fb7ae103..0069de3bcb7ef35805d2f73330ac00ef47fc1eef
@@@ -67,12 -67,15 +67,15 @@@ float CA_GetWinnerTeam(
  #define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
  float CA_CheckWinner()
  {
+       entity e;
        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);
                allowed_to_spawn = FALSE;
                round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+               FOR_EACH_PLAYER(e)
+                       nades_Clear(e);
                return 1;
        }
  
  
        allowed_to_spawn = FALSE;
        round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+       FOR_EACH_PLAYER(e)
+               nades_Clear(e);
        return 1;
  }
  
@@@ -106,32 -113,29 +113,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;
  }
@@@ -145,7 -149,6 +152,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
                {
                        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;
@@@ -164,11 -167,6 +171,11 @@@ MUTATOR_HOOKFUNCTION(ca_reset_map_playe
        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";
@@@ -197,48 -195,13 +204,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;
  
  MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver)
  {
 +      if(self.caplayer == 1)
 +              ca_LastPlayerForTeam_Notify();
        if(self.killindicator_teamchange == -2)
                self.caplayer = 0;
        if(self.caplayer)
@@@ -350,7 -311,6 +357,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 5280e559bbceab46554144dadbace4d92f41b63e,ffdc1612dde1235608c2874da85eefc60dd37c15..b02679e7a11e81acb3282524d7144d6f1d18791a
@@@ -1,7 -1,6 +1,6 @@@
  .float freezetag_frozen_time;
  .float freezetag_frozen_timeout;
  .float freezetag_revive_progress;
- .entity freezetag_ice;
  #define ICE_MAX_ALPHA 1
  #define ICE_MIN_ALPHA 0.1
  float freezetag_teams;
@@@ -10,29 -9,18 +9,18 @@@ void freezetag_count_alive_players(
  {
        entity e;
        total_players = redalive = bluealive = yellowalive = pinkalive = 0;
-       FOR_EACH_PLAYER(e) {
-               if(e.team == NUM_TEAM_1 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++redalive;
-               }
-               else if(e.team == NUM_TEAM_2 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++bluealive;
-               }
-               else if(e.team == NUM_TEAM_3 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++yellowalive;
-               }
-               else if(e.team == NUM_TEAM_4 && e.health >= 1)
+       FOR_EACH_PLAYER(e)
+       {
+               switch(e.team)
                {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++pinkalive;
+                       case NUM_TEAM_1: ++total_players; if(e.health >= 1 && e.frozen != 1) ++redalive; break;
+                       case NUM_TEAM_2: ++total_players; if(e.health >= 1 && e.frozen != 1) ++bluealive; break;
+                       case NUM_TEAM_3: ++total_players; if(e.health >= 1 && e.frozen != 1) ++yellowalive; break;
+                       case NUM_TEAM_4: ++total_players; if(e.health >= 1 && e.frozen != 1) ++pinkalive; break;
                }
        }
-       FOR_EACH_REALCLIENT(e) {
+       FOR_EACH_REALCLIENT(e)
+       {
                e.redalive_stat = redalive;
                e.bluealive_stat = bluealive;
                e.yellowalive_stat = yellowalive;
  #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;
  }
@@@ -105,7 -90,7 +93,7 @@@ float freezetag_CheckWinner(
                FOR_EACH_PLAYER(e)
                {
                        e.freezetag_frozen_timeout = 0;
-                       e.freezetag_revive_progress = 0;
+                       nades_Clear(e);
                }
                round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
                return 1;
        FOR_EACH_PLAYER(e)
        {
                e.freezetag_frozen_timeout = 0;
-               e.freezetag_revive_progress = 0;
+               nades_Clear(e);
        }
        round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
        return 1;
  }
  
-               if(!pl.freezetag_frozen)
 +entity freezetag_LastPlayerForTeam()
 +{
 +      entity pl, last_pl = world;
 +      FOR_EACH_PLAYER(pl)
 +      {
 +              if(pl.health >= 1)
- // 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
- void freezetag_Ice_Think()
- {
-       setorigin(self, self.owner.origin - '0 0 16');
-       self.nextthink = time;
- }
++              if(!pl.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);
 +      }
 +}
 +
  void freezetag_Add_Score(entity attacker)
  {
        if(attacker == self)
  
  void freezetag_Freeze(entity attacker)
  {
-       if(self.freezetag_frozen)
+       if(self.frozen)
                return;
-       self.freezetag_frozen = 1;
-       self.freezetag_frozen_time = time;
-       self.freezetag_revive_progress = 0;
-       self.health = 1;
        if(autocvar_g_freezetag_frozen_maxtime > 0)
                self.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
  
-       freezetag_count_alive_players();
+       Freeze(self, 0, 1, TRUE);
  
-       entity ice;
-       ice = spawn();
-       ice.owner = self;
-       ice.classname = "freezetag_ice";
-       ice.think = freezetag_Ice_Think;
-       ice.nextthink = time;
-       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
-       ice.alpha = ICE_MAX_ALPHA;
-       ice.colormod = Team_ColorRGB(self.team);
-       ice.glowmod = ice.colormod;
-       setmodel(ice, "models/ice/ice.md3");
-       self.freezetag_ice = ice;
-       RemoveGrapplingHook(self);
-       // 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');
+       freezetag_count_alive_players();
  
        freezetag_Add_Score(attacker);
  }
  
  void freezetag_Unfreeze(entity attacker)
  {
-       self.freezetag_frozen = 0;
        self.freezetag_frozen_time = 0;
        self.freezetag_frozen_timeout = 0;
-       self.freezetag_revive_progress = 0;
  
-       remove(self.freezetag_ice);
-       self.freezetag_ice = world;
-       if(self.waypointsprite_attached)
-               WaypointSprite_Kill(self.waypointsprite_attached);
+       Unfreeze(self);
  }
  
  
@@@ -258,7 -180,7 +211,7 @@@ void havocbot_goalrating_freeplayers(fl
        {
                if ((head != self) && (head.team == self.team))
                {
-                       if (head.freezetag_frozen)
+                       if (head.frozen == 1)
                        {
                                distance = vlen(head.origin - org);
                                if (distance > sradius)
@@@ -290,12 -212,12 +243,12 @@@ void havocbot_role_ft_offense(
        unfrozen = 0;
        FOR_EACH_PLAYER(head)
        {
-               if ((head.team == self.team) && (!head.freezetag_frozen))
+               if ((head.team == self.team) && (head.frozen != 1))
                        unfrozen++;
        }
  
        // If only one left on team or if role has timed out then start trying to free players.
-       if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout))
+       if (((unfrozen == 0) && (!self.frozen)) || (time > self.havocbot_role_timeout))
        {
                dprint("changing role to freeing\n");
                self.havocbot_role = havocbot_role_ft_freeing;
@@@ -353,8 -275,6 +306,8 @@@ void havocbot_role_ft_freeing(
  MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
  {
        self.health = 0; // neccessary to update correctly alive stats
-       if(!self.freezetag_frozen)
++      if(!self.frozen)
 +              freezetag_LastPlayerForTeam_Notify();
        freezetag_Unfreeze(world);
        freezetag_count_alive_players();
        return 1;
@@@ -365,7 -285,7 +318,7 @@@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDi
        if(round_handler_IsActive())
        if(round_handler_CountdownRunning())
        {
-               if(self.freezetag_frozen)
+               if(self.frozen)
                        freezetag_Unfreeze(world);
                freezetag_count_alive_players();
                return 1; // let the player die so that he can respawn whenever he wants
                || frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE)
        {
                // let the player die, he will be automatically frozen when he respawns
-               if(!self.freezetag_frozen)
+               if(self.frozen != 1)
                {
                        freezetag_Add_Score(frag_attacker);
                        freezetag_count_alive_players();
 +                      freezetag_LastPlayerForTeam_Notify();
                }
                else
                        freezetag_Unfreeze(world); // remove ice
+               self.health = 0; // Unfreeze resets health
                self.freezetag_frozen_timeout = -2; // freeze on respawn
                return 1;
        }
  
-       if(self.freezetag_frozen)
+       if(self.frozen)
                return 1;
  
        freezetag_Freeze(frag_attacker);
 +      freezetag_LastPlayerForTeam_Notify();
  
        if(frag_attacker == frag_target || frag_attacker == world)
        {
                Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname);
        }
  
-       frag_target.health = 1; // "respawn" the player :P
        return 1;
  }
  
@@@ -443,8 -360,6 +395,6 @@@ MUTATOR_HOOKFUNCTION(freezetag_reset_ma
        FOR_EACH_PLAYER(self)
        {
                self.killcount = 0;
-               if (self.freezetag_frozen)
-                       freezetag_Unfreeze(world);
                self.freezetag_frozen_timeout = -1;
                PutClientInServer();
                self.freezetag_frozen_timeout = 0;
@@@ -467,7 -382,7 +417,7 @@@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPr
        if(gameover)
                return 1;
  
-       if(self.freezetag_frozen)
+       if(self.frozen == 1)
        {
                // keep health = 1
                self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
  
        entity o;
        o = world;
-       if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
-               self.freezetag_ice.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.frozen)
+       //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)
                n = -1;
        {
                vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
                n = 0;
-               FOR_EACH_PLAYER(other) if(self != other)
+               FOR_EACH_PLAYER(other)
+               if(self != other)
+               if(other.frozen == 0)
+               if(other.deadflag == DEAD_NO)
+               if(SAME_TEAM(other, self))
+               if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
                {
-                       if(other.freezetag_frozen == 0)
-                       {
-                               if(other.team == self.team)
-                               {
-                                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
-                                       {
-                                               if(!o)
-                                                       o = other;
-                                               if(self.freezetag_frozen)
-                                                       other.reviving = TRUE;
-                                               ++n;
-                                       }
-                               }
-                       }
+                       if(!o)
+                               o = other;
+                       if(self.frozen == 1)
+                               other.reviving = TRUE;
+                       ++n;
                }
        }
  
-       if(n && self.freezetag_frozen) // OK, there is at least one teammate reviving us
+       if(n && self.frozen == 1) // OK, there is at least one teammate reviving us
        {
-               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
-               if(warmup_stage)
-                       self.health = max(1, self.freezetag_revive_progress * warmup_start_health);
-               else
-                       self.health = max(1, self.freezetag_revive_progress * start_health);
+               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));
  
-               if(self.freezetag_revive_progress >= 1)
+               if(self.revive_progress >= 1)
                {
                        freezetag_Unfreeze(self);
                        freezetag_count_alive_players();
                                {
                                        PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
                                        PlayerScore_Add(other, SP_SCORE, +1);
+                                       nades_GiveBonus(other,autocvar_g_nades_bonus_score_low);
                                }
                        }
  
                {
                        if(other.reviving)
                        {
-                               other.freezetag_revive_progress = self.freezetag_revive_progress;
+                               other.revive_progress = self.revive_progress;
                                other.reviving = FALSE;
                        }
                }
        }
-       else if(!n && self.freezetag_frozen) // only if no teammate is nearby will we reset
-       {
-               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
-               if(warmup_stage)
-                       self.health = max(1, self.freezetag_revive_progress * warmup_start_health);
-               else
-                       self.health = max(1, self.freezetag_revive_progress * start_health);
-       }
-       else if(!n)
+       else if(!n && self.frozen == 1) // only if no teammate is nearby will we reset
        {
-               self.freezetag_revive_progress = 0; // thawing nobody
+               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));
        }
-       return 1;
- }
- MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
- {
-       if(self.freezetag_frozen)
+       else if(!n && !self.frozen)
        {
-               if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
-               {
-                       self.movement_x = bound(-5, self.movement_x, 5);
-                       self.movement_y = bound(-5, self.movement_y, 5);
-                       self.movement_z = bound(-5, self.movement_z, 5);
-               }
-               else
-                       self.movement = '0 0 0';
-               self.disableclientprediction = 1;
+               self.revive_progress = 0; // thawing nobody
        }
-       return 1;
- }
- MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
- {
-       if(frag_target.freezetag_frozen && frag_deathtype != DEATH_HURTTRIGGER)
-       {
-               if(autocvar_g_freezetag_revive_falldamage > 0)
-               if(frag_deathtype == DEATH_FALL)
-               if(frag_damage >= autocvar_g_freezetag_revive_falldamage)
-               {
-                       freezetag_Unfreeze(frag_target);
-                       frag_target.health = autocvar_g_freezetag_revive_falldamage_health;
-                       pointparticles(particleeffectnum("iceorglass"), frag_target.origin, '0 0 0', 3);
-                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, frag_target.netname);
-                       Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_FALL);
-               }
  
-               frag_damage = 0;
-               frag_force = frag_force * autocvar_g_freezetag_frozen_force;
-       }
        return 1;
  }
  
- MUTATOR_HOOKFUNCTION(freezetag_PlayerJump)
- {
-       if(self.freezetag_frozen)
-               return TRUE; // no jumping in freezetag when frozen
-       return FALSE;
- }
- MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
- {
-       if (self.freezetag_frozen)
-               return 1;
-       return 0;
- }
- MUTATOR_HOOKFUNCTION(freezetag_ItemTouch)
- {
-       if (other.freezetag_frozen)
-               return MUT_ITEMTOUCH_RETURN;
-       return MUT_ITEMTOUCH_CONTINUE;
- }
  MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
  {
        if (!self.deadflag)
        return TRUE;
  }
  
- MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
- {
-       self.freezetag_frozen = other.freezetag_frozen;
-       self.freezetag_revive_progress = other.freezetag_revive_progress;
-       return 0;
- }
  MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount)
  {
        ret_float = freezetag_teams;
        return 0;
  }
  
- MUTATOR_HOOKFUNCTION(freezetag_VehicleTouch)
- {
-       if(other.freezetag_frozen)
-               return TRUE;
-       return FALSE;
- }
  void freezetag_Initialize()
  {
-       precache_model("models/ice/ice.md3");
        freezetag_teams = autocvar_g_freezetag_teams_override;
        if(freezetag_teams < 2)
                freezetag_teams = autocvar_g_freezetag_teams;
        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);
  }
  
  MUTATOR_DEFINITION(gamemode_freezetag)
        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(PlayerJump, freezetag_PlayerJump, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ItemTouch, freezetag_ItemTouch, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRole, freezetag_BotRoles, CBC_ORDER_ANY);
-       MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
        MUTATOR_HOOK(GetTeamCount, freezetag_GetTeamCount, CBC_ORDER_EXCLUSIVE);
-       MUTATOR_HOOK(VehicleTouch, freezetag_VehicleTouch, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
        {