]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/ctf_updates
authorMario <zacjardine@y7mail.com>
Thu, 9 Jul 2015 06:37:31 +0000 (16:37 +1000)
committerMario <zacjardine@y7mail.com>
Thu, 9 Jul 2015 06:37:31 +0000 (16:37 +1000)
Conflicts:
qcsrc/client/hud.qc

1  2 
qcsrc/client/hud.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/notifications.qh
qcsrc/common/stats.qh
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_ctf.qh

diff --combined qcsrc/client/hud.qc
index 15cc1176087b2485502ff45924ea2dab166822bb,b61fe2a5da685f95d66b3a766fdaded8d63cf2ca..33158820050a2d46ed4e50fd03f592fc0fc7dac0
@@@ -1,12 -1,24 +1,27 @@@
+ #include "hud.qh"
+ #include "_all.qh"
+ #include "hud_config.qh"
  #include "scoreboard.qh"
+ #include "sortlist.qh"
  #include "teamradar.qh"
+ #include "t_items.qh"
  #include "../common/buffs.qh"
+ #include "../common/constants.qh"
  #include "../common/counting.qh"
+ #include "../common/deathtypes.qh"
  #include "../common/mapinfo.qh"
  #include "../common/nades.qh"
- #include "../server/t_items.qh"
++
 +#include "../server/mutators/gamemode_ctf.qh"
 +
+ #include "../common/stats.qh"
+ #include "../csqcmodellib/cl_player.qh"
+ #include "../warpzonelib/mathlib.qh"
  /*
  ==================
  Misc HUD functions
@@@ -1486,17 -1498,6 +1501,6 @@@ void HUD_Powerups(void
  // Health/armor (#3)
  //
  
- // prev_* vars contain the health/armor at the previous FRAME
- // set to -1 when player is dead or was not playing
- int prev_health, prev_armor;
- float health_damagetime, armor_damagetime;
- int health_beforedamage, armor_beforedamage;
- // old_p_* vars keep track of previous values when smoothing value changes of the progressbar
- int old_p_health, old_p_armor;
- float old_p_healthtime, old_p_armortime;
- // prev_p_* vars contain the health/armor progressbar value at the previous FRAME
- // set to -1 to forcedly stop effects when we switch spectated player (e.g. from playerX: 70h to playerY: 50h)
- int prev_p_health, prev_p_armor;
  
  void HUD_HealthArmor(void)
  {
@@@ -2625,15 -2626,6 +2629,6 @@@ void HUD_RaceTimer (void
  
  // Vote window (#9)
  //
- int vote_yescount;
- int vote_nocount;
- int vote_needed;
- int vote_highlighted; // currently selected vote
- int vote_active; // is there an active vote?
- int vote_prev; // previous state of vote_active to check for a change
- float vote_alpha;
- float vote_change; // "time" when vote_active changed
  
  void HUD_Vote(void)
  {
@@@ -2865,39 -2857,30 +2860,39 @@@ void HUD_Mod_CA(vector myPos, vector my
  }
  
  // CTF HUD modicon section
 -float redflag_prevframe, blueflag_prevframe; // status during previous frame
 -int redflag_prevstatus, blueflag_prevstatus; // last remembered status
 -float redflag_statuschange_time, blueflag_statuschange_time; // time when the status changed
 +int redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe, neutralflag_prevframe; // status during previous frame
 +int redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus, neutralflag_prevstatus; // last remembered status
 +float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_statuschange_time, neutralflag_statuschange_time; // time when the status changed
  
  void HUD_Mod_CTF_Reset(void)
  {
 -      redflag_prevstatus = blueflag_prevstatus = redflag_prevframe = blueflag_prevframe = redflag_statuschange_time = blueflag_statuschange_time = 0;
 +      redflag_prevstatus = blueflag_prevstatus = yellowflag_prevstatus = pinkflag_prevstatus = neutralflag_prevstatus = 0;
 +      redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = neutralflag_prevframe = 0;
 +      redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0;
  }
  
  void HUD_Mod_CTF(vector pos, vector mySize)
  {
 -      vector redflag_pos, blueflag_pos;
 +      vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos;
        vector flag_size;
        float f; // every function should have that
  
 -      int redflag, blueflag; // current status
 -      float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime; // time since the status changed
 -      int stat_items;
 -
 -      stat_items = getstati(STAT_ITEMS, 0, 24);
 -      redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3;
 -      blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3;
 -
 -      if(redflag || blueflag)
 +      int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status
 +      float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime, neutralflag_statuschange_elapsedtime; // time since the status changed
 +      bool ctf_oneflag; // one-flag CTF mode enabled/disabled
 +      int stat_items = getstati(STAT_CTF_FLAGSTATUS, 0, 24);
 +      float fs, fs2, fs3, size1, size2;
 +      vector e1, e2;
 +
 +      redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3;
 +      blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3;
 +      yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3;
 +      pinkflag = (stat_items/CTF_PINK_FLAG_TAKEN) & 3;
 +      neutralflag = (stat_items/CTF_NEUTRAL_FLAG_TAKEN) & 3;
 +      
 +      ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL);
 +
 +      if(redflag || blueflag || yellowflag || pinkflag || neutralflag)
                mod_active = 1;
        else
                mod_active = 0;
        {
                redflag = 1;
                blueflag = 2;
 +              if(team_count >= 3)
 +                      yellowflag = 2;
 +              if(team_count >= 4)
 +                      pinkflag = 3;
 +              ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor?
        }
  
        // when status CHANGES, set old status into prevstatus and current status into status
                blueflag_prevframe = blueflag;
        }
  
 +      if (yellowflag != yellowflag_prevframe)
 +      {
 +              yellowflag_statuschange_time = time;
 +              yellowflag_prevstatus = yellowflag_prevframe;
 +              yellowflag_prevframe = yellowflag;
 +      }
 +
 +      if (pinkflag != pinkflag_prevframe)
 +      {
 +              pinkflag_statuschange_time = time;
 +              pinkflag_prevstatus = pinkflag_prevframe;
 +              pinkflag_prevframe = pinkflag;
 +      }
 +
 +      if (neutralflag != neutralflag_prevframe)
 +      {
 +              neutralflag_statuschange_time = time;
 +              neutralflag_prevstatus = neutralflag_prevframe;
 +              neutralflag_prevframe = neutralflag;
 +      }
 +
        redflag_statuschange_elapsedtime = time - redflag_statuschange_time;
        blueflag_statuschange_elapsedtime = time - blueflag_statuschange_time;
 +      yellowflag_statuschange_elapsedtime = time - yellowflag_statuschange_time;
 +      pinkflag_statuschange_elapsedtime = time - pinkflag_statuschange_time;
 +      neutralflag_statuschange_elapsedtime = time - neutralflag_statuschange_time;
  
 -      float BLINK_FACTOR = 0.15;
 -      float BLINK_BASE = 0.85;
 +      const float BLINK_FACTOR = 0.15;
 +      const float BLINK_BASE = 0.85;
        // note:
        //   RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2)
        // thus
        //   BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2)
        // ensure RMS == 1
 -      float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
 +      const float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
  
        string red_icon, red_icon_prevstatus;
 -      float red_alpha, red_alpha_prevstatus;
 +      int red_alpha, red_alpha_prevstatus;
        red_alpha = red_alpha_prevstatus = 1;
 -      switch(redflag) {
 +      switch(redflag)
 +      {
                case 1: red_icon = "flag_red_taken"; break;
                case 2: red_icon = "flag_red_lost"; break;
                case 3: red_icon = "flag_red_carrying"; red_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
                default:
 -                      if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_2))
 +                      if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_1))
                                red_icon = "flag_red_shielded";
                        else
                                red_icon = string_null;
                        break;
        }
 -      switch(redflag_prevstatus) {
 +      switch(redflag_prevstatus)
 +      {
                case 1: red_icon_prevstatus = "flag_red_taken"; break;
                case 2: red_icon_prevstatus = "flag_red_lost"; break;
                case 3: red_icon_prevstatus = "flag_red_carrying"; red_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
                default:
                        if(redflag == 3)
                                red_icon_prevstatus = "flag_red_carrying"; // make it more visible
 -                      else if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_2))
 +                      else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_1))
                                red_icon_prevstatus = "flag_red_shielded";
                        else
                                red_icon_prevstatus = string_null;
        }
  
        string blue_icon, blue_icon_prevstatus;
 -      float blue_alpha, blue_alpha_prevstatus;
 +      int blue_alpha, blue_alpha_prevstatus;
        blue_alpha = blue_alpha_prevstatus = 1;
 -      switch(blueflag) {
 +      switch(blueflag)
 +      {
                case 1: blue_icon = "flag_blue_taken"; break;
                case 2: blue_icon = "flag_blue_lost"; break;
                case 3: blue_icon = "flag_blue_carrying"; blue_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
                default:
 -                      if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_1))
 +                      if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_2))
                                blue_icon = "flag_blue_shielded";
                        else
                                blue_icon = string_null;
                        break;
        }
 -      switch(blueflag_prevstatus) {
 +      switch(blueflag_prevstatus)
 +      {
                case 1: blue_icon_prevstatus = "flag_blue_taken"; break;
                case 2: blue_icon_prevstatus = "flag_blue_lost"; break;
                case 3: blue_icon_prevstatus = "flag_blue_carrying"; blue_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
                default:
                        if(blueflag == 3)
                                blue_icon_prevstatus = "flag_blue_carrying"; // make it more visible
 -                      else if((stat_items & IT_CTF_SHIELDED) && (myteam == NUM_TEAM_1))
 +                      else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_2))
                                blue_icon_prevstatus = "flag_blue_shielded";
                        else
                                blue_icon_prevstatus = string_null;
                        break;
        }
  
 -      if(mySize.x > mySize.y) {
 -              if (myteam == NUM_TEAM_1) { // always draw own flag on left
 +      string yellow_icon, yellow_icon_prevstatus;
 +      int yellow_alpha, yellow_alpha_prevstatus;
 +      yellow_alpha = yellow_alpha_prevstatus = 1;
 +      switch(yellowflag)
 +      {
 +              case 1: yellow_icon = "flag_yellow_taken"; break;
 +              case 2: yellow_icon = "flag_yellow_lost"; break;
 +              case 3: yellow_icon = "flag_yellow_carrying"; yellow_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
 +              default:
 +                      if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_3))
 +                              yellow_icon = "flag_yellow_shielded";
 +                      else
 +                              yellow_icon = string_null;
 +                      break;
 +      }
 +      switch(yellowflag_prevstatus)
 +      {
 +              case 1: yellow_icon_prevstatus = "flag_yellow_taken"; break;
 +              case 2: yellow_icon_prevstatus = "flag_yellow_lost"; break;
 +              case 3: yellow_icon_prevstatus = "flag_yellow_carrying"; yellow_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
 +              default:
 +                      if(yellowflag == 3)
 +                              yellow_icon_prevstatus = "flag_yellow_carrying"; // make it more visible
 +                      else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_3))
 +                              yellow_icon_prevstatus = "flag_yellow_shielded";
 +                      else
 +                              yellow_icon_prevstatus = string_null;
 +                      break;
 +      }
 +
 +      string pink_icon, pink_icon_prevstatus;
 +      int pink_alpha, pink_alpha_prevstatus;
 +      pink_alpha = pink_alpha_prevstatus = 1;
 +      switch(pinkflag)
 +      {
 +              case 1: pink_icon = "flag_pink_taken"; break;
 +              case 2: pink_icon = "flag_pink_lost"; break;
 +              case 3: pink_icon = "flag_pink_carrying"; pink_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
 +              default:
 +                      if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_4))
 +                              pink_icon = "flag_pink_shielded";
 +                      else
 +                              pink_icon = string_null;
 +                      break;
 +      }
 +      switch(pinkflag_prevstatus)
 +      {
 +              case 1: pink_icon_prevstatus = "flag_pink_taken"; break;
 +              case 2: pink_icon_prevstatus = "flag_pink_lost"; break;
 +              case 3: pink_icon_prevstatus = "flag_pink_carrying"; pink_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
 +              default:
 +                      if(pinkflag == 3)
 +                              pink_icon_prevstatus = "flag_pink_carrying"; // make it more visible
 +                      else if((stat_items & CTF_SHIELDED) && (myteam != NUM_TEAM_4))
 +                              pink_icon_prevstatus = "flag_pink_shielded";
 +                      else
 +                              pink_icon_prevstatus = string_null;
 +                      break;
 +      }
 +
 +      string neutral_icon, neutral_icon_prevstatus;
 +      int neutral_alpha, neutral_alpha_prevstatus;
 +      neutral_alpha = neutral_alpha_prevstatus = 1;
 +      switch(neutralflag)
 +      {
 +              case 1: neutral_icon = "flag_neutral_taken"; break;
 +              case 2: neutral_icon = "flag_neutral_lost"; break;
 +              case 3: neutral_icon = "flag_neutral_carrying"; neutral_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
 +              default:
 +                      if((stat_items & CTF_SHIELDED))
 +                              neutral_icon = "flag_neutral_shielded";
 +                      else
 +                              neutral_icon = string_null;
 +                      break;
 +      }
 +      switch(neutralflag_prevstatus)
 +      {
 +              case 1: neutral_icon_prevstatus = "flag_neutral_taken"; break;
 +              case 2: neutral_icon_prevstatus = "flag_neutral_lost"; break;
 +              case 3: neutral_icon_prevstatus = "flag_neutral_carrying"; neutral_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break;
 +              default:
 +                      if(neutralflag == 3)
 +                              neutral_icon_prevstatus = "flag_neutral_carrying"; // make it more visible
 +                      else if((stat_items & CTF_SHIELDED))
 +                              neutral_icon_prevstatus = "flag_neutral_shielded";
 +                      else
 +                              neutral_icon_prevstatus = string_null;
 +                      break;
 +      }
 +
 +      if(ctf_oneflag)
 +      {
 +              // hacky, but these aren't needed
 +              red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null;
 +              fs = fs2 = fs3 = 1;
 +      }
 +      else switch(team_count)
 +      {
 +              default:
 +              case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
 +              case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
 +              case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break;
 +      }
 +
 +      if(mySize_x > mySize_y)
 +      {
 +              size1 = mySize_x;
 +              size2 = mySize_y;
 +              e1 = eX;
 +              e2 = eY;
 +      }
 +      else
 +      {
 +              size1 = mySize_y;
 +              size2 = mySize_x;
 +              e1 = eY;
 +              e2 = eX;
 +      }
 +
 +      switch(myteam)
 +      {
 +              default:
 +              case NUM_TEAM_1:
 +              {
                        redflag_pos = pos;
 -                      blueflag_pos = pos + eX * 0.5 * mySize.x;
 -              } else {
 -                      blueflag_pos = pos;
 -                      redflag_pos = pos + eX * 0.5 * mySize.x;
 +                      blueflag_pos = pos + eX * fs2 * size1;
 +                      yellowflag_pos = pos - eX * fs2 * size1;
 +                      pinkflag_pos = pos + eX * fs3 * size1;
 +                      break;
                }
 -              flag_size = eX * 0.5 * mySize.x + eY * mySize.y;
 -      } else {
 -              if (myteam == NUM_TEAM_1) { // always draw own flag on left
 -                      redflag_pos = pos;
 -                      blueflag_pos = pos + eY * 0.5 * mySize.y;
 -              } else {
 +              case NUM_TEAM_2:
 +              {
 +                      redflag_pos = pos + eX * fs2 * size1;
                        blueflag_pos = pos;
 -                      redflag_pos = pos + eY * 0.5 * mySize.y;
 +                      yellowflag_pos = pos - eX * fs2 * size1;
 +                      pinkflag_pos = pos + eX * fs3 * size1;
 +                      break;
 +              }
 +              case NUM_TEAM_3:
 +              {
 +                      redflag_pos = pos + eX * fs3 * size1;
 +                      blueflag_pos = pos - eX * fs2 * size1;
 +                      yellowflag_pos = pos;
 +                      pinkflag_pos = pos + eX * fs2 * size1;
 +                      break;
 +              }
 +              case NUM_TEAM_4:
 +              {
 +                      redflag_pos = pos - eX * fs2 * size1;
 +                      blueflag_pos = pos + eX * fs3 * size1;
 +                      yellowflag_pos = pos + eX * fs2 * size1;
 +                      pinkflag_pos = pos;
 +                      break;
                }
 -              flag_size = eY * 0.5 * mySize.y + eX * mySize.x;
        }
 +      neutralflag_pos = pos;
 +      flag_size = e1 * fs * size1 + e2 * size2;
  
        f = bound(0, redflag_statuschange_elapsedtime*2, 1);
        if(red_icon_prevstatus && f < 1)
                drawpic_aspect_skin_expanding(blueflag_pos, blue_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * blue_alpha_prevstatus, DRAWFLAG_NORMAL, f);
        if(blue_icon)
                drawpic_aspect_skin(blueflag_pos, blue_icon, flag_size, '1 1 1', panel_fg_alpha * blue_alpha * f, DRAWFLAG_NORMAL);
 +
 +      f = bound(0, yellowflag_statuschange_elapsedtime*2, 1);
 +      if(yellow_icon_prevstatus && f < 1)
 +              drawpic_aspect_skin_expanding(yellowflag_pos, yellow_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * yellow_alpha_prevstatus, DRAWFLAG_NORMAL, f);
 +      if(yellow_icon)
 +              drawpic_aspect_skin(yellowflag_pos, yellow_icon, flag_size, '1 1 1', panel_fg_alpha * yellow_alpha * f, DRAWFLAG_NORMAL);
 +
 +      f = bound(0, pinkflag_statuschange_elapsedtime*2, 1);
 +      if(pink_icon_prevstatus && f < 1)
 +              drawpic_aspect_skin_expanding(pinkflag_pos, pink_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * pink_alpha_prevstatus, DRAWFLAG_NORMAL, f);
 +      if(pink_icon)
 +              drawpic_aspect_skin(pinkflag_pos, pink_icon, flag_size, '1 1 1', panel_fg_alpha * pink_alpha * f, DRAWFLAG_NORMAL);
 +
 +      f = bound(0, neutralflag_statuschange_elapsedtime*2, 1);
 +      if(neutral_icon_prevstatus && f < 1)
 +              drawpic_aspect_skin_expanding(neutralflag_pos, neutral_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * neutral_alpha_prevstatus, DRAWFLAG_NORMAL, f);
 +      if(neutral_icon)
 +              drawpic_aspect_skin(neutralflag_pos, neutral_icon, flag_size, '1 1 1', panel_fg_alpha * neutral_alpha * f, DRAWFLAG_NORMAL);
  }
  
  // Keyhunt HUD modicon section
@@@ -4115,6 -3909,7 +4110,7 @@@ void HUD_InfoMessages(void
  //
  vector acc_prevspeed;
  float acc_prevtime, acc_avg, top_speed, top_speed_time;
+ float physics_update_time, discrete_speed, discrete_acceleration;
  void HUD_Physics(void)
  {
        if(!autocvar__hud_configure)
                acc_prevspeed = vel;
                acc_prevtime = time;
  
-               f = bound(0, f * 10, 1);
-               acc_avg = acc_avg * (1 - f) + acceleration * f;
+               if(autocvar_hud_panel_physics_acceleration_movingaverage)
+               {
+                       f = bound(0, f * 10, 1);
+                       acc_avg = acc_avg * (1 - f) + acceleration * f;
+                       acceleration = acc_avg;
+               }
+       }
+       int acc_decimals = 2;
+       if(time > physics_update_time)
+       {
+               // workaround for ftos_decimals returning a negative 0
+               if(discrete_acceleration > -1 / pow(10, acc_decimals) && discrete_acceleration < 0)
+                       discrete_acceleration = 0;
+               discrete_acceleration = acceleration;
+               discrete_speed = speed;
+               physics_update_time += autocvar_hud_panel_physics_update_interval;
        }
  
        //compute layout
                //else
                        //tmp_offset_x = 0;
                tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
-               drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(speed), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+               drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(discrete_speed), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
  
                //draw speed unit
                if (speed_baralign)
  
                f = acceleration/autocvar_hud_panel_physics_acceleration_max;
                if (autocvar_hud_panel_physics_acceleration_progressbar_nonlinear)
-                       f = sqrt(f);
+                       f = (f >= 0 ? sqrt(f) : -sqrt(-f));
  
                if (acceleration_progressbar_scale) // allow progressbar to go out of panel bounds
                {
  
                HUD_Panel_DrawProgressBar(panel_pos + acceleration_offset + tmp_offset, tmp_size, "accelbar", f, 0, acceleration_baralign, progressbar_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
        }
-       tmp_size.x = panel_size.x;
-       tmp_size.y = panel_size.y * text_scale;
-       tmp_offset.x = 0;
-       tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
-       if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3)
-               drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(acceleration, 2), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       if(autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3)
+       {
+               tmp_size.x = panel_size.x;
+               tmp_size.y = panel_size.y * text_scale;
+               tmp_offset.x = 0;
+               tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
+               drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(discrete_acceleration, acc_decimals), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       }
  
        draw_endBoldFont();
  }
@@@ -4502,15 -4316,23 +4517,23 @@@ void HUD_CenterPrint (void
                        reset_centerprint_messages();
                if (time > hud_configure_cp_generation_time)
                {
-                       float r;
-                       r = random();
-                       if (r > 0.75)
-                               centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10);
-                       else if (r > 0.5)
-                               centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0);
+                       if(HUD_PANEL(CENTERPRINT) == highlightedPanel)
+                       {
+                               float r;
+                               r = random();
+                               if (r > 0.8)
+                                       centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10);
+                               else if (r > 0.55)
+                                       centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0);
+                               else
+                                       centerprint_hud(sprintf("Message at time %s", seconds_tostring(time)));
+                               hud_configure_cp_generation_time = time + 1 + random()*4;
+                       }
                        else
-                               centerprint_hud(sprintf("Message at time %s", seconds_tostring(time)));
-                       hud_configure_cp_generation_time = time + 1 + random()*4;
+                       {
+                               centerprint_generic(0, sprintf("Centerprint message", seconds_tostring(time)), 10, 0);
+                               hud_configure_cp_generation_time = time + 10 - random()*3;
+                       }
                }
        }
  
index 1cef3616482f7f93db5010f75d2871cb52115a35,aed0865dda1e0152b3cdc87c745a21ee9f31c0b9..d42fc49963cefe8b4c54a0331556b2e7dc233601
@@@ -1,4 -1,20 +1,20 @@@
  #include "waypointsprites.qh"
+ #include "_all.qh"
+ #include "hud.qh"
+ #include "teamradar.qh"
+ #include "../common/buffs.qh"
+ #include "../common/constants.qh"
+ #include "../common/teams.qh"
+ #include "../common/weapons/all.qh"
+ #include "../csqcmodellib/interpolate.qh"
+ #include "../warpzonelib/mathlib.qh"
+ .float alpha;
  
  void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
  {
@@@ -204,9 -220,6 +220,9 @@@ string spritelookuptext(string s
                case "keycarrier-red": return _("Key carrier");
                case "keycarrier-yellow": return _("Key carrier");
                case "redbase": return _("Red base");
 +              case "yellowbase": return _("Yellow base");
 +              case "neutralbase": return _("White base");
 +              case "pinkbase": return _("Pink base");
                case "waypoint": return _("Waypoint");
                case "ons-gen-red": return _("Generator");
                case "ons-gen-blue": return _("Generator");
index d582a560fba6c93b233c4976657953c1a5307194,83526614ab5e3e7b6ec5e6f9f481797bb5c9df2c..a4fc55b38117a093d4c48b053da91962862bc384
@@@ -3,6 -3,7 +3,7 @@@
  
  #include "constants.qh"
  #include "teams.qh"
+ #include "util.qh"
  
  // ================================================
  //  Unified notification system, written by Samual
@@@ -346,34 -347,25 +347,34 @@@ void Send_Notification_WOCOVA
  
  #define MSG_INFO_NOTIFICATIONS \
      MSG_INFO_NOTIF(2, INFO_CHAT_NOSPECTATORS,              0, 0, "", "",                            "",                     _("^F4NOTE: ^BGSpectator chat is not sent to players during the match"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_, 4,                1, 0, "s1", "s1",                        "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_BROKEN_, 4,         2, 2, "s1 f1p2dec s2 f2p2dec", "s1",     "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG%s^BG's previous record of ^F2%s^BG seconds"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_CAPTURE_NEUTRAL,            1, 0, "s1", "s1",                        "notify_%s_captured",   _("^BG%s^BG captured the flag"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_TIME_, 4,           1, 1, "s1 f1p2dec", "s1",                "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_UNBROKEN_, 4,       2, 2, "s1 f1p2dec s2 f2p2dec", "s1",     "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F2%s^BG seconds, failing to break ^BG%s^BG's previous record of ^F1%s^BG seconds"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_ABORTRUN_, 4,    0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag was returned to base by its owner"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL,0, 0, "", "",                            "",                     _("^BGThe flag was returned by its owner"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DAMAGED_, 4,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag was destroyed and returned to base"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL, 0, 0, "", "",                            "",                     _("^BGThe flag was destroyed and returned to base"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DROPPED_, 4,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL, 0, 0, "", "",                            "",                     _("^BGThe flag was dropped in the base and returned itself"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_NEEDKILL_, 4,    0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL,0, 0, "", "",                            "",                     _("^BGThe flag fell somewhere it couldn't be reached and returned to base"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_SPEEDRUN_, 4,    0, 1, "f1p2dec", "",                     "",                     _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL,0, 1, "f1p2dec", "",                     "",                     _("^BGThe flag became impatient after ^F1%.2f^BG seconds and returned itself"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_TIMEOUT_, 4,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag has returned to the base"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL, 0, 0, "", "",                            "",                     _("^BGThe flag has returned to the base"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_LOST_, 4,                   1, 0, "s1", "s1",                        "notify_%s_lost",       _("^BG%s^BG lost the ^TC^TT^BG flag"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_LOST_NEUTRAL,               1, 0, "s1", "s1",                        "notify_%s_lost",       _("^BG%s^BG lost the flag"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_PICKUP_, 4,                 1, 0, "s1", "s1",                        "notify_%s_taken",      _("^BG%s^BG got the ^TC^TT^BG flag"), "") \
 +    MSG_INFO_NOTIF(1, INFO_CTF_PICKUP_NEUTRAL,             1, 0, "s1", "s1",                        "notify_%s_taken",      _("^BG%s^BG got the flag"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_RETURN_, 4,                 1, 0, "s1", "s1",                        "notify_%s_returned",   _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
 +    MULTITEAM_INFO(1, INFO_CTF_RETURN_MONSTER_, 4,         1, 0, "s1", "s1",                        "notify_%s_returned",   _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
      MSG_INFO_NOTIF(2, INFO_COINTOSS,                       1, 0, "s1", "",                          "",                     _("^F2Throwing coin... Result: %s^F2!"), "") \
      MSG_INFO_NOTIF(1, INFO_JETPACK_NOFUEL,                 0, 0, "", "",                            "",                     _("^BGYou don't have any fuel for the ^F1Jetpack"), "") \
      MSG_INFO_NOTIF(2, INFO_SUPERSPEC_MISSING_UID,          0, 0, "", "",                            "",                     _("^F2You lack a UID, superspec options will not be saved/restored"), "") \
      MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE,                   0, 0, "", "",                            "",                     _("^F1Round already started, you will join the game in the next round"), "") \
      MSG_INFO_NOTIF(1, INFO_CA_LEAVE,                       0, 0, "", "",                            "",                     _("^F2You will spectate in the next round"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_, 2,                1, 0, "s1", "s1",                        "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_BROKEN_, 2,         2, 2, "s1 f1p2dec s2 f2p2dec", "s1",     "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG%s^BG's previous record of ^F2%s^BG seconds"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_TIME_, 2,           1, 1, "s1 f1p2dec", "s1",                "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_CAPTURE_UNBROKEN_, 2,       2, 2, "s1 f1p2dec s2 f2p2dec", "s1",     "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F2%s^BG seconds, failing to break ^BG%s^BG's previous record of ^F1%s^BG seconds"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_ABORTRUN_, 2,    0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag was returned to base by its owner"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DAMAGED_, 2,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag was destroyed and returned to base"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DROPPED_, 2,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_NEEDKILL_, 2,    0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_SPEEDRUN_, 2,    0, 1, "f1p2dec", "",                     "",                     _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_TIMEOUT_, 2,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG flag has returned to the base"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_LOST_, 2,                   1, 0, "s1", "s1",                        "notify_%s_lost",       _("^BG%s^BG lost the ^TC^TT^BG flag"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_PICKUP_, 2,                 1, 0, "s1", "s1",                        "notify_%s_taken",      _("^BG%s^BG got the ^TC^TT^BG flag"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_RETURN_, 2,                 1, 0, "s1", "s1",                        "notify_%s_returned",   _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
 -    MULTITEAM_INFO(1, INFO_CTF_RETURN_MONSTER_, 2,         1, 0, "s1", "s1",                        "notify_%s_returned",   _("^BG%s^BG returned the ^TC^TT^BG flag"), "") \
      MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_CHEAT,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was unfairly eliminated by ^BG%s^K1%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_DROWN,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_water",         _("^BG%s%s^K1 was drowned by ^BG%s^K1%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FALL,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_fall",          _("^BG%s%s^K1 was grounded by ^BG%s^K1%s%s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CAMPCHECK,                   0, 0, "",             CPID_CAMPCHECK,      "0 0", _("^F2Don't camp!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_COINTOSS,                    1, 0, "s1",           NO_CPID,             "0 0", _("^F2Throwing coin... Result: %s^F2!"), "") \
      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_CTF_CAPTURESHIELD_INACTIVE,  0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGThis flag is currently inactive"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_SHIELDED,  0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGYou are now ^F1shielded^BG from the flag(s)\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_CAPTURE_, 4,             0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou captured the ^TC^TT^BG flag!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURE_NEUTRAL,         0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou captured the flag!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_FLAG_THROW_PUNISH,       0, 1, "f1secs",       CPID_CTF_LOWPRIO,    "0 0", _("^BGToo many flag throws! Throwing disabled for %s."), "") \
 -    MULTITEAM_CENTER(1, CENTER_CTF_PASS_OTHER_, 2,          2, 0, "s1 s2",        CPID_CTF_PASS,       "0 0", _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
 -    MULTITEAM_CENTER(1, CENTER_CTF_PASS_RECEIVED_, 2,       1, 0, "s1",           CPID_CTF_PASS,       "0 0", _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_PASS_OTHER_, 4,          2, 0, "s1 s2",        CPID_CTF_PASS,       "0 0", _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_OTHER_NEUTRAL,      2, 0, "s1 s2",        CPID_CTF_PASS,       "0 0", _("^BG%s^BG passed the flag to %s"), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_PASS_RECEIVED_, 4,       1, 0, "s1",           CPID_CTF_PASS,       "0 0", _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_RECEIVED_NEUTRAL,   1, 0, "s1",           CPID_CTF_PASS,       "0 0", _("^BGYou received the flag from %s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_REQUESTED,          1, 0, "s1 pass_key",  CPID_CTF_PASS,       "0 0", _("^BG%s^BG requests you to pass the flag%s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_REQUESTING,         1, 0, "s1",           CPID_CTF_PASS,       "0 0", _("^BGRequesting %s^BG to pass you the flag"), "") \
 -    MULTITEAM_CENTER(1, CENTER_CTF_PASS_SENT_, 2,           1, 0, "s1",           CPID_CTF_PASS,       "0 0", _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
 -    MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_, 2,              0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou got the ^TC^TT^BG flag!"), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_PASS_SENT_, 4,           1, 0, "s1",           CPID_CTF_PASS,       "0 0", _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PASS_SENT_NEUTRAL,       1, 0, "s1",           CPID_CTF_PASS,       "0 0", _("^BGYou passed the flag to %s"), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_, 4,              0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou got the ^TC^TT^BG flag!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_NEUTRAL,          0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou got the flag!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM,             1, 0, "s1",           CPID_CTF_LOWPRIO,    "0 0", _("^BGYou got your %steam^BG's flag, return it!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_ENEMY,       1, 0, "s1",           CPID_CTF_LOWPRIO,    "0 0", _("^BGYou got the %senemy^BG's flag, return it!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY,            1, 0, "s1",           CPID_CTF_LOWPRIO,    "0 0", _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_VERBOSE,    2, 0, "s1 s2 s1",     CPID_CTF_LOWPRIO,    "0 0", _("^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!"), "") \
 -    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM,             1, 0, "s1",           CPID_CTF_LOWPRIO,    "0 0", _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
 -    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_VERBOSE,     2, 0, "s1 s2 s1",     CPID_CTF_LOWPRIO,    "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
 -    MULTITEAM_CENTER(1, CENTER_CTF_RETURN_, 2,              0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou returned the ^TC^TT^BG flag!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_NEUTRAL,    1, 0, "s1",           CPID_CTF_LOWPRIO,    "0 0", _("^BGThe %senemy^BG got the flag! Retrieve it!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_NEUTRAL_VERBOSE, 2, 0, "s1 s2 s1",CPID_CTF_LOWPRIO,    "0 0", _("^BGThe %senemy (^BG%s%s)^BG got the flag! Retrieve it!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_TEAM,        1, 0, "s1",          CPID_CTF_LOWPRIO,    "0 0", _("^BGThe %senemy^BG got their flag! Retrieve it!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_ENEMY_TEAM_VERBOSE,2, 0, "s1 s2 s1",    CPID_CTF_LOWPRIO,    "0 0", _("^BGThe %senemy (^BG%s%s)^BG got their flag! Retrieve it!"), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_TEAM_, 4,         1, 0, "s1",           CPID_CTF_LOWPRIO,    "0 0", _("^BGYour %steam mate^BG got the ^TC^TT^BG flag! Protect them!"), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_PICKUP_TEAM_VERBOSE_,    4, 2, 0, "s1 s2 s1",  CPID_CTF_LOWPRIO,    "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG flag! Protect them!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_NEUTRAL,         1, 0, "s1",       CPID_CTF_LOWPRIO,    "0 0", _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_CTF_PICKUP_TEAM_VERBOSE_NEUTRAL, 2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO,    "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
 +    MULTITEAM_CENTER(1, CENTER_CTF_RETURN_, 4,              0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou returned the ^TC^TT^BG flag!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_STALEMATE_CARRIER,       0, 0, "",             CPID_STALEMATE,      "0 0", _("^BGStalemate! Enemies can now see you on radar!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_STALEMATE_OTHER,         0, 0, "",             CPID_STALEMATE,      "0 0", _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_FRAG,                 1, 1, "spree_cen s1",             NO_CPID, "0 0", _("^K3%sYou fragged ^BG%s"), _("^K3%sYou scored against ^BG%s")) \
      MULTITEAM_CHOICE##teams(default,challow,prefix,chtype,optiona,optionb)
  
  #define MSG_CHOICE_NOTIFICATIONS \
 -    MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_BROKEN_, 2,    MSG_INFO,    INFO_CTF_CAPTURE_,                INFO_CTF_CAPTURE_BROKEN_) \
 -    MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_TIME_, 2,      MSG_INFO,    INFO_CTF_CAPTURE_,                INFO_CTF_CAPTURE_TIME_) \
 -    MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_UNBROKEN_, 2,  MSG_INFO,    INFO_CTF_CAPTURE_,                INFO_CTF_CAPTURE_UNBROKEN_) \
 -    MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_TEAM,           MSG_CENTER,  CENTER_CTF_PICKUP_TEAM,           CENTER_CTF_PICKUP_TEAM_VERBOSE) \
 +    MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_BROKEN_, 4,    MSG_INFO,    INFO_CTF_CAPTURE_,                INFO_CTF_CAPTURE_BROKEN_) \
 +    MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_TIME_, 4,      MSG_INFO,    INFO_CTF_CAPTURE_,                INFO_CTF_CAPTURE_TIME_) \
 +    MULTITEAM_CHOICE(1, 2, CHOICE_CTF_CAPTURE_UNBROKEN_, 4,  MSG_INFO,    INFO_CTF_CAPTURE_,                INFO_CTF_CAPTURE_UNBROKEN_) \
 +    MULTITEAM_CHOICE(1, 2, CHOICE_CTF_PICKUP_TEAM_, 4,       MSG_CENTER,  CENTER_CTF_PICKUP_TEAM_,          CENTER_CTF_PICKUP_TEAM_VERBOSE_) \
 +    MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_TEAM_NEUTRAL,   MSG_CENTER,  CENTER_CTF_PICKUP_TEAM_NEUTRAL,   CENTER_CTF_PICKUP_TEAM_VERBOSE_NEUTRAL) \
      MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_ENEMY,          MSG_CENTER,  CENTER_CTF_PICKUP_ENEMY,          CENTER_CTF_PICKUP_ENEMY_VERBOSE) \
 +    MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL,  MSG_CENTER,  CENTER_CTF_PICKUP_ENEMY_NEUTRAL,  CENTER_CTF_PICKUP_ENEMY_NEUTRAL_VERBOSE) \
 +    MSG_CHOICE_NOTIF(1, 2, CHOICE_CTF_PICKUP_ENEMY_TEAM,     MSG_CENTER,  CENTER_CTF_PICKUP_ENEMY_TEAM,     CENTER_CTF_PICKUP_ENEMY_TEAM_VERBOSE) \
      MSG_CHOICE_NOTIF(1, 1, CHOICE_FRAG,                      MSG_CENTER,  CENTER_DEATH_MURDER_FRAG,         CENTER_DEATH_MURDER_FRAG_VERBOSE) \
      MSG_CHOICE_NOTIF(1, 1, CHOICE_FRAGGED,                   MSG_CENTER,  CENTER_DEATH_MURDER_FRAGGED,      CENTER_DEATH_MURDER_FRAGGED_VERBOSE) \
      MSG_CHOICE_NOTIF(1, 1, CHOICE_TYPEFRAG,                  MSG_CENTER,  CENTER_DEATH_MURDER_TYPEFRAG,     CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE) \
@@@ -1346,9 -1321,9 +1347,9 @@@ float NOTIF_CHOICE_COUNT
  // notification limits -- INCREASE AS NECESSARY
  const float NOTIF_ANNCE_MAX   = 100;
  const float NOTIF_INFO_MAX    = 300;
 -const float NOTIF_CENTER_MAX  = 200;
 +const float NOTIF_CENTER_MAX  = 250;
  const float NOTIF_MULTI_MAX   = 200;
 -const float NOTIF_CHOICE_MAX  = 20;
 +const float NOTIF_CHOICE_MAX  = 30;
  
  // notification entities
  entity msg_annce_notifs[NOTIF_ANNCE_MAX];
diff --combined qcsrc/common/stats.qh
index 28ec85e30a7cedf7d8c485ba1365403d6f35336e,f888e1b2c8a10e00668841e8048c2eb9119b8549..547f4b3b669029f8274cbfa30cdadecbce3d978b
@@@ -5,23 -5,38 +5,38 @@@
  // 255 is the current limit (MAX_CL_STATS - 1), engine will need to be modified if you wish to add more stats
  
  const int MAX_CL_STATS                = 256;
- const int STAT_HEALTH                 = 0;
+ // -Wdouble-declaration
+ // const int STAT_HEALTH                 = 0;
  // 1 empty?
  const int STAT_WEAPON                 = 2;
- const int STAT_AMMO                   = 3;
- const int STAT_ARMOR                  = 4;
- const int STAT_WEAPONFRAME            = 5;
- const int STAT_SHELLS                 = 6;
- const int STAT_NAILS                  = 7;
- const int STAT_ROCKETS                = 8;
- const int STAT_CELLS                  = 9;
- const int STAT_ACTIVEWEAPON           = 10;
- const int STAT_TOTALSECRETS           = 11;
- const int STAT_TOTALMONSTERS          = 12;
- const int STAT_SECRETS                = 13;
- const int STAT_MONSTERS               = 14;
- const int STAT_ITEMS                  = 15;
- const int STAT_VIEWHEIGHT             = 16;
+ // -Wdouble-declaration
+ // const int STAT_AMMO                   = 3;
+ // -Wdouble-declaration
+ // const int STAT_ARMOR                  = 4;
+ // -Wdouble-declaration
+ // const int STAT_WEAPONFRAME            = 5;
+ // -Wdouble-declaration
+ // const int STAT_SHELLS                 = 6;
+ // -Wdouble-declaration
+ // const int STAT_NAILS                  = 7;
+ // -Wdouble-declaration
+ // const int STAT_ROCKETS                = 8;
+ // -Wdouble-declaration
+ // const int STAT_CELLS                  = 9;
+ // -Wdouble-declaration
+ // const int STAT_ACTIVEWEAPON           = 10;
+ // -Wdouble-declaration
+ // const int STAT_TOTALSECRETS           = 11;
+ // -Wdouble-declaration
+ // const int STAT_TOTALMONSTERS          = 12;
+ // -Wdouble-declaration
+ // const int STAT_SECRETS                = 13;
+ // -Wdouble-declaration
+ // const int STAT_MONSTERS               = 14;
+ // -Wdouble-declaration
+ // const int STAT_ITEMS                  = 15;
+ // -Wdouble-declaration
+ // const int STAT_VIEWHEIGHT             = 16;
  // 17 empty?
  // 18 empty?
  // 19 empty?
@@@ -92,7 -107,7 +107,7 @@@ const int STAT_HEALING_ORB_ALPHA      
  const int STAT_PLASMA                 = 84;
  const int STAT_OK_AMMO_CHARGE         = 85;
  const int STAT_OK_AMMO_CHARGEPOOL     = 86;
 -// 87 empty?
 +const int STAT_CTF_FLAGSTATUS         = 87;
  // 88 empty?
  // 89 empty?
  // 90 empty?
@@@ -270,14 -285,19 +285,19 @@@ const int STAT_MOVEVARS_AIRSTOPACCELERA
  const int STAT_MOVEVARS_AIRSTRAFEACCELERATE           = 232;
  const int STAT_MOVEVARS_MAXAIRSTRAFESPEED             = 233;
  const int STAT_MOVEVARS_AIRCONTROL                    = 234;
- const int STAT_FRAGLIMIT                              = 235;
- const int STAT_TIMELIMIT                              = 236;
+ // -Wdouble-declaration
+ // const int STAT_FRAGLIMIT                              = 235;
+ // -Wdouble-declaration
+ // const int STAT_TIMELIMIT                              = 236;
  const int STAT_MOVEVARS_WALLFRICTION                  = 237;
  const int STAT_MOVEVARS_FRICTION                      = 238;
  const int STAT_MOVEVARS_WATERFRICTION                 = 239;
- const int STAT_MOVEVARS_TICRATE                       = 240;
- const int STAT_MOVEVARS_TIMESCALE                     = 241;
- const int STAT_MOVEVARS_GRAVITY                       = 242;
+ // -Wdouble-declaration
+ // const int STAT_MOVEVARS_TICRATE                       = 240;
+ // -Wdouble-declaration
+ // const int STAT_MOVEVARS_TIMESCALE                     = 241;
+ // -Wdouble-declaration
+ // const int STAT_MOVEVARS_GRAVITY                       = 242;
  const int STAT_MOVEVARS_STOPSPEED                     = 243;
  const int STAT_MOVEVARS_MAXSPEED                      = 244;
  const int STAT_MOVEVARS_SPECTATORMAXSPEED             = 245;
index 22b54efff2a455c32a58f37142c9798b39089651,d8a4e357bbde7ea69a37477d28e710bcb6956a6e..c870adc849026e3d0bee13a9819faf68872bfd19
@@@ -1,3 -1,15 +1,15 @@@
+ #include "gamemode_ctf.qh"
+ #include "../_all.qh"
+ #include "gamemode.qh"
+ #ifdef SVQC
+ #include "../vehicles/vehicle.qh"
+ #endif
+ #include "../../warpzonelib/common.qh"
+ #include "../../warpzonelib/mathlib.qh"
  // ================================================================
  //  Official capture the flag game mode coding, reworked by Samual
  //  Last updated: September, 2012
@@@ -14,11 -26,10 +26,11 @@@ void ctf_FakeTimeLimit(entity e, float 
                WriteCoord(MSG_ONE, (t + 1) / 60);
  }
  
 -void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later
 +void ctf_EventLog(string mode, int flagteam, entity actor) // use an alias for easy changing and quick editing later
  {
        if(autocvar_sv_eventlog)
 -              GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
 +              GameLogEcho(sprintf(":ctf:%s:%d:%d:%s", mode, flagteam, actor.team, ((actor != world) ? ftos(actor.playerid) : "")));
 +              //GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
  }
  
  void ctf_CaptureRecord(entity flag, entity player)
        string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
  
        // notify about shit
 -      if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_TIME_), player.netname, (cap_time * 100)); }
 -      else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_BROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
 -      else { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_UNBROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
 +      if(ctf_oneflag) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname); }
 +      else if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_TIME_), player.netname, (cap_time * 100)); }
 +      else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_BROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
 +      else { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_UNBROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
  
        // write that shit in the database
 +      if(!ctf_oneflag) // but not in 1-flag mode
        if((!ctf_captimerecord) || (cap_time < cap_record))
        {
                ctf_captimerecord = cap_time;
@@@ -83,7 -92,7 +95,7 @@@ void ctf_CalculatePassVelocity(entity f
        else { flag.velocity = (desired_direction * autocvar_g_ctf_pass_velocity); }
  }
  
 -float ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer)
 +bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer)
  {
        if(autocvar_g_ctf_pass_directional_max || autocvar_g_ctf_pass_directional_min)
        {
  // CaptureShield Functions
  // =======================
  
 -float ctf_CaptureShield_CheckStatus(entity p)
 +bool ctf_CaptureShield_CheckStatus(entity p)
  {
 -      float s, se;
 +      int s, s2, s3, s4, se, se2, se3, se4, sr, ser;
        entity e;
 -      float players_worseeq, players_total;
 +      int players_worseeq, players_total;
  
        if(ctf_captureshield_max_ratio <= 0)
                return false;
  
 -      s = PlayerScore_Add(p, SP_SCORE, 0);
 -      if(s >= -ctf_captureshield_min_negscore)
 +      s = PlayerScore_Add(p, SP_CTF_CAPS, 0);
 +      s2 = PlayerScore_Add(p, SP_CTF_PICKUPS, 0);
 +      s3 = PlayerScore_Add(p, SP_CTF_RETURNS, 0);
 +      s4 = PlayerScore_Add(p, SP_CTF_FCKILLS, 0);
 +
 +      sr = ((s - s2) + (s3 + s4));
 +
 +      if(sr >= -ctf_captureshield_min_negscore)
                return false;
  
        players_total = players_worseeq = 0;
        {
                if(DIFF_TEAM(e, p))
                        continue;
 -              se = PlayerScore_Add(e, SP_SCORE, 0);
 -              if(se <= s)
 +              se = PlayerScore_Add(e, SP_CTF_CAPS, 0);
 +              se2 = PlayerScore_Add(e, SP_CTF_PICKUPS, 0);
 +              se3 = PlayerScore_Add(e, SP_CTF_RETURNS, 0);
 +              se4 = PlayerScore_Add(e, SP_CTF_FCKILLS, 0);
 +
 +              ser = ((se - se2) + (se3 + se4));
 +
 +              if(ser <= sr)
                        ++players_worseeq;
                ++players_total;
        }
        return true;
  }
  
 -void ctf_CaptureShield_Update(entity player, float wanted_status)
 +void ctf_CaptureShield_Update(entity player, bool wanted_status)
  {
 -      float updated_status = ctf_CaptureShield_CheckStatus(player);
 +      bool updated_status = ctf_CaptureShield_CheckStatus(player);
        if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only
        {
                Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((updated_status) ? CENTER_CTF_CAPTURESHIELD_SHIELDED : CENTER_CTF_CAPTURESHIELD_FREE));
        }
  }
  
 -float ctf_CaptureShield_Customize()
 +bool ctf_CaptureShield_Customize()
  {
        if(!other.ctf_captureshielded) { return false; }
 -      if(SAME_TEAM(self, other)) { return false; }
 +      if(CTF_SAMETEAM(self, other)) { return false; }
  
        return true;
  }
  void ctf_CaptureShield_Touch()
  {
        if(!other.ctf_captureshielded) { return; }
 -      if(SAME_TEAM(self, other)) { return; }
 +      if(CTF_SAMETEAM(self, other)) { return; }
  
        vector mymid = (self.absmin + self.absmax) * 0.5;
        vector othermid = (other.absmin + other.absmax) * 0.5;
@@@ -217,7 -214,7 +229,7 @@@ void ctf_CaptureShield_Spawn(entity fla
  // Drop/Pass/Throw Code
  // ====================
  
 -void ctf_Handle_Drop(entity flag, entity player, float droptype)
 +void ctf_Handle_Drop(entity flag, entity player, int droptype)
  {
        // declarations
        player = (player ? player : flag.pass_sender);
        flag.ctf_status = FLAG_DROPPED;
  
        // messages and sounds
 -      Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_LOST_), player.netname);
 +      Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_LOST_) : INFO_CTF_LOST_NEUTRAL), player.netname);
        sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("dropped", player.team, player);
  
@@@ -294,11 -291,11 +306,11 @@@ void ctf_Handle_Retrieve(entity flag, e
        FOR_EACH_REALPLAYER(tmp_player)
        {
                if(tmp_player == sender)
 -                      Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_SENT_), player.netname);
 +                      Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_SENT_) : CENTER_CTF_PASS_SENT_NEUTRAL), player.netname);
                else if(tmp_player == player)
 -                      Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_RECEIVED_), sender.netname);
 +                      Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_RECEIVED_) : CENTER_CTF_PASS_RECEIVED_NEUTRAL), sender.netname);
                else if(SAME_TEAM(tmp_player, sender))
 -                      Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_OTHER_), sender.netname, player.netname);
 +                      Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_OTHER_) : CENTER_CTF_PASS_OTHER_NEUTRAL), sender.netname, player.netname);
        }
  
        // create new waypoint
        flag.pass_target = world;
  }
  
 -void ctf_Handle_Throw(entity player, entity receiver, float droptype)
 +void ctf_Handle_Throw(entity player, entity receiver, int droptype)
  {
        entity flag = player.flagcarried;
        vector targ_origin, flag_velocity;
  // Event Handlers
  // ==============
  
 -void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
 +void ctf_Handle_Capture(entity flag, entity toucher, int capturetype)
  {
        entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher);
        entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper);
 +      entity player_team_flag = world, tmp_entity;
        float old_time, new_time;
  
 -      if (!player) { return; } // without someone to give the reward to, we can't possibly cap
 +      if(!player) { return; } // without someone to give the reward to, we can't possibly cap
 +      if(CTF_DIFFTEAM(player, flag)) { return; }
 +      
 +      if(ctf_oneflag)
 +      for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
 +      if(SAME_TEAM(tmp_entity, player))
 +      {
 +              player_team_flag = tmp_entity;
 +              break;
 +      }
  
        nades_GiveBonus(player, autocvar_g_nades_bonus_score_high );
  
 +      player.throw_prevtime = time;
 +      player.throw_count = 0;
 +
        // messages and sounds
 -      Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(enemy_flag, CENTER_CTF_CAPTURE_));
 +      Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT_4(enemy_flag, CENTER_CTF_CAPTURE_) : CENTER_CTF_CAPTURE_NEUTRAL));
        ctf_CaptureRecord(enemy_flag, player);
 -      sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTEN_NONE);
 +      sound(player, CH_TRIGGER, ((ctf_oneflag) ? player_team_flag.snd_flag_capture : ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture)), VOL_BASE, ATTEN_NONE);
  
        switch(capturetype)
        {
@@@ -470,12 -454,12 +482,12 @@@ void ctf_Handle_Return(entity flag, ent
        // messages and sounds
        if(player.flags & FL_MONSTER)
        {
 -              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name);
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name);
        }
 -      else
 +      else if(flag.team)
        {
 -              Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_RETURN_));
 -              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_), player.netname);
 +              Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(flag, CENTER_CTF_RETURN_));
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_), player.netname);
        }
        sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("return", flag.team, player);
                flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time
        }
  
 +      // other
 +      if(player.flagcarried == flag)
 +              WaypointSprite_Kill(player.wps_flagcarrier);
 +
        // reset the flag
        ctf_RespawnFlag(flag);
  }
  
 -void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
 +void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
  {
        // declarations
        float pickup_dropped_score; // used to calculate dropped pickup score
 +      entity tmp_entity; // temporary entity
  
        // attach the flag to the player
        flag.owner = player;
        }
  
        // messages and sounds
 -      Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_PICKUP_), player.netname);
 -      Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PICKUP_));
 +      Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_PICKUP_) : INFO_CTF_PICKUP_NEUTRAL), player.netname);
        if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); }
 -
 -      Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, CHOICE_CTF_PICKUP_TEAM, Team_ColorCode(player.team), player.netname);
 -      Send_Notification(NOTIF_TEAM, flag, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY, Team_ColorCode(player.team), player.netname);
 +      if(!flag.team) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL); }
 +      else if(CTF_DIFFTEAM(player, flag)) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(flag, CENTER_CTF_PICKUP_)); }
 +      else { Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team)); }
 +
 +      Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, ((flag.team) ? APP_TEAM_ENT_4(flag, CHOICE_CTF_PICKUP_TEAM_) : CHOICE_CTF_PICKUP_TEAM_NEUTRAL), Team_ColorCode(player.team), player.netname);
 +      
 +      if(!flag.team)
 +      FOR_EACH_PLAYER(tmp_entity)
 +      if(tmp_entity != player)
 +      if(DIFF_TEAM(player, tmp_entity))
 +              Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname);
 +      
 +      if(flag.team)
 +      FOR_EACH_PLAYER(tmp_entity)
 +      if(tmp_entity != player)
 +      if(CTF_SAMETEAM(flag, tmp_entity))
 +      if(SAME_TEAM(player, tmp_entity))
 +              Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_PICKUP_TEAM_), Team_ColorCode(player.team), player.netname);
 +      else
 +              Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname);
  
        sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE);
  
  // Main Flag Functions
  // ===================
  
 -void ctf_CheckFlagReturn(entity flag, float returntype)
 +void ctf_CheckFlagReturn(entity flag, int returntype)
  {
        if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING))
        {
                {
                        switch(returntype)
                        {
 -                              case RETURN_DROPPED: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_DROPPED_)); break;
 -                              case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_DAMAGED_)); break;
 -                              case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_), ctf_captimerecord); break;
 -                              case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_NEEDKILL_)); break;
 +                              case RETURN_DROPPED: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DROPPED_) : INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL)); break;
 +                              case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DAMAGED_) : INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL)); break;
 +                              case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_) : INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL), ctf_captimerecord); break;
 +                              case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_NEEDKILL_) : INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL)); break;
  
                                default:
                                case RETURN_TIMEOUT:
 -                                      { Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_TIMEOUT_)); break; }
 +                                      { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_TIMEOUT_) : INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL)); break; }
                        }
                        sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE);
                        ctf_EventLog("returned", flag.team, world);
        }
  }
  
 +bool ctf_Stalemate_Customize()
 +{
 +      // make spectators see what the player would see
 +      entity e, wp_owner;
 +      e = WaypointSprite_getviewentity(other);
 +      wp_owner = self.owner;
 +
 +      // team waypoints
 +      if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; }
 +      if(SAME_TEAM(wp_owner, e)) { return false; }
 +      if(!IS_PLAYER(e)) { return false; }
 +
 +      return true;
 +}
 +
  void ctf_CheckStalemate(void)
  {
        // declarations
 -      float stale_red_flags = 0, stale_blue_flags = 0;
 +      int stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0;
        entity tmp_entity;
  
        entity ctf_staleflaglist = world; // reset the list, we need to build the list each time this function runs
        {
                if(autocvar_g_ctf_stalemate)
                if(tmp_entity.ctf_status != FLAG_BASE)
 -              if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time)
 +              if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time || !tmp_entity.team) // instant stalemate in oneflag
                {
                        tmp_entity.ctf_staleflagnext = ctf_staleflaglist; // link flag into staleflaglist
                        ctf_staleflaglist = tmp_entity;
                        {
                                case NUM_TEAM_1: ++stale_red_flags; break;
                                case NUM_TEAM_2: ++stale_blue_flags; break;
 +                              case NUM_TEAM_3: ++stale_yellow_flags; break;
 +                              case NUM_TEAM_4: ++stale_pink_flags; break;
 +                              default: ++stale_neutral_flags; break;
                        }
                }
        }
  
 -      if(stale_red_flags && stale_blue_flags)
 +      if(ctf_oneflag)
 +              stale_flags = (stale_neutral_flags >= 1);
 +      else
 +              stale_flags = (stale_red_flags >= 1) + (stale_blue_flags >= 1) + (stale_yellow_flags >= 1) + (stale_pink_flags >= 1);
 +
 +      if(ctf_oneflag && stale_flags == 1)
 +              ctf_stalemate = true;
 +      else if(stale_flags >= 2)
                ctf_stalemate = true;
 -      else if((!stale_red_flags && !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 2)
 +      else if(stale_flags == 0 && autocvar_g_ctf_stalemate_endcondition == 2)
                { ctf_stalemate = false; wpforenemy_announced = false; }
 -      else if((!stale_red_flags || !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 1)
 +      else if(stale_flags < 2 && autocvar_g_ctf_stalemate_endcondition == 1)
                { ctf_stalemate = false; wpforenemy_announced = false; }
  
        // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary
                for(tmp_entity = ctf_staleflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_staleflagnext)
                {
                        if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier))
 -                              WaypointSprite_Spawn("enemyflagcarrier", 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, tmp_entity.team, tmp_entity.owner, wps_enemyflagcarrier, true, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team));
 +                      {
 +                              WaypointSprite_Spawn(((ctf_oneflag) ? "flagcarrier" : "enemyflagcarrier"), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, 0, tmp_entity.owner, wps_enemyflagcarrier, true, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team));
 +                              tmp_entity.owner.wps_enemyflagcarrier.customizeentityforclient = ctf_Stalemate_Customize;
 +                      }
                }
  
                if (!wpforenemy_announced)
@@@ -724,15 -659,9 +736,15 @@@ void ctf_FlagDamage(entity inflictor, e
  {
        if(ITEM_DAMAGE_NEEDKILL(deathtype))
        {
 -              // automatically kill the flag and return it
 -              self.health = 0;
 -              ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
 +              if(autocvar_g_ctf_flag_return_damage_delay)
 +              {
 +                      self.ctf_flagdamaged = true;
 +              }
 +              else
 +              {
 +                      self.health = 0;
 +                      ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
 +              }
                return;
        }
        if(autocvar_g_ctf_flag_return_damage)
@@@ -815,13 -744,7 +827,13 @@@ void ctf_FlagThink(
                                        return;
                                }
                        }
 -                      if(autocvar_g_ctf_flag_return_time)
 +                      if(self.ctf_flagdamaged)
 +                      {
 +                              self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE);
 +                              ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
 +                              return;
 +                      }
 +                      else if(autocvar_g_ctf_flag_return_time)
                        {
                                self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE);
                                ctf_CheckFlagReturn(self, RETURN_TIMEOUT);
                                        wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check)
                                }
                        }
 +                      if(CTF_SAMETEAM(self, self.owner) && self.team)
 +                      {
 +                              if(autocvar_g_ctf_flag_return) // drop the flag if reverse status has changed
 +                                      ctf_Handle_Throw(self.owner, world, DROP_THROW);
 +                              else if(vlen(self.owner.origin - self.ctf_spawnorigin) <= autocvar_g_ctf_flag_return_carried_radius)
 +                                      ctf_Handle_Return(self, self.owner);
 +                      }
                        return;
                }
  
@@@ -898,22 -814,17 +910,22 @@@ void ctf_FlagTouch(
        if(gameover) { return; }
        if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; }
  
 -      entity toucher = other;
 -      float is_not_monster = (!(toucher.flags & FL_MONSTER));
 +      entity toucher = other, tmp_entity;
 +      bool is_not_monster = (!(toucher.flags & FL_MONSTER)), num_perteam = 0;
  
        // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces
        if(ITEM_TOUCH_NEEDKILL())
        {
 -              self.health = 0;
 -              ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
 -              return;
 +              if(!autocvar_g_ctf_flag_return_damage_delay)
 +              {
 +                      self.health = 0;
 +                      ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
 +              }
 +              if(!self.ctf_flagdamaged) { return; }
        }
  
 +      FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; }
 +
        // special touch behaviors
        if(toucher.frozen) { return; }
        else if(toucher.vehicle_flags & VHF_ISVEHICLE)
        {
                case FLAG_BASE:
                {
 -                      if(SAME_TEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster)
 +                      if(ctf_oneflag)
 +                      {
 +                              if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster)
 +                                      ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base
 +                              else if(!self.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
 +                                      ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the neutral flag
 +                      }
 +                      else if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster)
                                ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base
 -                      else if(DIFF_TEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
 +                      else if(CTF_DIFFTEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
                                ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag
                        break;
                }
  
                case FLAG_DROPPED:
                {
 -                      if(SAME_TEAM(toucher, self))
 +                      if(CTF_SAMETEAM(toucher, self) && (autocvar_g_ctf_flag_return || num_perteam <= 1) && self.team) // automatically return if there's only 1 player on the team
                                ctf_Handle_Return(self, toucher); // toucher just returned his own flag
                        else if(is_not_monster && (!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
                                ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag
@@@ -999,7 -903,9 +1011,7 @@@ void ctf_RespawnFlag(entity flag
        // reset the player (if there is one)
        if((flag.owner) && (flag.owner.flagcarried == flag))
        {
 -              if(flag.owner.wps_enemyflagcarrier)
 -                      WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier);
 -
 +              WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier);
                WaypointSprite_Kill(flag.wps_flagcarrier);
  
                flag.owner.flagcarried = world;
        if((flag.owner) && (flag.owner.vehicle))
                flag.scale = FLAG_SCALE;
  
 -      if((flag.ctf_status == FLAG_DROPPED) && (flag.wps_flagdropped))
 +      if(flag.ctf_status == FLAG_DROPPED)
                { WaypointSprite_Kill(flag.wps_flagdropped); }
  
        // reset the flag
        flag.ctf_dropper = world;
        flag.ctf_pickuptime = 0;
        flag.ctf_droptime = 0;
 +      flag.ctf_flagdamaged = 0;
  
        ctf_CheckStalemate();
  }
@@@ -1056,34 -961,17 +1068,34 @@@ void ctf_DelayedFlagSetup(void) // call
        self.bot_basewaypoint = self.nearestwaypoint;
  
        // waypointsprites
 -      WaypointSprite_SpawnFixed(((self.team == NUM_TEAM_1) ? "redbase" : "bluebase"), self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, false));
 -      WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, false));
 +      string basename = "base";
 +
 +      switch(self.team)
 +      {
 +              case NUM_TEAM_1: basename = "redbase"; break;
 +              case NUM_TEAM_2: basename = "bluebase"; break;
 +              case NUM_TEAM_3: basename = "yellowbase"; break;
 +              case NUM_TEAM_4: basename = "pinkbase"; break;
 +              default: basename = "neutralbase"; break;
 +      }
 +
 +      WaypointSprite_SpawnFixed(basename, self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, ((self.team) ? Team_ColorRGB(self.team) : '1 1 1'));
 +      WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, ((self.team) ? colormapPaletteColor(self.team - 1, false) : '1 1 1'));
  
        // captureshield setup
        ctf_CaptureShield_Spawn(self);
  }
  
 -void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
 +void set_flag_string(entity flag, .string field, string value, string teamname)
 +{
 +      if(flag.field == "")
 +              flag.field = strzone(sprintf(value,teamname));
 +}
 +
 +void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
  {
        // declarations
 -      teamnumber = fabs(teamnumber - bound(0, autocvar_g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1.
 +      string teamname = Static_Team_ColorName_Lower(teamnumber);
        self = flag; // for later usage with droptofloor()
  
        // main setup
  
        setattachment(flag, world, "");
  
 -      flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag"); // Primarily only used for debugging or when showing nearby item name
 -      flag.team = ((teamnumber) ? NUM_TEAM_1 : NUM_TEAM_2); // NUM_TEAM_1: color 4 team (red) - NUM_TEAM_2: color 13 team (blue)
 -      flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
 +      flag.netname = strzone(sprintf("%s%s^7 flag", Team_ColorCode(teamnumber), Team_ColorName_Upper(teamnumber)));
 +      flag.team = teamnumber;
        flag.classname = "item_flag_team";
        flag.target = "###item###"; // wut?
        flag.flags = FL_ITEM | FL_NOTARGET;
        flag.event_damage = ctf_FlagDamage;
        flag.pushable = true;
        flag.teleportable = TELEPORT_NORMAL;
 +      flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
        flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable;
        flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable;
        flag.velocity = '0 0 0';
        flag.ctf_status = FLAG_BASE;
  
        // appearence
 -      if(flag.model == "")       { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); }
 -      if(!flag.scale)            { flag.scale = FLAG_SCALE; }
 -      if(!flag.skin)             { flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin); }
 -      if(flag.toucheffect == "") { flag.toucheffect = ((teamnumber) ? "redflag_touch" : "blueflag_touch"); }
 -      if(flag.passeffect == "")  { flag.passeffect = ((teamnumber) ? "red_pass" : "blue_pass"); }
 -      if(flag.capeffect == "")   { flag.capeffect = ((teamnumber) ? "red_cap" : "blue_cap"); }
 -
 -      // sound
 -      if(flag.snd_flag_taken == "")    { flag.snd_flag_taken  = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); }
 -      if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); }
 -      if(flag.snd_flag_capture == "")  { flag.snd_flag_capture = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag
 -      if(flag.snd_flag_respawn == "")  { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
 -      if(flag.snd_flag_dropped == "")  { flag.snd_flag_dropped = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); }
 -      if(flag.snd_flag_touch == "")    { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound
 -      if(flag.snd_flag_pass == "")     { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here
 +      if(!flag.scale)                         { flag.scale = FLAG_SCALE; }
 +      if(flag.skin == 0)                      { flag.skin = cvar(sprintf("g_ctf_flag_%s_skin", teamname)); }
 +      if(flag.model == "")            { flag.model = cvar_string(sprintf("g_ctf_flag_%s_model", teamname)); }
 +      set_flag_string(flag, toucheffect,      "%sflag_touch", teamname);
 +      set_flag_string(flag, passeffect,       "%sflag_pass",  teamname);
 +      set_flag_string(flag, capeffect,        "%sflag_cap",   teamname);
 +
 +      // sounds
 +      set_flag_string(flag, snd_flag_taken,           "ctf/%s_taken.wav",     teamname);
 +      set_flag_string(flag, snd_flag_returned,        "ctf/%s_returned.wav",  teamname);
 +      set_flag_string(flag, snd_flag_capture,         "ctf/%s_capture.wav",   teamname);
 +      set_flag_string(flag, snd_flag_dropped,         "ctf/%s_dropped.wav",   teamname);
 +      if(flag.snd_flag_respawn == "")         { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
 +      if(flag.snd_flag_touch == "")           { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound
 +      if(flag.snd_flag_pass == "")            { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here
  
        // precache
        precache_sound(flag.snd_flag_taken);
  
        if(autocvar_g_ctf_flag_glowtrails)
        {
 -              flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
 +              switch(teamnumber)
 +              {
 +                      case NUM_TEAM_1: flag.glow_color = 251; break;
 +                      case NUM_TEAM_2: flag.glow_color = 210; break;
 +                      case NUM_TEAM_3: flag.glow_color = 110; break;
 +                      case NUM_TEAM_4: flag.glow_color = 145; break;
 +                      default:                 flag.glow_color = 254; break;
 +              }
                flag.glow_size = 25;
                flag.glow_trail = 1;
        }
  
        flag.effects |= EF_LOWPRECISION;
        if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; }
 -      if(autocvar_g_ctf_dynamiclights)   { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); }
 -
 +      if(autocvar_g_ctf_dynamiclights)
 +      {
 +              switch(teamnumber)
 +              {
 +                      case NUM_TEAM_1: flag.effects |= EF_RED; break;
 +                      case NUM_TEAM_2: flag.effects |= EF_BLUE; break;
 +                      case NUM_TEAM_3: flag.effects |= EF_DIMLIGHT; break;
 +                      case NUM_TEAM_4: flag.effects |= EF_RED; break;
 +                      default:                 flag.effects |= EF_DIMLIGHT; break;
 +              }
 +      }
 +      
        // flag placement
        if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location
        {
@@@ -1230,7 -1101,7 +1242,7 @@@ entity havocbot_ctf_find_flag(entity bo
        f = ctf_worldflaglist;
        while (f)
        {
 -              if (bot.team == f.team)
 +              if (CTF_SAMETEAM(bot, f))
                        return f;
                f = f.ctf_worldflagnext;
        }
@@@ -1243,37 -1114,24 +1255,37 @@@ entity havocbot_ctf_find_enemy_flag(ent
        f = ctf_worldflaglist;
        while (f)
        {
 -              if (bot.team != f.team)
 +              if(ctf_oneflag)
 +              {
 +                      if(CTF_DIFFTEAM(bot, f))
 +                      {
 +                              if(f.team)
 +                              {
 +                                      if(bot.flagcarried)
 +                                              return f;
 +                              }
 +                              else if(!bot.flagcarried)
 +                                      return f;
 +                      }
 +              }
 +              else if (CTF_DIFFTEAM(bot, f))
                        return f;
                f = f.ctf_worldflagnext;
        }
        return world;
  }
  
 -float havocbot_ctf_teamcount(entity bot, vector org, float tc_radius)
 +int havocbot_ctf_teamcount(entity bot, vector org, float tc_radius)
  {
        if (!teamplay)
                return 0;
  
 -      float c = 0;
 +      int c = 0;
        entity head;
  
        FOR_EACH_PLAYER(head)
        {
 -              if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
 +              if(DIFF_TEAM(head, bot) || head.deadflag != DEAD_NO || head == bot)
                        continue;
  
                if(vlen(head.origin - org) < tc_radius)
@@@ -1289,7 -1147,7 +1301,7 @@@ void havocbot_goalrating_ctf_ourflag(fl
        head = ctf_worldflaglist;
        while (head)
        {
 -              if (self.team == head.team)
 +              if (CTF_SAMETEAM(self, head))
                        break;
                head = head.ctf_worldflagnext;
        }
@@@ -1303,7 -1161,7 +1315,7 @@@ void havocbot_goalrating_ctf_ourbase(fl
        head = ctf_worldflaglist;
        while (head)
        {
 -              if (self.team == head.team)
 +              if (CTF_SAMETEAM(self, head))
                        break;
                head = head.ctf_worldflagnext;
        }
@@@ -1319,20 -1177,7 +1331,20 @@@ void havocbot_goalrating_ctf_enemyflag(
        head = ctf_worldflaglist;
        while (head)
        {
 -              if (self.team != head.team)
 +              if(ctf_oneflag)
 +              {
 +                      if(CTF_DIFFTEAM(self, head))
 +                      {
 +                              if(head.team)
 +                              {
 +                                      if(self.flagcarried)
 +                                              break;
 +                              }
 +                              else if(!self.flagcarried)
 +                                      break;
 +                      }
 +              }
 +              else if(CTF_DIFFTEAM(self, head))
                        break;
                head = head.ctf_worldflagnext;
        }
@@@ -1834,7 -1679,7 +1846,7 @@@ void havocbot_role_ctf_defense(
        }
  }
  
 -void havocbot_role_ctf_setrole(entity bot, float role)
 +void havocbot_role_ctf_setrole(entity bot, int role)
  {
        dprint(strcat(bot.netname," switched to "));
        switch(role)
  MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
  {
        entity flag;
 +      int t = 0, t2 = 0, t3 = 0;
  
        // initially clear items so they can be set as necessary later.
 -      self.items &= ~(IT_RED_FLAG_CARRYING | IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST
 -              | IT_BLUE_FLAG_CARRYING | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED);
 +      self.ctf_flagstatus &= ~(CTF_RED_FLAG_CARRYING          | CTF_RED_FLAG_TAKEN            | CTF_RED_FLAG_LOST 
 +                                                 | CTF_BLUE_FLAG_CARRYING             | CTF_BLUE_FLAG_TAKEN           | CTF_BLUE_FLAG_LOST
 +                                                 | CTF_YELLOW_FLAG_CARRYING   | CTF_YELLOW_FLAG_TAKEN         | CTF_YELLOW_FLAG_LOST
 +                                                 | CTF_PINK_FLAG_CARRYING     | CTF_PINK_FLAG_TAKEN           | CTF_PINK_FLAG_LOST
 +                                                 | CTF_NEUTRAL_FLAG_CARRYING  | CTF_NEUTRAL_FLAG_TAKEN        | CTF_NEUTRAL_FLAG_LOST
 +                                                 | CTF_FLAG_NEUTRAL | CTF_SHIELDED);
  
        // scan through all the flags and notify the client about them
        for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
        {
 +              if(flag.team == NUM_TEAM_1) { t = CTF_RED_FLAG_CARRYING;                t2 = CTF_RED_FLAG_TAKEN;                t3 = CTF_RED_FLAG_LOST; }
 +              if(flag.team == NUM_TEAM_2) { t = CTF_BLUE_FLAG_CARRYING;               t2 = CTF_BLUE_FLAG_TAKEN;               t3 = CTF_BLUE_FLAG_LOST; }
 +              if(flag.team == NUM_TEAM_3) { t = CTF_YELLOW_FLAG_CARRYING;     t2 = CTF_YELLOW_FLAG_TAKEN;             t3 = CTF_YELLOW_FLAG_LOST; }
 +              if(flag.team == NUM_TEAM_4) { t = CTF_PINK_FLAG_CARRYING;               t2 = CTF_PINK_FLAG_TAKEN;               t3 = CTF_PINK_FLAG_LOST; }
 +              if(flag.team == 0)                      { t = CTF_NEUTRAL_FLAG_CARRYING;        t2 = CTF_NEUTRAL_FLAG_TAKEN;    t3 = CTF_NEUTRAL_FLAG_LOST; self.ctf_flagstatus |= CTF_FLAG_NEUTRAL; }
 +
                switch(flag.ctf_status)
                {
                        case FLAG_PASSING:
                        case FLAG_CARRY:
                        {
                                if((flag.owner == self) || (flag.pass_sender == self))
 -                                      self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_CARRYING : IT_BLUE_FLAG_CARRYING); // carrying: self is currently carrying the flag
 -                              else
 -                                      self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_TAKEN : IT_BLUE_FLAG_TAKEN); // taken: someone on self's team is carrying the flag
 +                                      self.ctf_flagstatus |= t; // carrying: self is currently carrying the flag
 +                              else 
 +                                      self.ctf_flagstatus |= t2; // taken: someone else is carrying the flag
                                break;
                        }
                        case FLAG_DROPPED:
                        {
 -                              self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_LOST : IT_BLUE_FLAG_LOST); // lost: the flag is dropped somewhere on the map
 +                              self.ctf_flagstatus |= t3; // lost: the flag is dropped somewhere on the map
                                break;
                        }
                }
  
        // item for stopping players from capturing the flag too often
        if(self.ctf_captureshielded)
 -              self.items |= IT_CTF_SHIELDED;
 +              self.ctf_flagstatus |= CTF_SHIELDED;
  
        // update the health of the flag carrier waypointsprite
        if(self.wps_flagcarrier)
@@@ -1951,7 -1785,7 +1963,7 @@@ MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) 
                        frag_force *= autocvar_g_ctf_flagcarrier_forcefactor;
                }
        }
 -      else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && DIFF_TEAM(frag_target, frag_attacker)) // if the target is a flagcarrier
 +      else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && CTF_DIFFTEAM(frag_target, frag_attacker)) // if the target is a flagcarrier
        {
                if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON)))
                if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time)
@@@ -1973,11 -1807,7 +1985,11 @@@ MUTATOR_HOOKFUNCTION(ctf_PlayerDies
        }
  
        if(frag_target.flagcarried)
 -              { ctf_Handle_Throw(frag_target, world, DROP_NORMAL); }
 +      {
 +              entity tmp_entity = frag_target.flagcarried;
 +              ctf_Handle_Throw(frag_target, world, DROP_NORMAL);
 +              tmp_entity.ctf_dropper = world;
 +      }
  
        return false;
  }
@@@ -2127,8 -1957,6 +2139,8 @@@ MUTATOR_HOOKFUNCTION(ctf_VehicleEnter
  {
        if(vh_player.flagcarried)
        {
 +              vh_player.flagcarried.nodrawtoclient = vh_player; // hide the flag from the driver
 +
                if(!autocvar_g_ctf_allow_vehicle_carry && !autocvar_g_ctf_allow_vehicle_touch)
                {
                        ctf_Handle_Throw(vh_player, world, DROP_NORMAL);
@@@ -2154,7 -1982,6 +2166,7 @@@ MUTATOR_HOOKFUNCTION(ctf_VehicleExit
                setorigin(vh_player.flagcarried, FLAG_CARRY_OFFSET);
                vh_player.flagcarried.scale = FLAG_SCALE;
                vh_player.flagcarried.angles = '0 0 0';
 +              vh_player.flagcarried.nodrawtoclient = world;
                return true;
        }
  
@@@ -2165,7 -1992,7 +2177,7 @@@ MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun
  {
        if(self.flagcarried)
        {
 -              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_));
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, ((self.flagcarried.team) ? APP_TEAM_ENT_4(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL));
                ctf_RespawnFlag(self.flagcarried);
                return true;
        }
@@@ -2213,19 -2040,6 +2225,19 @@@ MUTATOR_HOOKFUNCTION(ctf_BotRoles
        return true;
  }
  
 +MUTATOR_HOOKFUNCTION(ctf_GetTeamCount)
 +{
 +      //ret_float = ctf_teams;
 +      ret_string = "ctf_team";
 +      return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(ctf_SpectateCopy)
 +{
 +      self.ctf_flagstatus = other.ctf_flagstatus;
 +      return false;
 +}
 +
  
  // ==========
  // Spawnfuncs
@@@ -2292,7 -2106,7 +2304,7 @@@ void spawnfunc_item_flag_team1(
  {
        if(!g_ctf) { remove(self); return; }
  
 -      ctf_FlagSetup(1, self); // 1 = red
 +      ctf_FlagSetup(NUM_TEAM_1, self);
  }
  
  /*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
@@@ -2310,62 -2124,7 +2322,62 @@@ void spawnfunc_item_flag_team2(
  {
        if(!g_ctf) { remove(self); return; }
  
 -      ctf_FlagSetup(0, self); // the 0 is misleading, but -- 0 = blue.
 +      ctf_FlagSetup(NUM_TEAM_2, self);
 +}
 +
 +/*QUAKED spawnfunc_item_flag_team3 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
 +CTF flag for team three (Yellow).
 +Keys: 
 +"angle" Angle the flag will point (minus 90 degrees)... 
 +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3...
 +"noise" sound played when flag is picked up...
 +"noise1" sound played when flag is returned by a teammate...
 +"noise2" sound played when flag is captured...
 +"noise3" sound played when flag is lost in the field and respawns itself... 
 +"noise4" sound played when flag is dropped by a player...
 +"noise5" sound played when flag touches the ground... */
 +void spawnfunc_item_flag_team3()
 +{
 +      if(!g_ctf) { remove(self); return; }
 +
 +      ctf_FlagSetup(NUM_TEAM_3, self);
 +}
 +
 +/*QUAKED spawnfunc_item_flag_team4 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
 +CTF flag for team four (Pink).
 +Keys: 
 +"angle" Angle the flag will point (minus 90 degrees)... 
 +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3...
 +"noise" sound played when flag is picked up...
 +"noise1" sound played when flag is returned by a teammate...
 +"noise2" sound played when flag is captured...
 +"noise3" sound played when flag is lost in the field and respawns itself... 
 +"noise4" sound played when flag is dropped by a player...
 +"noise5" sound played when flag touches the ground... */
 +void spawnfunc_item_flag_team4()
 +{
 +      if(!g_ctf) { remove(self); return; }
 +
 +      ctf_FlagSetup(NUM_TEAM_4, self);
 +}
 +
 +/*QUAKED spawnfunc_item_flag_neutral (0 0.5 0.8) (-48 -48 -37) (48 48 37)
 +CTF flag (Neutral).
 +Keys: 
 +"angle" Angle the flag will point (minus 90 degrees)... 
 +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3...
 +"noise" sound played when flag is picked up...
 +"noise1" sound played when flag is returned by a teammate...
 +"noise2" sound played when flag is captured...
 +"noise3" sound played when flag is lost in the field and respawns itself... 
 +"noise4" sound played when flag is dropped by a player...
 +"noise5" sound played when flag touches the ground... */
 +void spawnfunc_item_flag_neutral()
 +{
 +      if(!g_ctf) { remove(self); return; }
 +      if(!cvar("g_ctf_oneflag")) { remove(self); return; }
 +
 +      ctf_FlagSetup(0, self);
  }
  
  /*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
@@@ -2390,19 -2149,15 +2402,19 @@@ void spawnfunc_team_CTF_blueplayer() { 
  void spawnfunc_team_CTF_redspawn()   { spawnfunc_info_player_team1();  }
  void spawnfunc_team_CTF_bluespawn()  { spawnfunc_info_player_team2();  }
  
 +void team_CTF_neutralflag()                    { spawnfunc_item_flag_neutral();  }
 +void team_neutralobelisk()                     { spawnfunc_item_flag_neutral();  }
 +
  
  // ==============
  // Initialization
  // ==============
  
  // scoreboard setup
 -void ctf_ScoreRules()
 +void ctf_ScoreRules(int teams)
  {
 -      ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, true);
 +      CheckAllowedTeams(world);
 +      ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
        ScoreInfo_SetLabel_TeamScore  (ST_CTF_CAPS,     "caps",      SFL_SORT_PRIO_PRIMARY);
        ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
        ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME,  "captime",   SFL_LOWER_IS_BETTER | SFL_TIME);
  }
  
  // code from here on is just to support maps that don't have flag and team entities
 -void ctf_SpawnTeam (string teamname, float teamcolor)
 +void ctf_SpawnTeam (string teamname, int teamcolor)
  {
        entity oldself;
        oldself = self;
  
  void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
  {
 +      ctf_teams = 2;
 +
 +      entity tmp_entity;
 +      for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
 +      {
 +              if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); }
 +              if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); }
 +              if(tmp_entity.team == 0) { ctf_oneflag = true; }
 +      }
 +
 +      ctf_teams = bound(2, ctf_teams, 4);
 +
        // if no teams are found, spawn defaults
        if(find(world, classname, "ctf_team") == world)
        {
                print("No ""ctf_team"" entities found on this map, creating them anyway.\n");
                ctf_SpawnTeam("Red", NUM_TEAM_1 - 1);
                ctf_SpawnTeam("Blue", NUM_TEAM_2 - 1);
 +              if(ctf_teams >= 3)
 +                      ctf_SpawnTeam("Yellow", NUM_TEAM_3 - 1);
 +              if(ctf_teams >= 4)
 +                      ctf_SpawnTeam("Pink", NUM_TEAM_4 - 1);
        }
  
 -      ctf_ScoreRules();
 +      ctf_ScoreRules(ctf_teams);
  }
  
  void ctf_Initialize()
        ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
        ctf_captureshield_force = autocvar_g_ctf_shield_force;
  
 +      addstat(STAT_CTF_FLAGSTATUS, AS_INT, ctf_flagstatus);
 +
        InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
  }
  
@@@ -2487,8 -2224,6 +2499,8 @@@ MUTATOR_DEFINITION(gamemode_ctf
        MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
        MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRole, ctf_BotRoles, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(GetTeamCount, ctf_GetTeamCount, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SpectateCopy, ctf_SpectateCopy, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
        {
index d2572bc458896ef24159bc9ada697496ed6424aa,302f6ce104b305054a8f3dd36830a5a3b8200025..3cac4bd41fa9e5daef74663370fe55cc33138951
@@@ -7,13 -7,13 +7,13 @@@
  void ctf_RespawnFlag(entity flag);
  
  // score rule declarations
 -const float ST_CTF_CAPS = 1;
 -const float SP_CTF_CAPS = 4;
 -const float SP_CTF_CAPTIME = 5;
 -const float SP_CTF_PICKUPS = 6;
 -const float SP_CTF_DROPS = 7;
 -const float SP_CTF_FCKILLS = 8;
 -const float SP_CTF_RETURNS = 9;
 +const int ST_CTF_CAPS = 1;
 +const int SP_CTF_CAPS = 4;
 +const int SP_CTF_CAPTIME = 5;
 +const int SP_CTF_PICKUPS = 6;
 +const int SP_CTF_DROPS = 7;
 +const int SP_CTF_FCKILLS = 8;
 +const int SP_CTF_RETURNS = 9;
  
  // flag constants // for most of these, there is just one question to be asked: WHYYYYY?
  #define FLAG_MIN (PL_MIN + '0 0 -13')
@@@ -25,23 -25,20 +25,23 @@@ const float FLAG_THINKRATE = 0.2
  const float FLAG_TOUCHRATE = 0.5;
  const float WPFE_THINKRATE = 0.5;
  
 -#define FLAG_DROP_OFFSET ('0 0 32')
 -#define FLAG_CARRY_OFFSET ('-16 0 8')
 +const vector FLAG_DROP_OFFSET = ('0 0 32');
 +const vector FLAG_CARRY_OFFSET = ('-16 0 8');
  #define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13))
 -#define FLAG_WAYPOINT_OFFSET ('0 0 64')
 -#define FLAG_FLOAT_OFFSET ('0 0 32')
 -#define FLAG_PASS_ARC_OFFSET ('0 0 -10')
 +const vector FLAG_WAYPOINT_OFFSET = ('0 0 64');
 +const vector FLAG_FLOAT_OFFSET = ('0 0 32');
 +const vector FLAG_PASS_ARC_OFFSET = ('0 0 -10');
 +
 +const vector VEHICLE_FLAG_OFFSET = ('0 0 90');
 +
 +
  
 -#define VEHICLE_FLAG_OFFSET ('0 0 96')
  const float VEHICLE_FLAG_SCALE = 1.0;
  
  // waypoint colors
 -#define WPCOLOR_ENEMYFC(t) (colormapPaletteColor(t - 1, false) * 0.75)
 +#define WPCOLOR_ENEMYFC(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1')
  #define WPCOLOR_FLAGCARRIER(t) ('0.8 0.8 0')
 -#define WPCOLOR_DROPPEDFLAG(t) (('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5)
 +#define WPCOLOR_DROPPEDFLAG(t) ((t) ? ('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5 : '1 1 1')
  
  // sounds
  #define snd_flag_taken noise
@@@ -70,44 -67,44 +70,46 @@@ entity ctf_worldflaglist
  .entity wps_flagdropped;
  .entity wps_enemyflagcarrier;
  .float wps_helpme_time;
 -float wpforenemy_announced;
 +bool wpforenemy_announced;
  float wpforenemy_nextthink;
  
  // statuses
 -const float FLAG_BASE = 1;
 -const float FLAG_DROPPED = 2;
 -const float FLAG_CARRY = 3;
 -const float FLAG_PASSING = 4;
 +const int FLAG_BASE = 1;
 +const int FLAG_DROPPED = 2;
 +const int FLAG_CARRY = 3;
 +const int FLAG_PASSING = 4;
  
 -const float DROP_NORMAL = 1;
 -const float DROP_THROW = 2;
 -const float DROP_PASS = 3;
 -const float DROP_RESET = 4;
 +const int DROP_NORMAL = 1;
 +const int DROP_THROW = 2;
 +const int DROP_PASS = 3;
 +const int DROP_RESET = 4;
  
 -const float PICKUP_BASE = 1;
 -const float PICKUP_DROPPED = 2;
 +const int PICKUP_BASE = 1;
 +const int PICKUP_DROPPED = 2;
  
 -const float CAPTURE_NORMAL = 1;
 -const float CAPTURE_DROPPED = 2;
 +const int CAPTURE_NORMAL = 1;
 +const int CAPTURE_DROPPED = 2;
  
 -const float RETURN_TIMEOUT = 1;
 -const float RETURN_DROPPED = 2;
 -const float RETURN_DAMAGE = 3;
 -const float RETURN_SPEEDRUN = 4;
 -const float RETURN_NEEDKILL = 5;
 +const int RETURN_TIMEOUT = 1;
 +const int RETURN_DROPPED = 2;
 +const int RETURN_DAMAGE = 3;
 +const int RETURN_SPEEDRUN = 4;
 +const int RETURN_NEEDKILL = 5;
  
+ void ctf_Handle_Throw(entity player, entity receiver, float droptype);
  // flag properties
  #define ctf_spawnorigin dropped_origin
 -float ctf_stalemate; // indicates that a stalemate is active
 +bool ctf_stalemate; // indicates that a stalemate is active
  float ctf_captimerecord; // record time for capturing the flag
  .float ctf_pickuptime;
  .float ctf_droptime;
 -.float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
 +.int ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
  .entity ctf_dropper; // don't allow spam of dropping the flag
 -.float max_flag_health;
 +.int max_flag_health;
  .float next_take_time;
 +.bool ctf_flagdamaged;
 +int ctf_teams;
  
  // passing/throwing properties
  .float pass_distance;
  .entity pass_target;
  .float throw_antispam;
  .float throw_prevtime;
 -.float throw_count;
 +.int throw_count;
  
  // CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag.
 -.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
 +.bool ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
  float ctf_captureshield_min_negscore; // punish at -20 points
  float ctf_captureshield_max_ratio; // punish at most 30% of each team
  float ctf_captureshield_force; // push force of the shield
  
 +// 1 flag ctf
 +bool ctf_oneflag; // indicates whether or not a neutral flag has been found
 +
  // bot player logic
 -const float HAVOCBOT_CTF_ROLE_NONE = 0;
 -const float HAVOCBOT_CTF_ROLE_DEFENSE = 2;
 -const float HAVOCBOT_CTF_ROLE_MIDDLE = 4;
 -const float HAVOCBOT_CTF_ROLE_OFFENSE = 8;
 -const float HAVOCBOT_CTF_ROLE_CARRIER = 16;
 -const float HAVOCBOT_CTF_ROLE_RETRIEVER = 32;
 -const float HAVOCBOT_CTF_ROLE_ESCORT = 64;
 +const int HAVOCBOT_CTF_ROLE_NONE = 0;
 +const int HAVOCBOT_CTF_ROLE_DEFENSE = 2;
 +const int HAVOCBOT_CTF_ROLE_MIDDLE = 4;
 +const int HAVOCBOT_CTF_ROLE_OFFENSE = 8;
 +const int HAVOCBOT_CTF_ROLE_CARRIER = 16;
 +const int HAVOCBOT_CTF_ROLE_RETRIEVER = 32;
 +const int HAVOCBOT_CTF_ROLE_ESCORT = 64;
  
 -.float havocbot_cantfindflag;
 +.bool havocbot_cantfindflag;
  
  vector havocbot_ctf_middlepoint;
  float havocbot_ctf_middlepoint_radius;
  
 -void havocbot_role_ctf_setrole(entity bot, float role);
 +void havocbot_role_ctf_setrole(entity bot, int role);
 +
 +// team checking
 +#define CTF_SAMETEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? DIFF_TEAM(a,b) : SAME_TEAM(a,b))
 +#define CTF_DIFFTEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? SAME_TEAM(a,b) : DIFF_TEAM(a,b))
 +
 +// networked flag statuses
 +.int ctf_flagstatus;
  #endif
 +
 +const int CTF_RED_FLAG_TAKEN                  = 1;
 +const int CTF_RED_FLAG_LOST                           = 2;
 +const int CTF_RED_FLAG_CARRYING                       = 3;
 +const int CTF_BLUE_FLAG_TAKEN                 = 4;
 +const int CTF_BLUE_FLAG_LOST                  = 8;
 +const int CTF_BLUE_FLAG_CARRYING              = 12;
 +const int CTF_YELLOW_FLAG_TAKEN                       = 16;
 +const int CTF_YELLOW_FLAG_LOST                        = 32;
 +const int CTF_YELLOW_FLAG_CARRYING            = 48;
 +const int CTF_PINK_FLAG_TAKEN                 = 64;
 +const int CTF_PINK_FLAG_LOST                  = 128;
 +const int CTF_PINK_FLAG_CARRYING              = 192;
 +const int CTF_NEUTRAL_FLAG_TAKEN              = 256;
 +const int CTF_NEUTRAL_FLAG_LOST                       = 512;
 +const int CTF_NEUTRAL_FLAG_CARRYING           = 768;
 +const int CTF_FLAG_NEUTRAL                            = 2048;
 +const int CTF_SHIELDED                                        = 4096;
 +
  #endif