Merge remote-tracking branch 'origin/master' into terencehill/ca_arena_mutators
authorSamual Lenks <samual@xonotic.org>
Tue, 7 May 2013 05:13:01 +0000 (01:13 -0400)
committerSamual Lenks <samual@xonotic.org>
Tue, 7 May 2013 05:13:01 +0000 (01:13 -0400)
14 files changed:
1  2 
qcsrc/client/View.qc
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/hud_config.qc
qcsrc/common/constants.qh
qcsrc/common/notifications.qh
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/command/vote.qc
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src

diff --combined qcsrc/client/View.qc
@@@ -384,6 -384,7 +384,7 @@@ float checkfail[16]
  #define BUTTON_3 4
  #define BUTTON_4 8
  float cl_notice_run();
+ float prev_myteam;
  void CSQC_UpdateView(float w, float h)
  {
        entity e;
  #endif
                myteam = GetPlayerColor(player_localentnum - 1);
  
+       if(myteam != prev_myteam)
+       {
+               myteamcolors = colormapPaletteColor(myteam, 1);
+               for(i = 0; i < HUD_PANEL_NUM; ++i)
+                       hud_panel[i].update_time = time;
+               prev_myteam = myteam;
+       }
        ticrate = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE);
  
        float is_dead = (getstati(STAT_HEALTH) <= 0);
                        }
                }
        }
 -      
 -      if(autocvar_hud_damage)
 +
 +      if(autocvar_hud_damage && !getstati(STAT_FROZEN))
        {
                splash_size_x = max(vid_conwidth, vid_conheight);
                splash_size_y = max(vid_conwidth, vid_conheight);
                        if(getstatf(STAT_REVIVE_PROGRESS))
                        {
                                DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
 -                              drawstring_aspect(eY * 0.64 * vid_conheight, "Revival progress", eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
 +                              drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
                        }
                }
  
@@@ -253,9 -253,7 +253,9 @@@ float autocvar_hud_panel_healtharmor_te
  float autocvar_hud_panel_infomessages;
  float autocvar_hud_panel_infomessages_flip;
  float autocvar_hud_panel_modicons;
 +float autocvar_hud_panel_modicons_ca_layout;
  float autocvar_hud_panel_modicons_dom_layout;
 +float autocvar_hud_panel_modicons_freezetag_layout;
  float autocvar_hud_panel_notify;
  float autocvar_hud_panel_notify_fadetime;
  float autocvar_hud_panel_notify_flip;
@@@ -304,6 -302,7 +304,7 @@@ float autocvar_hud_panel_score
  float autocvar_hud_panel_score_rankings;
  float autocvar_hud_panel_timer;
  float autocvar_hud_panel_timer_increment;
+ float autocvar_hud_panel_update_interval;
  float autocvar_hud_panel_vote;
  float autocvar_hud_panel_vote_alreadyvoted_alpha;
  string autocvar_hud_panel_vote_bg_alpha;
diff --combined qcsrc/client/hud.qc
@@@ -487,11 -487,9 +487,9 @@@ void HUD_Weapons(void
                        return;
                }
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_WEAPONS;
  
        // update generic hud functions
-       HUD_Panel_UpdateCvars(weapons);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        draw_beginBoldFont();
@@@ -950,10 -948,8 +948,8 @@@ void HUD_Ammo(void
                if(!autocvar_hud_panel_ammo) return;
                if(spectatee_status == -1) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_AMMO;
  
-       HUD_Panel_UpdateCvars(ammo);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        draw_beginBoldFont();
@@@ -1154,14 -1150,12 +1150,12 @@@ void HUD_Powerups(void
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_POWERUPS;
                strength_time = 15;
                shield_time = 27;
                superweapons_time = 13;
        }
  
-       HUD_Panel_UpdateCvars(powerups);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        draw_beginBoldFont();
@@@ -1397,14 -1391,12 +1391,12 @@@ void HUD_HealthArmor(void
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_HEALTHARMOR;
                health = 150;
                armor = 75;
                fuel = 20;
        }
  
-       HUD_Panel_UpdateCvars(healtharmor);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@@ -1656,10 -1648,8 +1648,8 @@@ void HUD_Notify(void
        {
                if(!autocvar_hud_panel_notify) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_NOTIFY;
  
-       HUD_Panel_UpdateCvars(notify);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@@ -1795,10 -1785,8 +1785,8 @@@ void HUD_Timer(void
        {
                if(!autocvar_hud_panel_timer) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_TIMER;
  
-       HUD_Panel_UpdateCvars(timer);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        draw_beginBoldFont();
@@@ -1865,12 -1853,10 +1853,10 @@@ void HUD_Radar(void
                        if (autocvar_hud_panel_radar != 2 && !teamplay) return;
                }
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_RADAR;
  
-       HUD_Panel_UpdateCvars(radar);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
-       
        float f = 0;
  
        if (hud_panel_radar_maximized && !autocvar__hud_configure)
@@@ -2159,10 -2145,8 +2145,8 @@@ void HUD_Score(void
                if(!autocvar_hud_panel_score) return;
                if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_SCORE;
  
-       HUD_Panel_UpdateCvars(score);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@@ -2356,10 -2340,8 +2340,8 @@@ void HUD_RaceTimer (void
                if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
                if(spectatee_status == -1) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_RACETIMER;
  
-       HUD_Panel_UpdateCvars(racetimer);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        draw_beginBoldFont();
@@@ -2508,7 -2490,7 +2490,7 @@@ float vote_prev; // previous state of v
  float vote_alpha;
  float vote_change; // "time" when vote_active changed
  
- void HUD_VoteWindow(void) 
+ void HUD_Vote(void)
  {
        if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS)))
        {
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_VOTE;
                vote_yescount = 3;
                vote_nocount = 2;
                vote_needed = 4;
        if(!vote_alpha)
                return;
  
-       HUD_Panel_UpdateCvars(vote);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        if(uid2name_dialog)
  
  float mod_active; // is there any active mod icon?
  
 -// Clan Arena HUD modicons
 -void HUD_Mod_CA(vector pos, vector mySize)
 +void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, float layout, float i)
  {
 -      mod_active = 1; // CA should never hide the mod icons panel
 -      float redalive, bluealive;
 -      redalive = getstati(STAT_REDALIVE);
 -      bluealive = getstati(STAT_BLUEALIVE);
 +      float stat;
 +      string pic;
 +      vector color;
 +#ifdef GMQCC
 +      stat = -1;
 +      pic = "";
 +      color = '0 0 0';
 +#endif
 +      switch(i)
 +      {
 +              case 0:
 +                      stat = getstati(STAT_REDALIVE);
 +                      pic = "player_red.tga";
 +                      color = '1 0 0';
 +                      break;
 +              case 1:
 +                      stat = getstati(STAT_BLUEALIVE);
 +                      pic = "player_blue.tga";
 +                      color = '0 0 1';
 +                      break;
 +              case 2:
 +                      stat = getstati(STAT_YELLOWALIVE);
 +                      pic = "player_yellow.tga";
 +                      color = '1 1 0';
 +                      break;
 +              default:
 +              case 3:
 +                      stat = getstati(STAT_PINKALIVE);
 +                      pic = "player_pink.tga";
 +                      color = '1 0 1';
 +                      break;
 +      }
  
 -      vector redpos, bluepos;
 -      if(mySize_x > mySize_y)
 +      if(mySize_x/mySize_y > aspect_ratio)
        {
 -              redpos = pos;
 -              bluepos = pos + eY * 0.5 * mySize_y;
 -              drawpic_aspect_skin(redpos, "player_red.tga", 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 -              drawstring_aspect(redpos + eX * 0.5 * mySize_x, ftos(redalive), 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 -              drawpic_aspect_skin(bluepos, "player_blue.tga", 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 -              drawstring_aspect(bluepos + eX * 0.5 * mySize_x, ftos(bluealive), 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +              i = aspect_ratio * mySize_y;
 +              myPos_x = myPos_x + (mySize_x - i) / 2;
 +              mySize_x = i;
        }
        else
        {
 -              redpos = pos;
 -              bluepos = pos + eY * 0.5 * mySize_y;
 -              drawpic_aspect_skin(redpos, "player_red.tga", eX * mySize_x + eY * 0.3 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 -              drawstring_aspect(redpos + eY * 0.3 * mySize_y, ftos(redalive), eX * mySize_x + eY * 0.2 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 -              drawpic_aspect_skin(bluepos, "player_blue.tga", eX * mySize_x + eY * 0.3 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 -              drawstring_aspect(bluepos + eY * 0.3 * mySize_y, ftos(bluealive), eX * mySize_x + eY * 0.2 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +              i = 1/aspect_ratio * mySize_x;
 +              myPos_y = myPos_y + (mySize_y - i) / 2;
 +              mySize_y = i;
 +      }
 +
 +      if(layout)
 +      {
 +              drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +              drawstring_aspect(myPos + eX * 0.7 * mySize_x, ftos(stat), eX * 0.3 * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
 +      }
 +      else
 +              drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
 +}
 +
 +// Clan Arena and Freeze Tag HUD modicons
 +void HUD_Mod_CA(vector myPos, vector mySize)
 +{
 +      mod_active = 1; // required in each mod function that always shows something
 +      entity tm;
 +      float teams_count = 0;
 +      for(tm = teams.sort_next; tm; tm = tm.sort_next)
 +              if(tm.team != NUM_SPECTATOR)
 +                      ++teams_count;
 +
 +      float layout;
 +      if(gametype == MAPINFO_TYPE_CA)
 +              layout = autocvar_hud_panel_modicons_ca_layout;
 +      else //if(gametype == MAPINFO_TYPE_FREEZETAG)
 +              layout = autocvar_hud_panel_modicons_freezetag_layout;
 +      float rows, columns, aspect_ratio;
 +      rows = mySize_y/mySize_x;
 +      aspect_ratio = (layout) ? 2 : 1;
 +      rows = bound(1, floor((sqrt((4 * aspect_ratio * teams_count + rows) * rows) + rows + 0.5) / 2), teams_count);
 +      columns = ceil(teams_count/rows);
 +
 +      int i;
 +      float row = 0, column = 0;
 +      vector pos, itemSize;
 +      itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
 +      for(i=0; i<teams_count; ++i)
 +      {
 +              pos = myPos + eX * column * itemSize_x + eY * row * itemSize_y;
 +
 +              DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
 +
 +              ++row;
 +              if(row >= rows)
 +              {
 +                      row = 0;
 +                      ++column;
 +              }
        }
  }
  
@@@ -3360,11 -3272,11 +3340,11 @@@ void HUD_Mod_Dom(vector myPos, vector m
  
        int i;
        float row = 0, column = 0;
 +      vector pos, itemSize;
 +      itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
        for(i=0; i<teams_count; ++i)
        {
 -              vector pos, itemSize;
 -              pos = myPos + eX * column * mySize_x*(1/columns) + eY * row * mySize_y*(1/rows);
 -              itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
 +              pos = myPos + eX * column * itemSize_x + eY * row * itemSize_y;
  
                DrawDomItem(pos, itemSize, aspect_ratio, layout, i);
  
@@@ -3388,10 -3300,8 +3368,8 @@@ void HUD_ModIcons(void
                if(!autocvar_hud_panel_modicons) return;
                if (gametype != MAPINFO_TYPE_CTF && gametype != MAPINFO_TYPE_KEYHUNT && gametype != MAPINFO_TYPE_NEXBALL && gametype != MAPINFO_TYPE_CTS && gametype != MAPINFO_TYPE_RACE && gametype != MAPINFO_TYPE_CA && gametype != MAPINFO_TYPE_FREEZETAG && gametype != MAPINFO_TYPE_KEEPAWAY && gametype != MAPINFO_TYPE_DOMINATION) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_MODICONS;
  
-       HUD_Panel_UpdateCvars(modicons);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        draw_beginBoldFont();
  
  // Draw pressed keys (#11)
  //
- void HUD_DrawPressedKeys(void)
+ void HUD_PressedKeys(void)
  {
        if(!autocvar__hud_configure)
        {
                if(!autocvar_hud_panel_pressedkeys) return;
                if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_PRESSEDKEYS;
  
-       HUD_Panel_UpdateCvars(pressedkeys);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@@ -3522,10 -3429,8 +3497,8 @@@ void HUD_Chat(void
                if(autocvar__con_chat_maximized)
                        if(!hud_draw_maximized) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_CHAT;
  
-       HUD_Panel_UpdateCvars(chat);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        if(autocvar__con_chat_maximized && !autocvar__hud_configure) // draw at full screen height if maximized
@@@ -3598,10 -3503,8 +3571,8 @@@ void HUD_EngineInfo(void
        {
                if(!autocvar_hud_panel_engineinfo) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_ENGINEINFO;
  
-       HUD_Panel_UpdateCvars(engineinfo);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@@ -3661,10 -3564,8 +3632,8 @@@ void HUD_InfoMessages(void
        {
                if(!autocvar_hud_panel_infomessages) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_INFOMESSAGES;
  
-       HUD_Panel_UpdateCvars(infomessages);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
        vector pos, mySize;
        pos = panel_pos;
@@@ -3852,10 -3753,8 +3821,8 @@@ void HUD_Physics(void
                if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return;
                if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
        }
-       else
-               hud_configure_active_panel = HUD_PANEL_PHYSICS;
  
-       HUD_Panel_UpdateCvars(physics);
+       HUD_Panel_UpdateCvars();
        HUD_Panel_ApplyFadeAlpha();
  
        draw_beginBoldFont();
@@@ -4235,8 -4134,6 +4202,6 @@@ void HUD_CenterPrint (void
        }
        else
        {
-               hud_configure_active_panel = HUD_PANEL_CENTERPRINT;
                if (!hud_configure_prev)
                        reset_centerprint_messages();
                if (time > hud_configure_cp_generation_time)
                }
        }
  
-       HUD_Panel_UpdateCvars(centerprint);
+       HUD_Panel_UpdateCvars();
  
        // this panel doesn't fade when showing the scoreboard
        if(autocvar__menu_alpha)
@@@ -4441,44 -4338,6 +4406,6 @@@ void HUD_Reset (void
                HUD_Mod_CTF_Reset();
  }
  
- #define HUD_DrawPanel(id)\
- switch (id) {\
-       case (HUD_PANEL_RADAR):\
-               HUD_Radar(); break;\
-       case (HUD_PANEL_WEAPONS):\
-               HUD_Weapons(); break;\
-       case (HUD_PANEL_AMMO):\
-               HUD_Ammo(); break;\
-       case (HUD_PANEL_POWERUPS):\
-               HUD_Powerups(); break;\
-       case (HUD_PANEL_HEALTHARMOR):\
-               HUD_HealthArmor(); break;\
-       case (HUD_PANEL_NOTIFY):\
-               HUD_Notify(); break;\
-       case (HUD_PANEL_TIMER):\
-               HUD_Timer(); break;\
-       case (HUD_PANEL_SCORE):\
-               HUD_Score(); break;\
-       case (HUD_PANEL_RACETIMER):\
-               HUD_RaceTimer(); break;\
-       case (HUD_PANEL_VOTE):\
-               HUD_VoteWindow(); break;\
-       case (HUD_PANEL_MODICONS):\
-               HUD_ModIcons(); break;\
-       case (HUD_PANEL_PRESSEDKEYS):\
-               HUD_DrawPressedKeys(); break;\
-       case (HUD_PANEL_CHAT):\
-               HUD_Chat(); break;\
-       case (HUD_PANEL_ENGINEINFO):\
-               HUD_EngineInfo(); break;\
-       case (HUD_PANEL_INFOMESSAGES):\
-               HUD_InfoMessages(); break;\
-       case (HUD_PANEL_PHYSICS):\
-               HUD_Physics(); break;\
-       case (HUD_PANEL_CENTERPRINT):\
-               HUD_CenterPrint(); break;\
- } ENDS_WITH_CURLY_BRACE
  void HUD_Main (void)
  {
        float i;
        // they must call HUD_Panel_ApplyFadeAlpha(); only when showing the menu
        if(scoreboard_fade_alpha == 1)
        {
-               HUD_CenterPrint();
+               (panel = HUD_PANEL(CENTERPRINT)).panel_draw();
                return;
        }
  
                vector color;
                float hud_dock_color_team = autocvar_hud_dock_color_team;
                if((teamplay) && hud_dock_color_team) {
-                       color = colormapPaletteColor(myteam, 1) * hud_dock_color_team;
+                       if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
+                               color = '1 0 0' * hud_dock_color_team;
+                       else
+                               color = myteamcolors * hud_dock_color_team;
                }
                else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
                        color = '1 0 0' * hud_dock_color_team;
        hud_draw_maximized = 0;
        // draw panels in order specified by panel_order array
        for(i = HUD_PANEL_NUM - 1; i >= 0; --i)
-               HUD_DrawPanel(panel_order[i]);
+               (panel = hud_panel[panel_order[i]]).panel_draw();
  
        hud_draw_maximized = 1; // panels that may be maximized must check this var
        // draw maximized panels on top
        if(hud_panel_radar_maximized)
-               HUD_Radar();
+               (panel = HUD_PANEL(RADAR)).panel_draw();
        if(autocvar__con_chat_maximized)
-               HUD_Chat();
+               (panel = HUD_PANEL(CHAT)).panel_draw();
  
        if(autocvar__hud_configure)
        {
-               if(tab_panel != -1)
+               if(tab_panel)
                {
-                       HUD_Panel_UpdatePosSizeForId(tab_panel)
+                       panel = tab_panel;
+                       HUD_Panel_UpdatePosSize()
                        drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .2, DRAWFLAG_NORMAL);
                }
-               if(highlightedPanel != -1)
+               if(highlightedPanel)
                {
-                       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+                       panel = highlightedPanel;
+                       HUD_Panel_UpdatePosSize()
                        HUD_Panel_HlBorder(panel_bg_border + 1.5 * hlBorderSize, '0 0.5 1', 0.25 * (1 - autocvar__menu_alpha));
                }
                if(!hud_configure_prev || hud_configure_prev == -1)
                {
                        if(autocvar_hud_cursormode) { setcursormode(1); }
                        hudShiftState = 0;
+                       for(i = HUD_PANEL_NUM - 1; i >= 0; --i)
+                               hud_panel[panel_order[i]].update_time = time;
                }
        }
        else if (hud_configure_prev && autocvar_hud_cursormode)
@@@ -2,8 -2,8 +2,8 @@@
  // q: quoted, n: not quoted
  #define HUD_Write_Cvar_n(cvar) HUD_Write(strcat("seta ", cvar, " ", cvar_string(cvar), "\n"))
  #define HUD_Write_Cvar_q(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n"))
- #define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel_name, cvar_suf))
- #define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel_name, cvar_suf))
+ #define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel.panel_name, cvar_suf))
+ #define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel.panel_name, cvar_suf))
  // Save the config
  void HUD_Panel_ExportCfg(string cfgname)
  {
@@@ -52,7 -52,7 +52,7 @@@
                float i;
                for (i = 0; i < HUD_PANEL_NUM; ++i)
                {
-                       HUD_Panel_GetName(i);
+                       panel = hud_panel[i];
  
                        HUD_Write_PanelCvar_n("");
                        HUD_Write_PanelCvar_q("_pos");
                                        HUD_Write_PanelCvar_q("_alreadyvoted_alpha");
                                        break;
                                case HUD_PANEL_MODICONS:
 +                                      HUD_Write_PanelCvar_q("_ca_layout");
                                        HUD_Write_PanelCvar_q("_dom_layout");
 +                                      HUD_Write_PanelCvar_q("_freezetag_layout");
                                        break;
                                case HUD_PANEL_PRESSEDKEYS:
                                        HUD_Write_PanelCvar_q("_aspect");
@@@ -219,10 -217,10 +219,10 @@@ vector HUD_Panel_CheckMove(vector myPos
        myTarget = myPos;
  
        for (i = 0; i < HUD_PANEL_NUM; ++i) {
-               if(i == highlightedPanel || !panel_enabled)
-                       continue;
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               if(panel == highlightedPanel) continue;
+               HUD_Panel_UpdatePosSize()
+               if(!panel_enabled) continue;
  
                panel_pos -= '1 1 0' * panel_bg_border;
                panel_size += '2 2 0' * panel_bg_border;
  
  void HUD_Panel_SetPos(vector pos)
  {
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
        vector mySize;
        mySize = panel_size;
  
        string s;
        s = strcat(ftos(pos_x/vid_conwidth), " ", ftos(pos_y/vid_conheight));
  
-       HUD_Panel_GetName(highlightedPanel);
-       cvar_set(strcat("hud_panel_", panel_name, "_pos"), s);
+       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
  }
  
  // check if resize will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
@@@ -319,10 -317,10 +319,10 @@@ vector HUD_Panel_CheckResize(vector myS
        ratio = mySize_x/mySize_y;
  
        for (i = 0; i < HUD_PANEL_NUM; ++i) {
-               if(i == highlightedPanel || !panel_enabled)
-                       continue;
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               if(panel == highlightedPanel) continue;
+               HUD_Panel_UpdatePosSize()
+               if(!panel_enabled) continue;
  
                panel_pos -= '1 1 0' * panel_bg_border;
                panel_size += '2 2 0' * panel_bg_border;
  
  void HUD_Panel_SetPosSize(vector mySize)
  {
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
        vector resizeorigin;
        resizeorigin = panel_click_resizeorigin;
        local noref vector myPos; // fteqcc sucks
        mySize_x = max(0.025 * vid_conwidth, mySize_x);
        mySize_y = max(0.025 * vid_conheight, mySize_y);
  
-       if(highlightedPanel == HUD_PANEL_CHAT) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small.
+       if(highlightedPanel == HUD_PANEL(CHAT)) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small.
        {
                mySize_x = max(17 * autocvar_con_chatsize, mySize_x);
                mySize_y = max(2 * autocvar_con_chatsize + 2 * panel_bg_padding, mySize_y);
        //if(cvar("hud_configure_checkcollisions_debug"))
                //drawfill(myPos, mySize, '0 1 0', .3, DRAWFLAG_NORMAL);
  
-       HUD_Panel_GetName(highlightedPanel);
        string s;
        s = strcat(ftos(mySize_x/vid_conwidth), " ", ftos(mySize_y/vid_conheight));
-       cvar_set(strcat("hud_panel_", panel_name, "_size"), s);
+       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
  
        s = strcat(ftos(myPos_x/vid_conwidth), " ", ftos(myPos_y/vid_conheight));
-       cvar_set(strcat("hud_panel_", panel_name, "_pos"), s);
+       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
  }
  
  float pressed_key_time;
  vector highlightedPanel_initial_pos, highlightedPanel_initial_size;
  void HUD_Panel_Arrow_Action(float nPrimary)
  {
-       if (highlightedPanel == -1)
+       if(!highlightedPanel)
                return;
  
        hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions);
                        step = (step / 64) * (1 + 2 * (time - pressed_key_time));
        }
  
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
  
        highlightedPanel_initial_pos = panel_pos;
        highlightedPanel_initial_size = panel_size;
                HUD_Panel_SetPos(pos);
        }
  
-       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+       panel = highlightedPanel;
+       HUD_Panel_UpdatePosSize()
  
        if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
        {
@@@ -628,15 -628,16 +630,16 @@@ float prevMouseClickedTime; // time dur
  vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks
  
  void HUD_Panel_EnableMenu();
float tab_panels[HUD_PANEL_NUM];
float tab_panel, tab_backward;
entity tab_panels[HUD_PANEL_MAX];
entity tab_panel;
  vector tab_panel_pos;
+ float tab_backward;
  void HUD_Panel_FirstInDrawQ(float id);
  void reset_tab_panels()
  {
        int i;
        for(i = 0; i < HUD_PANEL_NUM; ++i)
-               tab_panels[i] = -1;
+               tab_panels[i] = world;
  }
  float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
  {
        {
                if (bInputType == 1) //ctrl has been released
                {
-                       if (tab_panel != -1)
+                       if (tab_panel)
                        {
                                //switch to selected panel
                                highlightedPanel = tab_panel;
                                highlightedAction = 0;
-                               HUD_Panel_FirstInDrawQ(highlightedPanel);
+                               HUD_Panel_FirstInDrawQ(highlightedPanel.panel_id);
                        }
-                       tab_panel = -1;
+                       tab_panel = world;
                        reset_tab_panels();
                }
        }
                //(it should only after every other panel of the hud)
                //It's a minor bug anyway, we can live with it
  
-               float starting_panel;
-               float old_tab_panel = tab_panel;
-               if (tab_panel == -1) //first press of TAB
+               entity starting_panel;
+               entity old_tab_panel = tab_panel;
+               if (!tab_panel) //first press of TAB
                {
-                       if (highlightedPanel != -1)
-                               HUD_Panel_UpdatePosSizeForId(highlightedPanel)
+                       if (highlightedPanel)
+                       {
+                               panel = highlightedPanel;
+                               HUD_Panel_UpdatePosSize()
+                       }
                        else
                                panel_pos = '0 0 0';
-                       starting_panel = highlightedPanel; //can be -1, it means no starting panel
+                       starting_panel = highlightedPanel;
                        tab_panel_pos = panel_pos; //to compute level
                }
                else
                level = floor(tab_panel_pos_y / level_height) * level_height; //starting level
                candidate_pos_x = (!tab_backward) ? vid_conwidth : 0;
                start_pos_x = tab_panel_pos_x;
-               tab_panel = -1;
+               tab_panel = world;
                k=0;
                while(++k)
                {
                        for(i = 0; i < HUD_PANEL_NUM; ++i)
                        {
-                               if (i == tab_panels[i] || i == starting_panel)
+                               panel = hud_panel[i];
+                               if (panel == tab_panels[i] || panel == starting_panel)
                                        continue;
-                               HUD_Panel_UpdatePosSizeForId(i)
+                               HUD_Panel_UpdatePosSize()
                                if (panel_pos_y >= level && (panel_pos_y - level) < level_height)
                                if (  ( !tab_backward && panel_pos_x >= start_pos_x && (panel_pos_x < candidate_pos_x || (panel_pos_x == candidate_pos_x && panel_pos_y <= candidate_pos_y)) )
                                        || ( tab_backward && panel_pos_x <= start_pos_x && (panel_pos_x > candidate_pos_x || (panel_pos_x == candidate_pos_x && panel_pos_y >= candidate_pos_y)) )  )
                                {
-                                       tab_panel = i;
+                                       tab_panel = panel;
                                        tab_panel_pos = candidate_pos = panel_pos;
                                }
                        }
-                       if (tab_panel != -1)
+                       if (tab_panel)
                                break;
                        if (k == LEVELS_NUM) //tab_panel not found
                        {
                                reset_tab_panels();
-                               if (old_tab_panel == -2) //this prevents an infinite loop (should not happen normally)
+                               if (!old_tab_panel)
                                {
-                                       tab_panel = -1;
+                                       tab_panel = world;
                                        return true;
                                }
                                starting_panel = old_tab_panel;
-                               old_tab_panel = -2;
+                               old_tab_panel = world;
                                goto find_tab_panel; //u must find tab_panel!
                        }
                        if (!tab_backward)
                        }
                }
  
-               tab_panels[tab_panel] = tab_panel;
+               tab_panels[tab_panel.panel_id] = tab_panel;
        }
        else if(nPrimary == K_SPACE && hudShiftState & S_CTRL) // enable/disable highlighted panel or dock
        {
                if (bInputType == 1 || mouseClicked)
                        return true;
  
-               if (highlightedPanel != -1)
-               {
-                       HUD_Panel_GetName(highlightedPanel);
-                       cvar_set(strcat("hud_panel_", panel_name), ftos(!(panel_enabled)));
-               }
+               if (highlightedPanel)
+                       cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name))));
                else
                        cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : "");
        }
                if (bInputType == 1 || mouseClicked)
                        return true;
  
-               if (highlightedPanel != -1)
+               if (highlightedPanel)
                {
-                       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+                       panel = highlightedPanel;
+                       HUD_Panel_UpdatePosSize()
                        panel_size_copied = panel_size;
-                       highlightedPanel_copied = highlightedPanel;
                }
        }
        else if(nPrimary == 'v' && hudShiftState & S_CTRL) // past copied size on the highlighted panel
                if (bInputType == 1 || mouseClicked)
                        return true;
  
-               if (highlightedPanel_copied == -1 || highlightedPanel == -1)
+               if (panel_size_copied == '0 0 0' || !highlightedPanel)
                        return true;
  
-               HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+               panel = highlightedPanel;
+               HUD_Panel_UpdatePosSize()
  
                // reduce size if it'd go beyond screen boundaries
                vector tmp_size = panel_size_copied;
                highlightedPanel_backup = highlightedPanel;
  
                s = strcat(ftos(tmp_size_x/vid_conwidth), " ", ftos(tmp_size_y/vid_conheight));
-               HUD_Panel_GetName(highlightedPanel);
-               cvar_set(strcat("hud_panel_", panel_name, "_size"), s);
+               cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
        }
        else if(nPrimary == 'z' && hudShiftState & S_CTRL) // undo last action
        {
                if (bInputType == 1 || mouseClicked)
                        return true;
                //restore previous values
-               if (highlightedPanel_backup != -1)
+               if (highlightedPanel_backup)
                {
-                       HUD_Panel_GetName(highlightedPanel_backup);
                        s = strcat(ftos(panel_pos_backup_x/vid_conwidth), " ", ftos(panel_pos_backup_y/vid_conheight));
-                       cvar_set(strcat("hud_panel_", panel_name, "_pos"), s);
+                       cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_pos"), s);
                        s = strcat(ftos(panel_size_backup_x/vid_conwidth), " ", ftos(panel_size_backup_y/vid_conheight));
-                       cvar_set(strcat("hud_panel_", panel_name, "_size"), s);
-                       highlightedPanel_backup = -1;
+                       cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_size"), s);
+                       highlightedPanel_backup = world;
                }
        }
        else if(nPrimary == K_UPARROW || nPrimary == K_DOWNARROW || nPrimary == K_LEFTARROW || nPrimary == K_RIGHTARROW)
        {
                if (bInputType == 1)
                        return true;
-               if (highlightedPanel != -1)
+               if (highlightedPanel)
                        HUD_Panel_EnableMenu();
        }
        else if(hit_con_bind)
@@@ -916,7 -917,8 +919,8 @@@ float HUD_Panel_Check_Mouse_Pos(float a
                i = panel_order[j];
                j += 1;
  
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               HUD_Panel_UpdatePosSize()
  
                border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
  
@@@ -996,14 -998,15 +1000,15 @@@ void HUD_Panel_Highlight(float allow_mo
                i = panel_order[j];
                j += 1;
  
-               HUD_Panel_UpdatePosSizeForId(i);
+               panel = hud_panel[i];
+               HUD_Panel_UpdatePosSize()
  
                border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
  
                // move
                if(allow_move && mousepos_x > panel_pos_x && mousepos_y > panel_pos_y && mousepos_x < panel_pos_x + panel_size_x && mousepos_y < panel_pos_y + panel_size_y)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 1;
                        panel_click_distance = mousepos - panel_pos;
                // resize from topleft border
                else if(mousepos_x >= panel_pos_x - border && mousepos_y >= panel_pos_y - border && mousepos_x <= panel_pos_x + 0.5 * panel_size_x && mousepos_y <= panel_pos_y + 0.5 * panel_size_y)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 1;
                // resize from topright border
                else if(mousepos_x >= panel_pos_x + 0.5 * panel_size_x && mousepos_y >= panel_pos_y - border && mousepos_x <= panel_pos_x + panel_size_x + border && mousepos_y <= panel_pos_y + 0.5 * panel_size_y)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 2;
                // resize from bottomleft border
                else if(mousepos_x >= panel_pos_x - border && mousepos_y >= panel_pos_y + 0.5 * panel_size_y && mousepos_x <= panel_pos_x + 0.5 * panel_size_x && mousepos_y <= panel_pos_y + panel_size_y + border)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 3;
                // resize from bottomright border
                else if(mousepos_x >= panel_pos_x + 0.5 * panel_size_x && mousepos_y >= panel_pos_y + 0.5 * panel_size_y && mousepos_x <= panel_pos_x + panel_size_x + border && mousepos_y <= panel_pos_y + panel_size_y + border)
                {
-                       highlightedPanel = i;
+                       highlightedPanel = hud_panel[i];
                        HUD_Panel_FirstInDrawQ(i);
                        highlightedAction = 2;
                        resizeCorner = 4;
                        return;
                }
        }
-       highlightedPanel = -1;
+       highlightedPanel = world;
        highlightedAction = 0;
  }
  
@@@ -1064,8 -1067,7 +1069,7 @@@ void HUD_Panel_EnableMenu(
  {
        menu_enabled = 2;
        menu_enabled_time = time;
-       HUD_Panel_GetName(highlightedPanel);
-       localcmd("menu_showhudoptions ", panel_name, "\n");
+       localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n");
  }
  float mouse_over_panel;
  void HUD_Panel_Mouse()
        {
                if(prevMouseClicked == 0)
                {
-                       if (tab_panel != -1)
+                       if (tab_panel)
                        {
                                //stop ctrl-tab selection
-                               tab_panel = -1;
+                               tab_panel = world;
                                reset_tab_panels();
                        }
                        HUD_Panel_Highlight(mouseClicked & S_MOUSE1); // sets highlightedPanel, highlightedAction, panel_click_distance, panel_click_resizeorigin
-                                                                       // and calls HUD_Panel_UpdatePosSizeForId() for the highlighted panel
-                       if (highlightedPanel != -1)
+                                                                       // and calls HUD_Panel_UpdatePosSize() for the highlighted panel
+                       if (highlightedPanel)
                        {
                                highlightedPanel_initial_pos = panel_pos;
                                highlightedPanel_initial_size = panel_size;
                        }
                        // doubleclick check
-                       if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel != -1 && prevMouseClickedPos == mousepos)
+                       if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel && prevMouseClickedPos == mousepos)
                        {
                                mouseClicked = 0; // to prevent spam, I guess.
                                HUD_Panel_EnableMenu();
                        }
                }
                else
-                       HUD_Panel_UpdatePosSizeForId(highlightedPanel);
+               {
+                       panel = highlightedPanel;
+                       HUD_Panel_UpdatePosSize()
+               }
  
-               if (highlightedPanel != -1)
+               if (highlightedPanel)
                {
                        drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
                        if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
                        mouse_over_panel = 0;
                else
                        mouse_over_panel = HUD_Panel_Check_Mouse_Pos(TRUE);
-               if (mouse_over_panel && tab_panel == -1)
+               if (mouse_over_panel && !tab_panel)
                        drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
        }
        // draw cursor after performing move/resize to have the panel pos/size updated before mouse_over_panel
@@@ -177,7 -177,6 +177,7 @@@ const float STAT_SECRETS_TOTAL = 70
  const float STAT_SECRETS_FOUND = 71;
  
  const float STAT_RESPAWN_TIME = 72;
 +const float STAT_ROUNDSTARTTIME = 73;
  
  // mod stats (1xx)
  const float STAT_REDALIVE = 100;
@@@ -392,43 -391,6 +392,6 @@@ float WR_SWITCHABLE       = 12; // (CSQC) imp
  float WR_PLAYERDEATH    = 13; // (SVQC) does not need to do anything
  float WR_GONETHINK    = 14; // (SVQC) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed
  
- float HUD_PANEL_WEAPONS               = 0;
- float HUD_PANEL_AMMO          = 1;
- float HUD_PANEL_POWERUPS      = 2;
- float HUD_PANEL_HEALTHARMOR   = 3;
- float HUD_PANEL_NOTIFY                = 4;
- float HUD_PANEL_TIMER         = 5;
- float HUD_PANEL_RADAR         = 6;
- float HUD_PANEL_SCORE         = 7;
- float HUD_PANEL_RACETIMER     = 8;
- float HUD_PANEL_VOTE          = 9;
- float HUD_PANEL_MODICONS      = 10;
- float HUD_PANEL_PRESSEDKEYS   = 11;
- float HUD_PANEL_CHAT          = 12;
- float HUD_PANEL_ENGINEINFO    = 13;
- float HUD_PANEL_INFOMESSAGES  = 14;
- float HUD_PANEL_PHYSICS       = 15;
- float HUD_PANEL_CENTERPRINT   = 16;
- float HUD_PANEL_NUM           = 17; // always last panel id + 1, please increment when adding a new panel
- string HUD_PANELNAME_WEAPONS          = "weapons";
- string HUD_PANELNAME_AMMO             = "ammo";
- string HUD_PANELNAME_POWERUPS         = "powerups";
- string HUD_PANELNAME_HEALTHARMOR      = "healtharmor";
- string HUD_PANELNAME_NOTIFY           = "notify";
- string HUD_PANELNAME_TIMER            = "timer";
- string HUD_PANELNAME_RADAR            = "radar";
- string HUD_PANELNAME_SCORE            = "score";
- string HUD_PANELNAME_RACETIMER                = "racetimer";
- string HUD_PANELNAME_VOTE             = "vote";
- string HUD_PANELNAME_MODICONS         = "modicons";
- string HUD_PANELNAME_PRESSEDKEYS      = "pressedkeys";
- string HUD_PANELNAME_CHAT             = "chat";
- string HUD_PANELNAME_ENGINEINFO               = "engineinfo";
- string HUD_PANELNAME_INFOMESSAGES     = "infomessages";
- string HUD_PANELNAME_PHYSICS  = "physics";
- string HUD_PANELNAME_CENTERPRINT      = "centerprint";
  #define SERVERFLAG_ALLOW_FULLBRIGHT 1
  #define SERVERFLAG_TEAMPLAY 2
  #define SERVERFLAG_PLAYERSTATS 4
@@@ -66,6 -66,10 +66,10 @@@ void Create_Notification_Entity
  
  void Dump_Notifications(float fh, float alsoprint);
  
+ #ifdef NOTIFICATIONS_DEBUG
+ void Debug_Notification(string input);
+ #endif
  void Local_Notification(float net_type, float net_name, ...count);
  void Local_Notification_WOVA(
        float net_type, float net_name,
@@@ -319,12 -323,8 +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_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_CENTER_NOTIF(1, CENTER_NIX_COUNTDOWN,               0, 2, "item_wepname",  CPID_NIX,              "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_NIX_NEWWEAPON,               0, 1, "item_wepname",  CPID_NIX,              "0 0", _("^F2Active weapon: ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_OVERTIME_FRAG,               0, 0, "",              CPID_OVERTIME,         "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!")) \
-       MSG_CENTER_NOTIF(1, CENTER_OVERTIME_TIME,               0, 1, "f1time",        CPID_OVERTIME,         "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nAdded ^F4%s^F2 to the game!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_OVERTIME_TIME,               0, 1, "f1time",        CPID_OVERTIME,         "0 0", _("^F2Now playing ^F4OVERTIME^F2!\n^BGAdded ^F4%s^BG to the game!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_INVISIBILITY,      0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Invisibility has worn off"), "") \
        MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SHIELD,            0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Shield has worn off"), "") \
        MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SPEED,             0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Speed has worn off"), "") \
        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) \
@@@ -688,6 -683,7 +692,7 @@@ var float autocvar_notification_show_sp
  var float autocvar_notification_errors_are_fatal = TRUE;
  var float autocvar_notification_lifetime_runtime = 0.5;
  var float autocvar_notification_lifetime_mapload = 10;
+ var float autocvar_notification_debug = FALSE;
  
  #ifdef SVQC
  .float FRAG_VERBOSE;
@@@ -741,7 -737,7 +746,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
@@@ -795,7 -791,7 +800,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)) \
@@@ -839,7 -835,7 +844,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,7 -75,6 +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;
@@@ -710,8 -709,6 +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")
@@@ -817,15 -814,11 +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")
@@@ -974,6 -967,7 +974,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;
@@@ -1228,3 -1222,7 +1228,7 @@@ float autocvar_physics_ode
  float autocvar_g_physical_items;
  float autocvar_g_physical_items_damageforcescale;
  float autocvar_g_physical_items_reset;
+ float autocvar_g_touchexplode_radius;
+ float autocvar_g_touchexplode_damage;
+ float autocvar_g_touchexplode_edgedamage;
+ float autocvar_g_touchexplode_force;
@@@ -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,24 -385,6 +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);
        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_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_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;
@@@ -592,21 -611,6 +592,6 @@@ void FixPlayermodel(
                                setcolor(self, stof(autocvar_sv_defaultplayercolors));
  }
  
- void PlayerTouchExplode(entity p1, entity p2)
- {
-       vector org;
-       org = (p1.origin + p2.origin) * 0.5;
-       org_z += (p1.mins_z + p2.mins_z) * 0.5;
-       te_explosion(org);
-       entity e;
-       e = spawn();
-       setorigin(e, org);
-       RadiusDamage(e, world, g_touchexplode_damage, g_touchexplode_edgedamage, g_touchexplode_radius, world, g_touchexplode_force, DEATH_TOUCHEXPLODE, world);
-       remove(e);
- }
  /*
  =============
  PutClientInServer
@@@ -618,7 -622,11 +603,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;
@@@ -1032,13 -1047,14 +1017,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()
@@@ -1214,11 -1230,19 +1199,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
@@@ -1448,6 -1472,13 +1433,6 @@@ void ClientConnect (void
        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();
        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);
  }
  /*
  =============
@@@ -1592,6 -1625,12 +1577,6 @@@ void ClientDisconnect (void
  
        bot_relinkplayerlist();
  
 -      if(g_arena)
 -      {
 -              Spawnqueue_Unmark(self);
 -              Spawnqueue_Remove(self);
 -      }
 -
        accuracy_free(self);
        ClientData_Detach();
        PlayerScore_Detach(self);
@@@ -1949,7 -1988,7 +1934,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))
@@@ -2074,6 -2113,7 +2059,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);
@@@ -2224,8 -2264,6 +2209,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
        {
@@@ -2285,9 -2322,8 +2270,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);
@@@ -2439,8 -2475,6 +2424,6 @@@ void PlayerUseKey(
        MUTATOR_CALLHOOK(PlayerUseKey);
  }
  
- .float touchexplode_time;
  /*
  =============
  PlayerPreThink
@@@ -2456,10 -2490,14 +2439,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_lms || 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;
                }
-               // FIXME from now on self.deadflag is always 0 (and self.health is never < 1)
-               // so (self.deadflag == DEAD_NO) is always true in the code below
-               if(g_touchexplode)
-               if(time > self.touchexplode_time)
-               if(self.classname == "player")
-               if(self.deadflag == DEAD_NO)
-               if not(IS_INDEPENDENT_PLAYER(self))
-               FOR_EACH_PLAYER(other) if(self != other)
-               {
-                       if(time > other.touchexplode_time)
-                       if(other.deadflag == DEAD_NO)
-                       if not(IS_INDEPENDENT_PLAYER(other))
-                       if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax))
-                       {
-                               PlayerTouchExplode(self, other);
-                               self.touchexplode_time = other.touchexplode_time = time + 0.2;
-                       }
-               }
  
                if(g_lms && !self.deadflag && autocvar_g_lms_campcheck_interval)
                {
                                        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 :(
-                                       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');
+                                       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;
@@@ -319,84 -319,6 +319,84 @@@ void VoteThink(
  //  Game logic for warmup
  // =======================
  
 +// Resets the state of all clients, items, weapons, waypoints, ... of the map.
 +void reset_map(float dorespawn)
 +{
 +      entity oldself;
 +      oldself = self;
 +
 +      if(time <= game_starttime && round_handler_IsActive())
 +              round_handler_Reset(game_starttime);
 +
 +      if(g_race || g_cts)
 +              race_ReadyRestart();
 +      else MUTATOR_CALLHOOK(reset_map_global);
 +
 +      lms_lowest_lives = 999;
 +      lms_next_place = player_count;
 +
 +      for(self = world; (self = nextent(self)); )
 +      if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
 +      {
 +              if(self.reset)
 +              {
 +                      self.reset();
 +                      continue;
 +              }
 +
 +              if(self.team_saved)
 +                      self.team = self.team_saved;
 +
 +              if(self.flags & FL_PROJECTILE) // remove any projectiles left
 +                      remove(self);
 +      }
 +
 +      // Waypoints and assault start come LAST
 +      for(self = world; (self = nextent(self)); )
 +      if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
 +      {
 +              if(self.reset2)
 +              {
 +                      self.reset2();
 +                      continue;
 +              }
 +      }
 +
 +      // Moving the player reset code here since the player-reset depends
 +      // on spawnpoint entities which have to be reset first --blub
 +      if(dorespawn)
 +      if(!MUTATOR_CALLHOOK(reset_map_players))
 +      FOR_EACH_CLIENT(self) // reset all players
 +      {
 +              /*
 +              only reset players if a restart countdown is active
 +              this can either be due to cvar sv_ready_restart_after_countdown having set
 +              restart_mapalreadyrestarted to 1 after the countdown ended or when
 +              sv_ready_restart_after_countdown is not used and countdown is still running
 +              */
 +              if (restart_mapalreadyrestarted || (time < game_starttime))
 +              {
 +                      //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
 +                      if (IS_PLAYER(self)) {
 +                              //PlayerScore_Clear(self);
 +                              if(g_lms)
 +                                      PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
 +                              self.killcount = 0;
 +                              //stop the player from moving so that he stands still once he gets respawned
 +                              self.velocity = '0 0 0';
 +                              self.avelocity = '0 0 0';
 +                              self.movement = '0 0 0';
 +                              PutClientInServer();
 +                      }
 +              }
 +      }
 +
 +      if(g_keyhunt)
 +              kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round + (game_starttime - time), kh_StartRound);
 +
 +      self = oldself;
 +}
 +
  // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
  void ReadyRestart_think() 
  {
@@@ -423,7 -345,8 +423,7 @@@ void ReadyRestart_force(
        checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
  
        readyrestart_happened = 1;
 -      game_starttime = time;
 -      if(!g_ca && !g_arena) { game_starttime += RESTART_COUNTDOWN; }
 +      game_starttime = time + RESTART_COUNTDOWN;
  
        // clear player attributes
        FOR_EACH_CLIENT(tmp_player)
        inWarmupStage = 0; // once the game is restarted the game is in match stage
  
        // reset the .ready status of all players (also spectators)
 -      FOR_EACH_CLIENTSLOT(tmp_player) { tmp_player.ready = 0; }
 +      FOR_EACH_REALCLIENT(tmp_player) { tmp_player.ready = 0; }
        readycount = 0;
        Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
  
        // lock teams with lockonrestart
 -      if(autocvar_teamplay_lockonrestart && teamplay) 
 +      if(autocvar_teamplay_lockonrestart && teamplay)
        {
                lockteams = 1;
                bprint("^1The teams are now locked.\n");
        }
  
        //initiate the restart-countdown-announcer entity
 -      if(autocvar_sv_ready_restart_after_countdown && !g_ca && !g_arena)
 +      if(autocvar_sv_ready_restart_after_countdown)
        {
                restart_timer = spawn();
                restart_timer.think = ReadyRestart_think;
@@@ -491,13 -414,10 +491,13 @@@ void ReadyCount(
        float ready_needed_factor, ready_needed_count;
        float t_ready = 0, t_players = 0;
  
 -      FOR_EACH_REALPLAYER(tmp_player)
 +      FOR_EACH_REALCLIENT(tmp_player)
        {
 -              ++t_players;
 -              if(tmp_player.ready) { ++t_ready; }
 +              if(IS_PLAYER(tmp_player) || tmp_player.caplayer == 1)
 +              {
 +                      ++t_players;
 +                      if(tmp_player.ready) { ++t_ready; }
 +              }
        }
  
        readycount = t_ready;
@@@ -691,7 -611,7 +691,7 @@@ float VoteCommand_parse(entity caller, 
                        
                        if(accepted > 0)
                        {
-                               string reason = ((argc > next_token) ? substring(vote_command, argv_start_index(next_token), argv_end_index(-1) - argv_start_index(next_token)) : "No reason provided");
+                               string reason = ((argc > next_token) ? substring(vote_command, argv_start_index(next_token), strlen(vote_command) - argv_start_index(next_token)) : "No reason provided");
                                string command_arguments;
                                
                                if(first_command == "kickban")
diff --combined qcsrc/server/g_damage.qc
@@@ -120,6 -120,10 +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)
        {
@@@ -372,7 -376,7 +372,7 @@@ void Obituary(entity attacker, entity i
        string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : "");
  
        #ifdef NOTIFICATIONS_DEBUG
-       dprint(
+       Debug_Notification(
                sprintf(
                        "Obituary(%s, %s, %s, %s = %d);\n",
                        attacker.netname,
diff --combined qcsrc/server/g_world.qc
@@@ -256,17 -256,16 +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");
@@@ -584,6 -581,8 +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);
@@@ -1339,7 -1350,7 +1339,7 @@@ void IntermissionThink(
                && ((self.autoscreenshot > 0) && (time > self.autoscreenshot)) )
        {
                self.autoscreenshot = -1;
-               if(clienttype(self) == CLIENTTYPE_REAL) { stuffcmd(self, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"", GetMapname(), strftime(FALSE, "%s"))); }
+               if(clienttype(self) == CLIENTTYPE_REAL) { stuffcmd(self, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(FALSE, "%s"))); }
                return;
        }
  
@@@ -1467,7 -1478,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:");
@@@ -1581,7 -1592,7 +1581,7 @@@ void NextLevel(
                PlayerStats_AddGlobalInfo(e);
        PlayerStats_Shutdown();
        WeaponStats_Shutdown();
+       
        Kill_Notification(NOTIF_ALL, world, MSG_CENTER, 0); // kill all centerprints now
  
        if(autocvar_sv_eventlog)
@@@ -1667,7 -1678,7 +1667,7 @@@ void InitiateOvertime() // ONLY call th
        tl += autocvar_timelimit_overtime;
        cvar_set("timelimit", ftos(tl));
  
-       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime);
+       Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60);
  }
  
  float GetWinningCode(float fraglimitreached, float equality)
@@@ -993,12 -993,6 +993,6 @@@ float g_bugrigs_speed_ref
  float g_bugrigs_speed_pow;
  float g_bugrigs_steer;
  
- float g_touchexplode;
- float g_touchexplode_radius;
- float g_touchexplode_damage;
- float g_touchexplode_edgedamage;
- float g_touchexplode_force;
  float sv_autotaunt;
  float sv_taunt;
  
@@@ -1014,6 -1008,8 +1008,8 @@@ void readlevelcvars(void
                MUTATOR_ADD(mutator_spawn_near_teammate);
        if(cvar("g_physical_items"))
                MUTATOR_ADD(mutator_physical_items);
+       if(cvar("g_touchexplode"))
+               MUTATOR_ADD(mutator_touchexplode);
        if(!g_minstagib)
        {
                if(cvar("g_invincible_projectiles"))
      g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
      g_bugrigs_steer = cvar("g_bugrigs_steer");
  
-     g_touchexplode = cvar("g_touchexplode");
-     g_touchexplode_radius = cvar("g_touchexplode_radius");
-     g_touchexplode_damage = cvar("g_touchexplode_damage");
-     g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
-     g_touchexplode_force = cvar("g_touchexplode_force");
        sv_clones = cvar("sv_clones");
        sv_foginterval = cvar("sv_foginterval");
        g_cloaked = cvar("g_cloaked");
      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();
  }
@@@ -1,5 -1,3 +1,5 @@@
 +MUTATOR_DECLARATION(gamemode_arena);
 +MUTATOR_DECLARATION(gamemode_ca);
  MUTATOR_DECLARATION(gamemode_keyhunt);
  MUTATOR_DECLARATION(gamemode_freezetag);
  MUTATOR_DECLARATION(gamemode_keepaway);
@@@ -17,5 -15,6 +17,6 @@@ MUTATOR_DECLARATION(mutator_spawn_near_
  MUTATOR_DECLARATION(mutator_physical_items);
  MUTATOR_DECLARATION(mutator_vampire);
  MUTATOR_DECLARATION(mutator_superspec);
+ MUTATOR_DECLARATION(mutator_touchexplode);
  
  MUTATOR_DECLARATION(sandbox);
diff --combined qcsrc/server/progs.src
@@@ -35,8 -35,6 +35,8 @@@ 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
@@@ -87,8 -85,6 +87,8 @@@ antilag.q
  
  playerdemo.qh
  
 +round_handler.qh
 +
  // singleplayer stuff
  item_key.qh
  secret.qh
@@@ -106,6 -102,7 +106,6 @@@ g_subs.q
  g_tetris.qc
  
  //runematch.qc
 -arena.qc
  
  g_violence.qc
  g_damage.qc
@@@ -214,13 -211,9 +214,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
@@@ -238,6 -231,7 +238,7 @@@ mutators/mutator_spawn_near_teammate.q
  mutators/mutator_physical_items.qc
  mutators/sandbox.qc
  mutators/mutator_superspec.qc
+ mutators/mutator_touchexplode.qc
  
  ../warpzonelib/anglestransform.qc
  ../warpzonelib/mathlib.qc