Merge remote-tracking branch 'origin/master' into Mario/lms_updates
authorSamual Lenks <samual@xonotic.org>
Tue, 7 May 2013 05:37:55 +0000 (01:37 -0400)
committerSamual Lenks <samual@xonotic.org>
Tue, 7 May 2013 05:37:55 +0000 (01:37 -0400)
Conflicts:
qcsrc/server/arena.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/scores.qc

14 files changed:
1  2 
gamemodes.cfg
qcsrc/common/notifications.qh
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_lms.qc
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src
qcsrc/server/scores.qc
qcsrc/server/teamplay.qc

diff --combined gamemodes.cfg
@@@ -142,6 -142,7 +142,7 @@@ set g_ft_weapon_stay 
  set g_arena 0 "Arena: many one-on-one rounds are played to find the winner"
  set g_arena_maxspawned 2      "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)"
  set g_arena_roundbased 1      "if disabled, the next player will spawn as soon as someone dies"
+ set g_arena_round_timelimit 180
  set g_arena_warmup 5  "time, newly spawned players have to prepare themselves in round based matches"
  
  
@@@ -161,6 -162,9 +162,9 @@@ set g_ca_spectate_enemies 0 "Allow spec
  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
+ seta g_ca_teams_override 0
+ set g_ca_teams 0
  
  
  // ==================
@@@ -268,13 -272,17 +272,17 @@@ set g_domination_point_glow             0 "dominat
  //  freezetag
  // ===========
  set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them"
- seta g_freezetag_warmup 5 "Time players get to run around before the round starts"
+ set g_freezetag_warmup 5 "Time players get to run around before the round starts"
  seta g_freezetag_point_limit -1       "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_freezetag_point_leadlimit -1   "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
- seta g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
- seta g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
- seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
- seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+ 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_round_timelimit 180
+ 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
+ set g_freezetag_teams 0
  
  
  // ==========
@@@ -333,7 -341,6 +341,7 @@@ set g_keyhunt_teams 
  // ===================
  set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins"
  set g_lms_lives_override -1
 +set g_lms_extra_lives 0
  set g_lms_regenerate 0
  set g_lms_campcheck_interval 10
  set g_lms_campcheck_damage 100
@@@ -323,8 -323,12 +323,12 @@@ void Send_Notification_WOVA
        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\n"), "") \
        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\n"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K1 was frozen by ^BG%s\n"), "") \
-       MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s\n"), "") \
-       MULTITEAM_INFO(1, INFO_FREEZETAG_ROUND_WIN_, 4,        0, 0, "", "",                            "",                     _("^TC^TT^BG team wins the round, all other teams were frozen\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED,         1, 1, "s1 f1", "",                       "",                     _("^BG%s^K3 was automatically revived after %s second(s)\n"), "") \
+       MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4,             0, 0, "", "",                            "",                     _("^TC^TT^BG team wins the round\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN,               1, 0, "s1", "",                          "",                     _("^BG%s^BG wins the round\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_ROUND_TIED,                     0, 0, "", "",                            "",                     _("^BGRound tied\n"), "") \
+       MSG_INFO_NOTIF(1, INFO_ROUND_OVER,                     0, 0, "", "",                            "",                     _("^BGRound over, there's no winner\n"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF,                 1, 0, "s1", "",                          "",                     _("^BG%s^K1 froze themself\n"), "") \
        MSG_INFO_NOTIF(1, INFO_GODMODE_OFF,                    0, 1, "f1", "",                          "",                     _("^BGGodmode saved you %s units of damage, cheater!\n"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DONTHAVE,           0, 1, "item_wepname", "",                      "",               _("^BGYou do not have the ^F1%s\n"), "") \
                MSG_CENTER_NOTIF(default, prefix##PINK, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4))) \
        #endif
  #define MSG_CENTER_NOTIFICATIONS \
-       MSG_CENTER_NOTIF(1, CENTER_ARENA_BEGIN,                 0, 0, "",             CPID_ARENA,          "2 0", _("^F4Begin!"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_ARENA_NEEDPLAYER,            0, 0, "",             CPID_ARENA,          "2 0", _("^BGNeed at least 1 player in each team to play Clan Arena!"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_ARENA_ROUNDSTART,            0, 1, "",             CPID_ARENA,          "1 f1", _("^F4Round will start in ^COUNT"), "") \
        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_GAMESTART,      "2 0", _("^F4Begin!"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_GAMESTART,         0, 1, "",             CPID_GAMESTART,      "1 f1", _("^F4Game starts in ^COUNT"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN,             0, 0, "",             CPID_ROUND,          "2 0", _("^F4Begin!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_GAMESTART,         0, 1, "",             CPID_ROUND,          "1 f1", _("^F4Game starts in ^COUNT"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_ROUNDSTART,        0, 1, "",             CPID_ROUND,          "1 f1", _("^F4Round starts in ^COUNT"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_ROUNDSTOP,         0, 0, "",             CPID_ROUND,          "2 0", _("^F4Round cannot start"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ROUND_TIED,                  0, 0, "",             CPID_ROUND,          "0 0", _("^BGRound tied"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ROUND_OVER,                  0, 0, "",             CPID_ROUND,          "0 0", _("^BGRound over, there's no winner"), "") \
        MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_FREE,      0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
        MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_SHIELDED,  0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
        MULTITEAM_CENTER(1, CENTER_CTF_CAPTURE_, 2,             0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou captured the ^TC^TT^BG flag!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_TEAMKILL_FRAG,         1, 0, "s1",           NO_CPID,             "0 0", _("^K1Moron! You fragged ^BG%s^K1, a team mate!"), _("^K1Moron! You went against ^BG%s^K1, a team mate!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_TEAMKILL_FRAGGED,      1, 0, "s1",           NO_CPID,             "0 0", _("^K1You were fragged by ^BG%s^K1, a team mate"), _("^K1You were scored against by ^BG%s^K1, a team mate")) \
        MSG_CENTER_NOTIF(1, CENTER_DISCONNECT_IDLING,           0, 1, "",             CPID_IDLING,         "1 f1", _("^K1Stop idling!\n^BGDisconnecting in ^COUNT..."), "") \
 +      MSG_CENTER_NOTIF(1, CENTER_EXTRALIVES,                          0, 0, "",             NO_CPID,             "0 0", _("^F2You picked up some extra lives"), "") \
        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_REVIVED,           1, 0, "s1",           NO_CPID,             "0 0", _("^K3You were revived by ^BG%s"), "") \
-       MULTITEAM_CENTER(1, CENTER_FREEZETAG_ROUND_WIN_, 4,     0, 0, "",             NO_CPID,             "0 0", _("^TC^TT^BG team wins the round, all other teams were frozen"), "") \
+       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_ROUND_PLAYER_WIN,            1, 0, "s1",           CPID_ROUND,          "0 0", _("^BG%s^BG 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", _("^K1You spawned after the round started, you'll spawn as frozen"), "") \
+       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_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_HELP,                0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGAll keys are in your team's hands!\nHelp the key carriers to meet!"), "") \
        MULTITEAM_CENTER(1, CENTER_KEYHUNT_INTERFERE_, 4,       0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGAll keys are in ^TC^TT team^BG's hands!\nInterfere ^F4NOW^BG!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_MEET,                0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGAll keys are in your team's hands!\nMeet the other key carriers ^F4NOW^BG!"), "") \
+       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, "kh_teams",      CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       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_MISSING_PLAYERS,             0, 1, "f1",            CPID_MISSING_PLAYERS,  "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
        MSG_CENTER_NOTIF(1, CENTER_LMS_CAMPCHECK,               0, 0, "",              CPID_LMS_CAMP,         "0 0", _("^F2Don't camp!"), "") \
        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, ITEM_WEAPON_NOAMMO,                   NO_MSG,        INFO_ITEM_WEAPON_NOAMMO,                   CENTER_ITEM_WEAPON_NOAMMO) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_PRIMORSEC,                NO_MSG,        INFO_ITEM_WEAPON_PRIMORSEC,                CENTER_ITEM_WEAPON_PRIMORSEC) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_UNAVAILABLE,              NO_MSG,        INFO_ITEM_WEAPON_UNAVAILABLE,              CENTER_ITEM_WEAPON_UNAVAILABLE) \
-       MSG_MULTI_NOTIF(1, MULTI_ARENA_BEGIN,                    ANNCE_BEGIN,   NO_MSG,                                    CENTER_ARENA_BEGIN) \
        MSG_MULTI_NOTIF(1, MULTI_COUNTDOWN_BEGIN,                ANNCE_BEGIN,   NO_MSG,                                    CENTER_COUNTDOWN_BEGIN) \
        MSG_MULTI_NOTIF(1, MULTI_MINSTA_FINDAMMO,                ANNCE_NUM_10,  NO_MSG,                                    CENTER_MINSTA_FINDAMMO_FIRST) \
        MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER,              NO_MSG,        INFO_WEAPON_ACCORDEON_MURDER,              NO_MSG) \
@@@ -738,7 -746,7 +747,7 @@@ var float autocvar_notification_frag_ve
      f2race_time: mmssss of f2
      race_col: color of race time/position (i.e. good or bad)
      race_diff: show time difference between f2 and f3
-     kh_teams: show which teams still need players in keyhunt centerprint
+     missing_teams: show which teams still need players
      pass_key: find the keybind for "passing" or "dropping" in CTF game mode
      frag_ping: show the ping of a player
      frag_stats: show health/armor/ping of a player
@@@ -792,7 -800,7 +801,7 @@@ string arg_slot[NOTIF_MAX_ARGS]
      ARG_CASE(ARG_CS_SV,     "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,        "kh_teams",      notif_arg_kh_teams(f1, f2, f3, f4)) \
+     ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
      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)) \
@@@ -836,7 -844,7 +845,7 @@@ string notif_arg_frag_stats(float fheal
                return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(FALSE, fping));
  }
  
- string notif_arg_kh_teams(float f1, float f2, float f3, float f4)
+ string notif_arg_missing_teams(float f1, float f2, float f3, float f4)
  {
        return sprintf("%s%s%s%s",
                (f1 ?
@@@ -75,6 -75,7 +75,7 @@@ float autocvar_g_arena_maxspawned
  float autocvar_g_arena_point_leadlimit;
  float autocvar_g_arena_point_limit;
  float autocvar_g_arena_roundbased;
+ float autocvar_g_arena_round_timelimit;
  float autocvar_g_arena_warmup;
  float autocvar_g_assault;
  float autocvar_g_balance_armor_blockpercent;
@@@ -709,6 -710,8 +710,8 @@@ float autocvar_g_ca_point_leadlimit
  float autocvar_g_ca_point_limit;
  float autocvar_g_ca_round_timelimit;
  float autocvar_g_ca_spectate_enemies;
+ float autocvar_g_ca_teams;
+ float autocvar_g_ca_teams_override;
  float autocvar_g_ca_warmup;
  float autocvar_g_campaign;
  #define autocvar_g_campaign_forceteam cvar("g_campaign_forceteam")
@@@ -814,11 -817,15 +817,15 @@@ string autocvar_g_forced_team_pink
  string autocvar_g_forced_team_red;
  string autocvar_g_forced_team_yellow;
  float autocvar_g_freezetag_frozen_force;
+ float autocvar_g_freezetag_frozen_maxtime;
  float autocvar_g_freezetag_point_leadlimit;
  float autocvar_g_freezetag_point_limit;
  float autocvar_g_freezetag_revive_extra_size;
  float autocvar_g_freezetag_revive_speed;
  float autocvar_g_freezetag_revive_clearspeed;
+ float autocvar_g_freezetag_round_timelimit;
+ float autocvar_g_freezetag_teams;
+ float autocvar_g_freezetag_teams_override;
  float autocvar_g_freezetag_warmup;
  #define autocvar_g_friendlyfire cvar("g_friendlyfire")
  #define autocvar_g_friendlyfire_virtual cvar("g_friendlyfire_virtual")
@@@ -861,7 -868,6 +868,7 @@@ float autocvar_g_keyhunt_teams
  float autocvar_g_keyhunt_teams_override;
  float autocvar_g_lms_campcheck_damage;
  float autocvar_g_lms_campcheck_distance;
 +float autocvar_g_lms_extra_lives;
  float autocvar_g_lms_campcheck_interval;
  float autocvar_g_lms_join_anytime;
  float autocvar_g_lms_last_join;
@@@ -968,7 -974,6 +975,6 @@@ float autocvar_g_spawn_useallspawns
  float autocvar_g_spawnpoints_auto_move_out_of_solid;
  #define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
  float autocvar_g_spawnsound;
- float autocvar_g_start_delay;
  #define autocvar_g_start_weapon_laser cvar("g_start_weapon_laser")
  float autocvar_g_tdm_team_spawns;
  float autocvar_g_tdm_teams;
@@@ -277,7 -277,7 +277,7 @@@ entity SelectSpawnPoint (float anypoint
        else
        {
                float mindist;
-               if (arena_roundbased && !g_ca)
+               if (g_arena && arena_roundbased)
                        mindist = 800;
                else
                        mindist = 100;
@@@ -385,6 -385,24 +385,24 @@@ void PutObserverInServer (void
                WriteEntity(MSG_ONE, self);
        }
  
+       if(g_lms)
+       {
+               // Only if the player cannot play at all
+               if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
+                       self.frags = FRAGS_SPECTATOR;
+               else
+                       self.frags = FRAGS_LMS_LOSER;
+       }
+       else 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;
        MUTATOR_CALLHOOK(MakePlayerObserver);
  
        minstagib_stop_countdown(self);
        if not(g_ca)  // don't reset teams when moving a ca player to the spectators
                self.team = -1;  // move this as it is needed to log the player spectating in eventlog
  
 -      if(self.killcount != -666) {
 -              if(g_lms) {
 -                      if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0 && self.lms_spectate_warning != 2)
 -                              Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, self.netname);
 -                      else
 -                              Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_FORFEIT, self.netname);
 -              } else { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname); }
 +      if(self.killcount != -666)
 +      {
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname);
  
                if(self.just_joined == FALSE) {
                        LogTeamchange(self.playerid, -1, 4);
        self.pauseregen_finished = 0;
        self.damageforcescale = 0;
        self.death_time = 0;
+       self.respawn_flags = 0;
        self.respawn_time = 0;
+       self.stat_respawn_time = 0;
        self.alpha = 0;
        self.scale = 0;
        self.fade_time = 0;
        self.punchvector = '0 0 0';
        self.oldvelocity = self.velocity;
        self.fire_endtime = -1;
-       if(g_arena)
-       {
-               if(self.version_mismatch)
-               {
-                       self.frags = FRAGS_SPECTATOR;
-                       Spawnqueue_Unmark(self);
-                       Spawnqueue_Remove(self);
-               }
-               else
-               {
-                       self.frags = FRAGS_LMS_LOSER;
-                       Spawnqueue_Insert(self);
-               }
-       }
-       else if(g_ca)
-       {
-               if(self.caplayer)
-                       self.frags = FRAGS_LMS_LOSER;
-               else
-                       self.frags = FRAGS_SPECTATOR;
-       }
-       else 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;
  }
  
  .float model_randomizer;
@@@ -610,11 -603,7 +599,7 @@@ Called when a client spawns in the serv
  void PutClientInServer (void)
  {
        if(clienttype(self) == CLIENTTYPE_BOT)
-       {
                self.classname = "player";
-               if(g_ca)
-                       self.caplayer = 1;
-       }
        else if(clienttype(self) == CLIENTTYPE_REAL)
        {
                msg_entity = self;
                        self.classname = "observer";
        }
  
-       if((g_arena && !self.spawned) || (g_ca && !allowed_to_spawn))
-               self.classname = "observer";
+       MUTATOR_CALLHOOK(PutClientInServer);
  
        if(gameover)
                self.classname = "observer";
  
-       if(self.classname == "player" && (!g_ca || (g_ca && allowed_to_spawn))) {
+       if(self.classname == "player") {
                entity spot, oldself;
                float j;
  
                }
                self.damageforcescale = 2;
                self.death_time = 0;
+               self.respawn_flags = 0;
                self.respawn_time = 0;
+               self.stat_respawn_time = 0;
                self.scale = 0;
                self.fade_time = 0;
                self.pain_frame = 0;
                self.lastteleporttime = time; // prevent insane speeds due to changing origin
          self.hud = HUD_NORMAL;
  
-               if(g_arena)
-               {
-                       Spawnqueue_Remove(self);
-                       Spawnqueue_Mark(self);
-               }
-               else if(g_ca)
-                       self.caplayer = 1;
                self.event_damage = PlayerDamage;
  
                self.bot_attack = TRUE;
@@@ -1035,14 -1017,13 +1013,13 @@@ void ClientKill_Now_TeamChange(
        }
        else if(self.killindicator_teamchange == -2)
        {
-               if(g_ca)
-                       self.caplayer = 0;
                if(blockSpectators)
                        Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
                PutObserverInServer();
        }
        else
                SV_ChangeTeam(self.killindicator_teamchange - 1);
+       self.killindicator_teamchange = 0;
  }
  
  void ClientKill_Now()
@@@ -1218,19 -1199,11 +1195,11 @@@ void ClientKill_TeamChange (float targe
  
  void ClientKill (void)
  {
-       if (gameover)
-               return;
+       if(gameover) return;
+       if(self.player_blocked) return;
+       if(self.freezetag_frozen) return;
  
-       if((g_arena || g_ca) && ((champion && champion.classname == "player" && player_count > 1) || player_count == 1)) // don't allow a kill in this case either
-       {
-               // do nothing
-       }
-     else if(self.freezetag_frozen)
-     {
-         // do nothing
-     }
-       else
-               ClientKill_TeamChange(0);
+       ClientKill_TeamChange(0);
  }
  
  void CTS_ClientKill (entity e) // silent version of ClientKill, used when player finishes a CTS run. Useful to prevent cheating by running back to the start line and starting out with more speed
@@@ -1390,7 -1363,7 +1359,7 @@@ void ClientConnect (void
  
        JoinBestTeam(self, FALSE, FALSE); // if the team number is valid, keep it
  
 -      if((autocvar_sv_spectate == 1 && !g_lms) || autocvar_g_campaign || self.team_forced < 0) {
 +      if((autocvar_sv_spectate == 1) || autocvar_g_campaign || self.team_forced < 0) {
                self.classname = "observer";
        } else {
                if(teamplay)
        else
                stuffcmd(self, "set _teams_available 0\n");
  
-       if(g_arena || g_ca)
-       {
-               self.classname = "observer";
-               if(g_arena)
-                       Spawnqueue_Insert(self);
-       }
        attach_entcs();
  
        bot_relinkplayerlist();
                        stuffcmd(self, "cl_cmd settemp chase_active 1\n");
        }
  
 -      if(g_lms)
 -      {
 -              if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
 -              {
 -                      PlayerScore_Add(self, SP_LMS_RANK, 666);
 -                      self.frags = FRAGS_SPECTATOR;
 -              }
 -      }
 -
        if(!sv_foginterval && world.fog != "")
                stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
  
        CSQCMODEL_AUTOINIT();
  
        self.model_randomizer = random();
-     
-     if(clienttype(self) != CLIENTTYPE_REAL)
-         return;
-         
-     sv_notice_join();
-     
-     MUTATOR_CALLHOOK(ClientConnect);
+       if(clienttype(self) == CLIENTTYPE_REAL)
+               sv_notice_join();
+       MUTATOR_CALLHOOK(ClientConnect);
  }
  /*
  =============
@@@ -1604,12 -1577,6 +1564,6 @@@ void ClientDisconnect (void
  
        bot_relinkplayerlist();
  
-       if(g_arena)
-       {
-               Spawnqueue_Unmark(self);
-               Spawnqueue_Remove(self);
-       }
        accuracy_free(self);
        ClientData_Detach();
        PlayerScore_Detach(self);
@@@ -1967,7 -1934,7 +1921,7 @@@ void player_regen (void
        limita = limita * limit_mod;
        //limitf = limitf * limit_mod;
  
-       if(g_lms || g_ca)
+       if(g_ca)
                rot_mod = 0;
  
        if (!g_minstagib && !g_ca && (!g_lms || autocvar_g_lms_regenerate))
@@@ -2092,7 -2059,6 +2046,6 @@@ void SpectateCopy(entity spectatee) 
        self.dmg_inflictor = spectatee.dmg_inflictor;
        self.v_angle = spectatee.v_angle;
        self.angles = spectatee.v_angle;
-       self.stat_respawn_time = spectatee.stat_respawn_time;
        if(!self.BUTTON_USE)
                self.fixangle = TRUE;
        setorigin(self, spectatee.origin);
@@@ -2243,6 -2209,8 +2196,8 @@@ void ShowRespawnCountdown(
  
  void LeaveSpectatorMode()
  {
+       if(self.caplayer)
+               return;
        if(nJoinAllowed(self))
        {
                if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
  
                        if(IS_PLAYER(self)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname); }
                }
-               else if not(g_ca && self.caplayer) { stuffcmd(self, "menu_showteamselect\n"); }
+               else
+                       stuffcmd(self, "menu_showteamselect\n");
        }
        else
        {
@@@ -2301,8 -2270,9 +2257,9 @@@ float nJoinAllowed(entity ignore) 
                return maxclients - totalClients;
  
        float currentlyPlaying = 0;
-       FOR_EACH_REALPLAYER(e)
-               currentlyPlaying += 1;
+       FOR_EACH_REALCLIENT(e)
+               if(e.classname == "player" || e.caplayer == 1)
+                       currentlyPlaying += 1;
  
        if(currentlyPlaying < autocvar_g_maxplayers)
                return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
@@@ -2469,14 -2439,10 +2426,10 @@@ void PlayerPreThink (void
        WarpZone_PlayerPhysics_FixVAngle();
  
        self.stat_game_starttime = game_starttime;
+       self.stat_round_starttime = round_starttime;
        self.stat_allow_oldnexbeam = autocvar_g_allow_oldnexbeam;
        self.stat_leadlimit = autocvar_leadlimit;
  
-       if(g_arena || (g_ca && !allowed_to_spawn))
-               self.stat_respawn_time = 0;
-       else
-               self.stat_respawn_time = self.respawn_time;
        if(frametime)
        {
                // physics frames: update anticheat stuff
  
                if (self.deadflag != DEAD_NO)
                {
-                       float button_pressed, force_respawn;
                        if(self.personal && g_race_qualifying)
                        {
                                if(time > self.respawn_time)
                                {
                                        self.respawn_time = time + 1; // only retry once a second
+                                       self.stat_respawn_time = self.respawn_time;
                                        respawn();
                                        self.impulse = 141;
                                }
                        }
                        else
                        {
+                               float button_pressed;
                                if(frametime)
                                        player_anim();
                                button_pressed = (self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE);
-                               force_respawn = (g_ca || g_cts || autocvar_g_forced_respawn);
 -
++                              
                                if (self.deadflag == DEAD_DYING)
                                {
-                                       if(force_respawn)
+                                       if(self.respawn_flags & RESPAWN_FORCE)
                                                self.deadflag = DEAD_RESPAWNING;
                                        else if(!button_pressed)
                                                self.deadflag = DEAD_DEAD;
                                                respawn();
                                        }
                                }
                                ShowRespawnCountdown();
+                               if(self.respawn_flags & RESPAWN_SILENT)
+                                       self.stat_respawn_time = 0;
+                               else
+                                       self.stat_respawn_time = self.respawn_time;
                        }
  
                        // if respawning, invert stat_respawn_time to indicate this, the client translates it
                        return;
                }
  
 -              if(g_lms && !self.deadflag && autocvar_g_lms_campcheck_interval)
 -              {
 -                      vector dist;
 -
 -                      // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
 -                      dist = self.prevorigin - self.origin;
 -                      dist_z = 0;
 -                      self.lms_traveled_distance += fabs(vlen(dist));
 -
 -                      if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime))
 -                      {
 -                              self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval*2;
 -                              self.lms_traveled_distance = 0;
 -                      }
 -
 -                      if(time > self.lms_nextcheck)
 -                      {
 -                              //sprint(self, "distance: ", ftos(self.lms_traveled_distance), "\n");
 -                              if(self.lms_traveled_distance < autocvar_g_lms_campcheck_distance)
 -                              {
 -                                      Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_CAMPCHECK);
 -                                      // FIXME KadaverJack: gibbing player here causes playermodel to bounce around, instead of eye.md3
 -                                      // I wasn't able to find out WHY that happens, so I put a workaround in place that shall prevent players from being gibbed :(
 -                                      if(self.vehicle)
 -                                              Damage(self.vehicle, self, self, autocvar_g_lms_campcheck_damage * 2, DEATH_CAMP, self.vehicle.origin, '0 0 0');
 -                                      else
 -                                              Damage(self, self, self, bound(0, autocvar_g_lms_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP, self.origin, '0 0 0');
 -                              }
 -                              self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval;
 -                              self.lms_traveled_distance = 0;
 -                      }
 -              }
 -
                self.prevorigin = self.origin;
  
                float do_crouch = self.BUTTON_CROUCH;
@@@ -301,8 -301,8 +301,6 @@@ float W_IsWeaponThrowable(float w
                return 0;
        if (g_weaponarena)
                return 0;
-       if (g_ca)
 -      if (g_lms)
--              return 0;
        if (g_cts)
                return 0;
        if (g_nexball && w == WEP_GRENADE_LAUNCHER)
@@@ -352,7 -352,19 +350,19 @@@ void W_ThrowWeapon(vector velo, vector 
        Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
  }
  
- // Bringed back weapon frame
+ float forbidWeaponUse()
+ {
+       if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
+               return 1;
+       if(round_handler_IsActive() && !round_handler_IsRoundStarted())
+               return 1;
+       if(self.player_blocked)
+               return 1;
+       if(self.freezetag_frozen)
+               return 1;
+       return 0;
+ }
  void W_WeaponFrame()
  {
        vector fo, ri, up;
        if (frametime)
                self.weapon_frametime = frametime;
  
-       if(((arena_roundbased || g_ca || g_freezetag) && time < warmup) || ((time < game_starttime) && !autocvar_sv_ready_restart_after_countdown))
-               return;
-       if(self.freezetag_frozen == 1)
-               return;
        if (!self.weaponentity || self.health < 1)
                return; // Dead player can't use weapons and injure impulse commands
  
+       if(forbidWeaponUse())
+       if(self.weaponentity.state != WS_CLEAR)
+       {
+               w_ready();
+               return;
+       }
        if(!self.switchweapon)
        {
                self.weapon = 0;
diff --combined qcsrc/server/defs.qh
@@@ -46,6 -46,9 +46,6 @@@ entity        activator
  float player_count;
  float currentbots;
  float bots_would_leave;
 -float lms_lowest_lives;
 -float lms_next_place;
 -float LMS_NewPlayerLives();
  
  void UpdateFrags(entity player, float f);
  .float totalfrags;
@@@ -101,6 -104,7 +101,7 @@@ float server_is_dedicated
  //.float cnt2;
  
  .float play_time;
+ .float respawn_flags;
  .float respawn_time;
  .float death_time;
  .float fade_time;
@@@ -443,8 -447,10 +444,10 @@@ string cvar_changes
  string cvar_purechanges;
  float cvar_purechanges_count;
  
- float game_starttime; //point in time when the countdown is over
+ float game_starttime; //point in time when the countdown to game start is over
+ float round_starttime; //point in time when the countdown to round start is over
  .float stat_game_starttime;
+ .float stat_round_starttime;
  
  .float stat_sv_airaccel_qw;
  .float stat_sv_airstrafeaccel_qw;
@@@ -576,8 -582,6 +579,6 @@@ string deathmessage
  .void (float act_state) setactive;
  .entity realowner;
  
- float allowed_to_spawn; // boolean variable used by the clan arena code to determine if a player can spawn (after the round has ended)
  float serverflags;
  
  .float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
  .float player_blocked;
  
  .float freezetag_frozen;
- .float freezetag_revive_progress;
  
  .entity muzzle_flash;
  .float misc_bulletcounter;    // replaces uzi & hlac bullet counter.
diff --combined qcsrc/server/g_damage.qc
@@@ -120,10 -120,6 +120,6 @@@ void GiveFrags (entity attacker, entit
  
        PlayerScore_Add(targ, SP_DEATHS, 1);
  
-       if(g_arena || g_ca)
-               if(autocvar_g_arena_roundbased)
-                       return;
        if(targ != attacker) // not for suicides
        if(g_weaponarena_random)
        {
        else
        {
                self = oldself;
 -              if(g_lms)
 -              {
 -                      // remove a life
 -                      float tl;
 -                      tl = PlayerScore_Add(targ, SP_LMS_LIVES, -1);
 -                      if(tl < lms_lowest_lives)
 -                              lms_lowest_lives = tl;
 -                      if(tl <= 0)
 -                      {
 -                              if(!lms_next_place)
 -                                      lms_next_place = player_count;
 -                              else
 -                                      lms_next_place = min(lms_next_place, player_count);
 -                              PlayerScore_Add(targ, SP_LMS_RANK, lms_next_place); // won't ever spawn again
 -                              --lms_next_place;
 -                      }
 -                      f = 0;
 -              }
        }
  
        attacker.totalfrags += f;
@@@ -682,6 -696,14 +678,6 @@@ void Damage (entity targ, entity inflic
                }
  
                if(targ.classname == "player")
 -              if(attacker.classname == "player")
 -              if(attacker != targ)
 -              {
 -                      targ.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
 -                      attacker.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
 -              }
 -
 -              if(targ.classname == "player")
                if (g_minstagib)
                {
                        if ((deathtype == DEATH_FALL)  ||
diff --combined qcsrc/server/g_world.qc
@@@ -256,16 -256,17 +256,17 @@@ void cvar_changes_init(
                BADCVAR("g_arena");
                BADCVAR("g_assault");
                BADCVAR("g_ca");
+               BADCVAR("g_ca_teams");
                BADCVAR("g_ctf");
                BADCVAR("g_cts");
                BADCVAR("g_dm");
                BADCVAR("g_domination");
                BADCVAR("g_domination_default_teams");
                BADCVAR("g_freezetag");
+               BADCVAR("g_freezetag_teams");
                BADCVAR("g_keepaway");
                BADCVAR("g_keyhunt");
                BADCVAR("g_keyhunt_teams");
-               BADCVAR("g_keyhunt_teams");
                BADCVAR("g_lms");
                BADCVAR("g_nexball");
                BADCVAR("g_onslaught");
                BADCVAR("g_balance_teams_scorefactor");
                BADCVAR("g_ban_sync_trusted_servers");
                BADCVAR("g_ban_sync_uri");
+               BADCVAR("g_ca_teams_override");
                BADCVAR("g_ctf_ignore_frags");
                BADCVAR("g_domination_point_limit");
+               BADCVAR("g_freezetag_teams_override");
                BADCVAR("g_friendlyfire");
                BADCVAR("g_fullbrightitems");
                BADCVAR("g_fullbrightplayers");
@@@ -581,8 -584,6 +584,6 @@@ void spawnfunc_worldspawn (void
  
        compressShortVector_init();
  
-       allowed_to_spawn = TRUE;
        entity head;
        head = nextent(world);
        maxclients = 0;
        addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon);
        addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon);
        addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime);
+       addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime);
        addstat(STAT_ALLOW_OLDNEXBEAM, AS_INT, stat_allow_oldnexbeam);
        Nagger_Init();
  
  
        addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
  
-       if(g_ca || g_freezetag)
-       {
-               addstat(STAT_REDALIVE, AS_INT, redalive_stat);
-               addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
-               addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
-               addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
-       }
-       if(g_freezetag)
-       {
-               addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
-               addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
-       }
        // g_movementspeed hack
        addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
        addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
@@@ -1478,7 -1467,7 +1467,7 @@@ void DumpStats(float final
                {
                        s = strcat(":player:see-labels:", GetPlayerScoreString(other, 0), ":");
                        s = strcat(s, ftos(rint(time - other.jointime)), ":");
-                       if(other.classname == "player" || g_arena || g_ca || g_lms)
+                       if(other.classname == "player" || g_arena || other.caplayer == 1 || g_lms)
                                s = strcat(s, ftos(other.team), ":");
                        else
                                s = strcat(s, "spectator:");
@@@ -1769,6 -1758,24 +1758,6 @@@ float WinningCondition_Onslaught(
        return WINNING_NO;
  }
  
 -float LMS_NewPlayerLives()
 -{
 -      float fl;
 -      fl = autocvar_fraglimit;
 -      if(fl == 0)
 -              fl = 999;
 -
 -      // first player has left the game for dying too much? Nobody else can get in.
 -      if(lms_lowest_lives < 1)
 -              return 0;
 -
 -      if(!autocvar_g_lms_join_anytime)
 -              if(lms_lowest_lives < fl - autocvar_g_lms_last_join)
 -                      return 0;
 -
 -      return bound(1, lms_lowest_lives, fl);
 -}
 -
  // Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
  // they win. Otherwise the defending team wins once the timelimit passes.
  void assault_new_round();
@@@ -750,7 -750,7 +750,7 @@@ void readplayerstartcvars(
        s = cvar_string("g_weaponarena");
        if (s == "0" || s == "")
        {
 -              if(g_lms || g_ca)
 +              if(g_ca)
                        s = "most";
        }
  
                g_pinata = 0; // incompatible
                g_weapon_stay = 0; // incompatible
                WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
 -              if(!(g_lms || g_ca))
 +              if(!g_ca)
                        start_items |= IT_UNLIMITED_AMMO;
        }
        else if (g_minstagib)
        }
        else
        {
 -              if(g_lms || g_ca)
 +              if(g_ca)
                {
                        start_ammo_shells = cvar("g_lms_start_ammo_shells");
                        start_ammo_nails = cvar("g_lms_start_ammo_nails");
                }
        }
  
 -      if (g_lms || g_ca)
 +      if (g_ca)
        {
                start_health = cvar("g_lms_start_health");
                start_armorvalue = cvar("g_lms_start_armor");
@@@ -1140,8 -1140,8 +1140,8 @@@ void readlevelcvars(void
      if(!g_weapon_stay)
          g_weapon_stay = cvar("g_weapon_stay");
  
-       if not(inWarmupStage && !g_ca)
-               game_starttime = cvar("g_start_delay");
+       if not(inWarmupStage)
+               game_starttime = time + cvar("g_start_delay");
  
        readplayerstartcvars();
  }
index cb8f63b,0000000..681a1d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,249 -1,0 +1,249 @@@
-       MUTATOR_HOOK(PlayerClearScore, lms_KeepScore, CBC_ORDER_ANY);
 +// main functions
 +float LMS_NewPlayerLives()
 +{
 +      float fl;
 +      fl = autocvar_fraglimit;
 +      if(fl == 0)
 +              fl = 999;
 +
 +      // first player has left the game for dying too much? Nobody else can get in.
 +      if(lms_lowest_lives < 1)
 +              return 0;
 +
 +      if(!autocvar_g_lms_join_anytime)
 +              if(lms_lowest_lives < fl - autocvar_g_lms_last_join)
 +                      return 0;
 +
 +      return bound(1, lms_lowest_lives, fl);
 +}
 +
 +// mutator hooks
 +MUTATOR_HOOKFUNCTION(lms_PlayerSpawn)
 +{
 +      if(IS_PLAYER(self))
 +      if(restart_mapalreadyrestarted || (time < game_starttime))
 +              PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
 +              
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_RemovePlayer)
 +{
 +      // Only if the player cannot play at all
 +      if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
 +              self.frags = FRAGS_SPECTATOR;
 +      else
 +              self.frags = FRAGS_LMS_LOSER;
 +              
 +      if(self.killcount != -666)
 +              if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0 && self.lms_spectate_warning != 2)
 +                      Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, self.netname);
 +              else
 +                      Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_FORFEIT, self.netname);
 +              
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_ClientConnect)
 +{
 +      self.classname = "player";
 +      campaign_bots_may_start = 1;
 +      
 +      if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
 +      {
 +              PlayerScore_Add(self, SP_LMS_RANK, 666);
 +              self.frags = FRAGS_SPECTATOR;
 +      }
 +                      
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_PlayerThink)
 +{
 +      if(self.deadflag == DEAD_DYING)
 +              self.deadflag = DEAD_RESPAWNING;
 +              
 +      if not(self.deadflag)
 +      if(autocvar_g_lms_campcheck_interval)
 +      {
 +              vector dist;
 +
 +              // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
 +              dist = self.prevorigin - self.origin;
 +              dist_z = 0;
 +              self.lms_traveled_distance += fabs(vlen(dist));
 +
 +              if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime))
 +              {
 +                      self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval*2;
 +                      self.lms_traveled_distance = 0;
 +              }
 +
 +              if(time > self.lms_nextcheck)
 +              {
 +                      if(self.lms_traveled_distance < autocvar_g_lms_campcheck_distance)
 +                      {
 +                              Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_CAMPCHECK);
 +                              if(self.vehicle)
 +                                      Damage(self.vehicle, self, self, autocvar_g_lms_campcheck_damage * 2, DEATH_CAMP, self.vehicle.origin, '0 0 0');
 +                              else
 +                                      Damage(self, self, self, bound(0, autocvar_g_lms_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP, self.origin, '0 0 0');
 +                      }
 +                      self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval;
 +                      self.lms_traveled_distance = 0;
 +              }
 +      }
 +              
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_PlayerDamage)
 +{
 +      if(IS_PLAYER(frag_target))
 +      if(IS_PLAYER(frag_attacker))
 +      if(frag_attacker != frag_target)
 +      {
 +              frag_target.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
 +              frag_attacker.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
 +      }
 +              
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_ForbidThrowing)
 +{
 +      // forbode!
 +      return TRUE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_GiveFragsForKill)
 +{
 +      // remove a life
 +      float tl;
 +      tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1);
 +      if(tl < lms_lowest_lives)
 +              lms_lowest_lives = tl;
 +      if(tl <= 0)
 +      {
 +              if(!lms_next_place)
 +                      lms_next_place = player_count;
 +              else
 +                      lms_next_place = min(lms_next_place, player_count);
 +              PlayerScore_Add(frag_target, SP_LMS_RANK, lms_next_place); // won't ever spawn again
 +              --lms_next_place;
 +      }
 +      frag_score = 0;
 +              
 +      return TRUE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_SetStartItems)
 +{
 +      start_items &~= IT_UNLIMITED_AMMO;
 +      start_ammo_shells = cvar("g_lms_start_ammo_shells");
 +      start_ammo_nails = cvar("g_lms_start_ammo_nails");
 +      start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
 +      start_ammo_cells = cvar("g_lms_start_ammo_cells");
 +      start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
 +      start_health = cvar("g_lms_start_health");
 +      start_armorvalue = cvar("g_lms_start_armor");
 +
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_KeepScore)
 +{
 +      // don't clear player score
 +      return TRUE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_FilterItem)
 +{
 +      if(autocvar_g_lms_extra_lives)
 +      if(self.classname == "item_health_mega")
 +      {
 +              self.max_health = 1;
 +              return FALSE;
 +      }
 +      
 +      return TRUE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_ItemTouch)
 +{
 +      // give extra lives for mega health
 +      if(self.items & IT_HEALTH)
 +      {
 +              Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_EXTRALIVES);
 +              PlayerScore_Add(other, SP_LMS_LIVES, autocvar_g_lms_extra_lives);
 +      }
 +      
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(lms_BotSpawn)
 +{
 +      // temporary hack to give bots lives
 +      if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
 +      {
 +              PlayerScore_Add(self, SP_LMS_RANK, 666);
 +              self.frags = FRAGS_SPECTATOR;
 +      }
 +      
 +      return FALSE;
 +}
 +
 +// scoreboard stuff
 +void lms_ScoreRules()
 +{
 +      ScoreRules_basics(0, 0, 0, FALSE);
 +      ScoreInfo_SetLabel_PlayerScore(SP_LMS_LIVES,    "lives",     SFL_SORT_PRIO_SECONDARY);
 +      ScoreInfo_SetLabel_PlayerScore(SP_LMS_RANK,     "rank",      SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE);
 +      ScoreRules_basics_end();
 +}
 +
 +void lms_Initialize()
 +{
 +      lms_lowest_lives = 9999;
 +      lms_next_place = 0;
 +      
 +      lms_ScoreRules();
 +}
 +
 +MUTATOR_DEFINITION(gamemode_lms)
 +{
 +      MUTATOR_HOOK(PlayerSpawn, lms_PlayerSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MakePlayerObserver, lms_RemovePlayer, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(ClientConnect, lms_ClientConnect, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerPreThink, lms_PlayerThink, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerDamage_Calculate, lms_PlayerDamage, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(ForbidThrowCurrentWeapon, lms_ForbidThrowing, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(GiveFragsForKill, lms_GiveFragsForKill, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SetStartItems, lms_SetStartItems, CBC_ORDER_ANY);
++      MUTATOR_HOOK(ForbidPlayerScore_Clear, lms_KeepScore, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(FilterItem, lms_FilterItem, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(ItemTouch, lms_ItemTouch, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(HavocBot_ChooseRule, lms_BotSpawn, CBC_ORDER_ANY);
 +
 +      MUTATOR_ONADD
 +      {
 +              if(time > 1) // game loads at time 1
 +                      error("This is a game type and it cannot be added at runtime.");
 +              lms_Initialize();
 +      }
 +
 +      MUTATOR_ONROLLBACK_OR_REMOVE
 +      {
 +              // we actually cannot roll back lms_Initialize here
 +              // BUT: we don't need to! If this gets called, adding always
 +              // succeeds.
 +      }
 +
 +      MUTATOR_ONREMOVE
 +      {
 +              print("This is a game type and it cannot be removed at runtime.");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
@@@ -1,3 -1,5 +1,5 @@@
+ MUTATOR_DECLARATION(gamemode_arena);
+ MUTATOR_DECLARATION(gamemode_ca);
  MUTATOR_DECLARATION(gamemode_keyhunt);
  MUTATOR_DECLARATION(gamemode_freezetag);
  MUTATOR_DECLARATION(gamemode_keepaway);
@@@ -5,7 -7,6 +7,7 @@@ MUTATOR_DECLARATION(gamemode_ctf)
  MUTATOR_DECLARATION(gamemode_nexball);
  MUTATOR_DECLARATION(gamemode_onslaught);
  MUTATOR_DECLARATION(gamemode_domination);
 +MUTATOR_DECLARATION(gamemode_lms);
  
  MUTATOR_DECLARATION(mutator_dodging);
  MUTATOR_DECLARATION(mutator_invincibleprojectiles);
diff --combined qcsrc/server/progs.src
@@@ -35,12 -35,13 +35,14 @@@ defs.qh            // Should rename this, it has 
  
  mutators/base.qh
  mutators/mutators.qh
+ mutators/gamemode_arena.qh
+ mutators/gamemode_ca.qh
  mutators/gamemode_ctf.qh
  mutators/gamemode_domination.qh
  mutators/gamemode_keyhunt.qh // TODO fix this
  mutators/gamemode_keepaway.qh
  mutators/gamemode_nexball.qh 
 +mutators/gamemode_lms.qh
  mutators/mutator_dodging.qh
  
  //// tZork Turrets ////
@@@ -86,6 -87,8 +88,8 @@@ antilag.q
  
  playerdemo.qh
  
+ round_handler.qh
  // singleplayer stuff
  item_key.qh
  secret.qh
@@@ -103,7 -106,6 +107,6 @@@ g_subs.q
  g_tetris.qc
  
  //runematch.qc
- arena.qc
  
  g_violence.qc
  g_damage.qc
@@@ -212,9 -214,13 +215,13 @@@ anticheat.q
  cheats.qc
  playerstats.qc
  
+ round_handler.qc
  ../common/explosion_equation.qc
  
  mutators/base.qc
+ mutators/gamemode_arena.qc
+ mutators/gamemode_ca.qc
  mutators/gamemode_ctf.qc
  mutators/gamemode_domination.qc
  mutators/gamemode_freezetag.qc
@@@ -222,7 -228,6 +229,7 @@@ mutators/gamemode_keyhunt.q
  mutators/gamemode_keepaway.qc
  mutators/gamemode_nexball.qc
  mutators/gamemode_onslaught.qc
 +mutators/gamemode_lms.qc
  mutators/mutator_invincibleproj.qc
  mutators/mutator_new_toys.qc
  mutators/mutator_nix.qc
diff --combined qcsrc/server/scores.qc
@@@ -256,8 -256,8 +256,8 @@@ float PlayerScore_Clear(entity player
        if(teamscores_entities_count)
                return 0;
  
-       if(MUTATOR_CALLHOOK(PlayerClearScore)) return 0;
-       if(g_arena || g_ca) return 0;
+       if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0;
 -      if(g_lms) return 0;
++
        if(g_cts) return 0; // in CTS, you don't lose score by observing
        if(g_race && g_race_qualifying) return 0; // in qualifying, you don't lose score by observing
  
@@@ -527,12 -527,12 +527,12 @@@ void WinningConditionHelper(
                                s = strcat(s, ":human");
                        else
                                s = strcat(s, ":bot");
-                       if(p.classname != "player" && !g_arena && !g_ca && !g_lms)
+                       if(p.classname != "player" && !g_arena && p.caplayer != 1 && !g_lms)
                                s = strcat(s, ":spectator");
                }
                else
                {
-                       if(p.classname == "player" || g_arena || g_ca || g_lms)
+                       if(p.classname == "player" || g_arena || p.caplayer == 1 || g_lms)
                                s = GetPlayerScoreString(p, 2);
                        else
                                s = "-666";
diff --combined qcsrc/server/teamplay.qc
@@@ -112,17 -112,16 +112,14 @@@ void InitGameplayMode(
                leadlimit_override = 0; // not supported by LMS
                if(fraglimit_override == 0)
                        fraglimit_override = -1;
 -              lms_lowest_lives = 9999;
 -              lms_next_place = 0;
 -              ScoreRules_lms();
 +              MUTATOR_ADD(gamemode_lms);
        }
  
        if(g_arena)
        {
                fraglimit_override = autocvar_g_arena_point_limit;
                leadlimit_override = autocvar_g_arena_point_leadlimit;
-               maxspawned = autocvar_g_arena_maxspawned;
-               if(maxspawned < 2)
-                       maxspawned = 2;
-               arena_roundbased = autocvar_g_arena_roundbased;
+               MUTATOR_ADD(gamemode_arena);
        }
  
        if(g_ca)
                ActivateTeamplay();
                fraglimit_override = autocvar_g_ca_point_limit;
                leadlimit_override = autocvar_g_ca_point_leadlimit;
-               precache_sound("ctf/red_capture.wav");
-               precache_sound("ctf/blue_capture.wav");
+               MUTATOR_ADD(gamemode_ca);
        }
        if(g_keyhunt)
        {
                ActivateTeamplay();