]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into divVerent/4team_ctf
authorMario <mario.mario@y7mail.com>
Tue, 28 Jan 2014 05:43:05 +0000 (16:43 +1100)
committerMario <mario.mario@y7mail.com>
Tue, 28 Jan 2014 05:43:05 +0000 (16:43 +1100)
1  2 
effectinfo.txt
gamemodes.cfg
qcsrc/client/hud.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/notifications.qh
qcsrc/server/autocvars.qh
qcsrc/server/mutators/gamemode_ctf.qc

diff --combined effectinfo.txt
index af1e03ddcc84162ee69c49f81d6cb3a976d057e8,782521447a3c96ab998a73b28028c6c5f6f95d94..88c4f972aa125b3eee1b156596bcde9793d651f1
@@@ -7670,38 -7670,6 +7670,38 @@@ velocityjitter 300 300 30
  velocitymultiplier 0.5
  airfriction 3
  
 +// yellowflag_touch -- effects for touching the yellow flag
 +// used nowhere in code
 +effect yellowflag_touch
 +count 35
 +type spark
 +tex 40 40
 +color 0xFFFF0F 0xFFFF0F
 +size 1 3
 +alpha 0 256 556
 +gravity 1
 +bounce 1.5
 +originjitter 1 1 1
 +velocityjitter 300 300 300
 +velocitymultiplier 0.5
 +airfriction 3
 +
 +// pinkflag_touch -- effects for touching the pink flag
 +// used nowhere in code
 +effect pinkflag_touch
 +count 35
 +type spark
 +tex 40 40
 +color 0xFF0FFF 0xFF0FFF
 +size 1 3
 +alpha 0 256 556
 +gravity 1
 +bounce 1.5
 +originjitter 1 1 1
 +velocityjitter 300 300 300
 +velocitymultiplier 0.5
 +airfriction 3
 +
  // red_pass
  // used nowhere in code
  effect red_pass
@@@ -7760,64 -7728,6 +7760,64 @@@ size 4 
  alpha 256 256 1280
  type static
  
 +// yellow_pass
 +// used nowhere in code
 +effect yellow_pass
 +trailspacing 64
 +color 0xFFFF0F 0xFFFF0F
 +size 2 2
 +tex 32 32
 +alpha 64 128 64
 +airfriction 5
 +sizeincrease 2
 +type static
 +effect yellow_pass
 +trailspacing 12
 +color 0xFFFF0F 0xFFFF0F
 +size 1 1
 +tex 0 8
 +alpha 32 64 32
 +airfriction 9
 +sizeincrease 8
 +velocityjitter 64 64 64
 +type static
 +effect yellow_pass
 +trailspacing 12
 +color 0xFFFF0F 0xFFFF0F
 +size 4 4
 +//tex 48 55
 +alpha 256 256 1280
 +type static
 +
 +// pink_pass
 +// used nowhere in code
 +effect pink_pass
 +trailspacing 64
 +color 0xFFFFFF 0xFFFFFF
 +size 2 2
 +tex 32 32
 +alpha 64 128 64
 +airfriction 5
 +sizeincrease 2
 +type static
 +effect pink_pass
 +trailspacing 12
 +color 0xFFFFFF 0xFFFFFF
 +size 1 1
 +tex 0 8
 +alpha 32 64 32
 +airfriction 9
 +sizeincrease 8
 +velocityjitter 64 64 64
 +type static
 +effect pink_pass
 +trailspacing 12
 +color 0xFFFFFF 0xFFFFFF
 +size 4 4
 +//tex 48 55
 +alpha 256 256 1280
 +type static
 +
  // red_cap -- red team capture effect
  effect red_cap
  count 500
@@@ -7868,56 -7778,6 +7868,56 @@@ alpha 190 190 18
  sizeincrease -80
  color 0x0000FF 0x000097
  
 +// yellow_cap -- yellow team capture effect
 +effect yellow_cap
 +count 500
 +type spark
 +tex 64 64
 +color 0xFFFF0F 0xFFFF0F
 +size 1 1
 +alpha 0 256 100
 +stretchfactor 2
 +//gravity 1
 +bounce 1.5
 +originjitter 1 1 1
 +velocityjitter 1000 1000 1500
 +velocitymultiplier 0.5
 +airfriction 2
 +stretchfactor 0.6
 +effect yellow_cap
 +countabsolute 1
 +type smoke
 +tex 65 65
 +size 150 150
 +alpha 190 190 180
 +sizeincrease -80
 +color 0xFFFF0F 0xFFFF0F
 +
 +// pink_cap -- pink team capture effect
 +effect pink_cap
 +count 500
 +type spark
 +tex 64 64
 +color 0xFF0FFF 0xFF0FFF
 +size 1 1
 +alpha 0 256 100
 +stretchfactor 2
 +//gravity 1
 +bounce 1.5
 +originjitter 1 1 1
 +velocityjitter 1000 1000 1500
 +velocitymultiplier 0.5
 +airfriction 2
 +stretchfactor 0.6
 +effect pink_cap
 +countabsolute 1
 +type smoke
 +tex 65 65
 +size 150 150
 +alpha 190 190 180
 +sizeincrease -80
 +color 0xFF0FFF 0xFF0FFF
 +
  // spawn_point_red -- red team idle spawn point effect
  effect spawn_point_red
  count 37.5
@@@ -8118,7 -7978,6 +8118,6 @@@ alpha 190 190 18
  sizeincrease -80
  color 0xFFFFFF 0xFFFFFF
  
  // nade effects
  effect nade_blue
  trailspacing 1
diff --combined gamemodes.cfg
index b401cc2ebd036269d13d96043d056f84d3f6a9c7,a44b83ad5e79d7b25fd846a147a5b40ecef6fe43..0dc3e719a5d5779fff39c74473e048beafdc5d7d
@@@ -36,6 -36,7 +36,7 @@@ alias cl_hook_gamestart_n
  alias cl_hook_gamestart_cts
  alias cl_hook_gamestart_ka
  alias cl_hook_gamestart_ft
+ alias cl_hook_gamestart_inv
  alias cl_hook_gameend
  alias cl_hook_activeweapon
  
@@@ -56,6 -57,7 +57,7 @@@ alias sv_hook_gamestart_n
  alias sv_hook_gamestart_cts
  alias sv_hook_gamestart_ka
  alias sv_hook_gamestart_ft
+ alias sv_hook_gamestart_inv
  alias sv_hook_gamerestart
  alias sv_hook_gameend
  
@@@ -78,6 -80,7 +80,7 @@@ seta g_keyhunt_point_leadlimit -1     "Keyh
  seta g_race_laps_limit -1     "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_invasion_round_limit -1 "Invasion round limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  
  
  // =================================
@@@ -127,6 -130,9 +130,9 @@@ set g_cts_weapon_stay 
  set g_ft_respawn_waves 0
  set g_ft_respawn_delay 0
  set g_ft_weapon_stay 0
+ set g_inv_respawn_waves 0
+ set g_inv_respawn_delay 0
+ set g_inv_weapon_stay 0
  
  
  // =========
@@@ -144,8 -150,9 +150,9 @@@ set g_ca_point_leadlimit 
  set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games."
  set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
  set g_ca_damage2score_multiplier 0.01
- set g_ca_round_timelimit 180
+ set g_ca_round_timelimit 180 "round time limit in seconds"
  seta g_ca_teams_override 0
+ set g_ca_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
  set g_ca_teams 0
  
  
  //  capture the flag
  // ==================
  set g_ctf 0 "Capture The Flag: take the enemy flag and bring it to yours at your base to score"
 +set g_ctf_flag_return 1 "auto return the flag to base when touched by a teammate"
 +set g_ctf_flag_return_carried_radius 100 "allow flags to be returned by carrier if base is within this radius"
  set g_ctf_flag_return_time 15
  set g_ctf_flag_return_dropped 100
  set g_ctf_flag_return_damage 0
@@@ -202,6 -207,7 +209,7 @@@ set g_ctf_pass_timelimit 2 "how long a 
  set g_ctf_pass_velocity 750 "how fast or far a player can pass the flag"
  set g_ctf_allow_vehicle_touch 0 "allow flags to be picked up/captured/returned without even leaving the vehicle"
  set g_ctf_allow_vehicle_carry 1 "allow players to hold flags inside a vehicle"
+ set g_ctf_allow_monster_touch 0 "allow flags to be returned by monsters"
  
  set g_ctf_shield_max_ratio 0  "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)"
  set g_ctf_shield_min_negscore 20      "shield the player from the flag if he's got this negative amount of points or less"
@@@ -211,10 -217,6 +219,10 @@@ set g_ctf_flag_red_model "models/ctf/fl
  set g_ctf_flag_red_skin 0
  set g_ctf_flag_blue_model "models/ctf/flags.md3"
  set g_ctf_flag_blue_skin 1
 +set g_ctf_flag_yellow_model "models/ctf/flags.md3"
 +set g_ctf_flag_yellow_skin 2
 +set g_ctf_flag_pink_model "models/ctf/flags.md3"
 +set g_ctf_flag_pink_skin 3
  set g_ctf_flag_glowtrails 1
  set g_ctf_fullbrightflags 0
  set g_ctf_dynamiclights 0
@@@ -237,7 -239,7 +245,7 @@@ set g_cts_finish_kill_delay 10 "preven
  // ==========================
  set g_dm 1 "Deathmatch: killing any other player is one frag, player with most frags wins"
  set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)"
- set g_tdm_team_spawns 0 "when 1, a map can define team spawnpoints for TDM"
+ set g_tdm_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
  seta g_tdm_teams_override 0   "how many teams are in team deathmatch"
  
  
@@@ -268,10 -270,11 +276,11 @@@ set g_freezetag_revive_clearspeed 1.6 "
  set g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
  set g_freezetag_revive_falldamage 0 "Enable reviving from this amount of fall damage"
  set g_freezetag_revive_falldamage_health 40 "Amount of health player has if they revived from falling"
- set g_freezetag_round_timelimit 180
+ set g_freezetag_round_timelimit 180 "round time limit in seconds"
  set g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
  set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
  seta g_freezetag_teams_override 0
+ set g_freezetag_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
  set g_freezetag_teams 0
  
  
@@@ -406,3 -409,13 +415,13 @@@ set g_race 0 "Race: be faster than you
  set g_race_qualifying_timelimit 0
  set g_race_qualifying_timelimit_override -1
  set g_race_teams 0    "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
+ // ==========
+ //  invasion
+ // ==========
+ set g_invasion 0 "Invasion: survive against waves of monsters"
+ set g_invasion_round_timelimit 120 "maximum time to kill all monsters"
+ set g_invasion_warmup 10 "time between waves to prepare for battle"
+ set g_invasion_monster_count 10 "number of monsters on first wave (increments)"
+ set g_invasion_zombies_only 0 "only spawn zombies"
+ set g_invasion_spawn_delay 2 "spawn more monsters after this delay"
diff --combined qcsrc/client/hud.qc
index 075d1a38f1e46f377c0dbf1d3cb33fa19c094e3d,252142506f88731b28e21e679c0bd5d84b7d52b2..f14ea11324eb358db50dfa5aa7e4bba06a4e099b
@@@ -2730,42 -2730,30 +2730,42 @@@ void HUD_Mod_CA(vector myPos, vector my
  }
  
  // CTF HUD modicon section
 -float redflag_prevframe, blueflag_prevframe; // status during previous frame
 -float redflag_prevstatus, blueflag_prevstatus; // last remembered status
 -float redflag_statuschange_time, blueflag_statuschange_time; // time when the status changed
 +float redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe; // status during previous frame
 +float redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus; // last remembered status
 +float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_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 = 0;
 +      redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = 0;
 +      redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_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;
        vector flag_size;
        float f; // every function should have that
  
 -      float redflag, blueflag; // current status
 -      float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime; // time since the status changed
 +      float redflag, blueflag, yellowflag, pinkflag; // current status
 +      float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime; // time since the status changed
        float stat_items;
 +      float teams_count = 0;
 +      float fs, fs2, fs3, size1, size2;
 +      vector e1, e2;
 +      entity tm;
 +
 +      for(tm = teams.sort_next; tm; tm = tm.sort_next)
 +      if(tm.team != NUM_SPECTATOR)
 +              ++teams_count;
  
        stat_items = getstati(STAT_ITEMS, 0, 24);
        redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3;
        blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3;
 +      yellowflag = (stat_items/IT_YELLOW_FLAG_TAKEN) & 3;
 +      pinkflag = (stat_items/IT_PINK_FLAG_TAKEN) & 3;
  
 -      if(redflag || blueflag)
 +      if(redflag || blueflag || yellowflag || pinkflag)
                mod_active = 1;
        else
                mod_active = 0;
        {
                redflag = 1;
                blueflag = 2;
 +              if(teams_count >= 3)
 +                      yellowflag = 2;
 +              if(teams_count >= 4)
 +                      pinkflag = 3;
        }
  
        // 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;
 +      }
 +
        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;
  
        float BLINK_FACTOR = 0.15;
        float BLINK_BASE = 0.85;
                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 & IT_CTF_SHIELDED) && (myteam != NUM_TEAM_1))
                                red_icon = "flag_red_shielded";
                        else
                                red_icon = string_null;
                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 & IT_CTF_SHIELDED) && (myteam != NUM_TEAM_1))
                                red_icon_prevstatus = "flag_red_shielded";
                        else
                                red_icon_prevstatus = string_null;
                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 & IT_CTF_SHIELDED) && (myteam != NUM_TEAM_2))
                                blue_icon = "flag_blue_shielded";
                        else
                                blue_icon = string_null;
                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 & IT_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;
 +      float 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 & IT_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 & IT_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;
 +      float 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 & IT_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 & IT_CTF_SHIELDED) && (myteam != NUM_TEAM_4))
 +                              pink_icon_prevstatus = "flag_pink_shielded";
 +                      else
 +                              pink_icon_prevstatus = string_null;
 +                      break;
 +      }
 +
 +      switch(teams_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;
        }
 +      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);
  }
  
  // Keyhunt HUD modicon section
@@@ -4236,7 -4095,7 +4236,7 @@@ float centerprint_showing
  
  void centerprint_generic(float new_id, string strMessage, float duration, float countdown_num)
  {
-       //print(sprintf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num));
+       //printf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num);
        float i, j;
  
        if(strMessage == "" && new_id == 0)
@@@ -4402,8 -4261,7 +4402,7 @@@ void HUD_CenterPrint (void
        float a, sz, align, current_msg_pos_y = 0, msg_size;
        vector pos;
        string ts;
-       n = -1; // if no msg will be displayed, n stays -1
+       float all_messages_expired = TRUE;
  
        pos = panel_pos;
        if (autocvar_hud_panel_centerprint_flip)
                                continue;
                }
  
+               all_messages_expired = FALSE;
  
                // fade the centerprint_hud in/out
-               if(centerprint_time[j] < 0)
-                       a = bound(0, (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in), 1);
-               else if(centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time)
-                       a = bound(0, (time - (centerprint_expire_time[j] - centerprint_time[j])) / max(0.0001, autocvar_hud_panel_centerprint_fade_in), 1);
-               else if(centerprint_expire_time[j] > time)
+               if(centerprint_time[j] < 0)  // Expired but forced. Expire time is the fade-in time.
+                       a = (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
+               else if(centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time)  // Regularily printed. Not fading out yet.
+                       a = (time - (centerprint_expire_time[j] - centerprint_time[j])) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
+               else // Expiring soon, so fade it out.
                        a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out);
-               else
-                       a = 0;
+               if (a <= 0.5/255.0)  // Guaranteed invisible - don't show.
+                       continue;
+               if (a > 1)
+                       a = 1;
  
                // set the size from fading in/out before subsequent fading
                sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize);
                        a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha
                        a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message
                }
+               a *= panel_fg_alpha;
  
                // finally set the size based on the new theAlpha from subsequent fading
                sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize));
                                {
                                        if (align)
                                                pos_x = panel_pos_x + (panel_size_x - stringwidth(ts, TRUE, fontsize)) * align;
-                                       drawcolorcodedstring(pos + eY * 0.5 * (1 - sz) * fontsize_y, ts, fontsize, a * panel_fg_alpha, DRAWFLAG_NORMAL);
+                                       if (a > 0.5/255.0)  // Otherwise guaranteed invisible - don't show. This is checked a second time after some multiplications with other factors were done so temporary changes of these cannot cause flicker.
+                                               drawcolorcodedstring(pos + eY * 0.5 * (1 - sz) * fontsize_y, ts, fontsize, a, DRAWFLAG_NORMAL);
                                        pos_y += fontsize_y;
                                }
                                else
                }
        }
        drawfontscale = '1 1 0';
-       if (n == -1)
+       if (all_messages_expired)
        {
                centerprint_showing = FALSE;
                reset_centerprint_messages();
index 5f36cb6b04e6d820a35a35495d0502ca7ad347cb,cde516b623dd0bbeafb6dd5b10c8382c5c491b81..64d214b0686e07c3457a9a537dc1530369514372
@@@ -269,8 -269,6 +269,8 @@@ 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 "pinkbase": return _("Pink base");
                case "waypoint": return _("Waypoint");
                case "ons-gen-red": return _("Generator");
                case "ons-gen-blue": return _("Generator");
@@@ -459,7 -457,7 +459,7 @@@ void Draw_WaypointSprite(
        if(rgb == '0 0 0')
        {
                self.teamradar_color = '1 0 1';
-               print(sprintf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage));
+               printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
        }
  
        if(time - floor(time) > 0.5)
index 3780f2563ac3d70fb8acef8fb85f6392aa7f124f,003c0fcc1d64fc4fa0e112576e4342f5933b5d36..6acee55bed23ac17e573cf084314cf023234437a
@@@ -339,24 -339,27 +339,27 @@@ void Send_Notification_WOCOVA
        MULTITEAM_INFO##teams(default,prefix,strnum,flnum,args,hudargs,icon,normal,gentle)
  
  #define MSG_INFO_NOTIFICATIONS \
 -      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(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"), "") \
 +      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"), "") \
 +      MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_DAMAGED_, 4,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG 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"), "") \
 +      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"), "") \
 +      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"), "") \
 +      MULTITEAM_INFO(1, INFO_CTF_FLAGRETURN_TIMEOUT_, 4,     0, 0, "", "",                            "",                     _("^BGThe ^TC^TT^BG 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"), "") \
 +      MULTITEAM_INFO(1, INFO_CTF_PICKUP_, 4,                 1, 0, "s1", "s1",                        "notify_%s_taken",      _("^BG%s^BG got the ^TC^TT^BG 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(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_INFO_NOTIF(1, INFO_DEATH_MURDER_FIRE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s"), _("^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_LAVA,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_lava",          _("^BG%s%s^K1 was cooked by ^BG%s^K1%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_MONSTER,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was pushed infront of a monster by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SHOOTING_STAR,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_shootingstar",  _("^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SLIME,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was slimed by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_GENERIC,             2, 1, "s1 s2loc spree_lost", "s1",       "notify_selfkill",      _("^BG%s^K1 died%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_LAVA,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_lava",          _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_MAGE,                2, 1, "s1 s2loc spree_lost", "s1",           "notify_death",                 _("^BG%s^K1 was exploded by a Mage%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_CLAW,   2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_SMASH,  2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was smashed by a Shambler%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_ZAP,    2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was zapped to death by a Shambler%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SPIDER,              2, 1, "s1 s2loc spree_lost", "s1",           "notify_death",                 _("^BG%s^K1 was bitten by a Spider%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_WYVERN,          2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_JUMP,     2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 joins the Zombies%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_MELEE,    2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was given kung fu lessons by a Zombie%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NOAMMO,              2, 1, "s1 s2loc spree_lost", "s1",       "notify_outofammo",     _("^BG%s^K1 died%s%s. What's the point of living without ammo?"), _("^BG%s^K1 ran out of ammo%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_ROT,                 2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 rotted away%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_SHOOTING_STAR,       2, 1, "s1 s2loc spree_lost", "s1",       "notify_shootingstar",  _("^BG%s^K1 became a shooting star%s%s"), "") \
        MULTITEAM_INFO(1, INFO_KEYHUNT_PICKUP_, 4,             1, 0, "s1", "",                          "",                     _("^BG%s^BG picked up the ^TC^TT Key"), "") \
        MSG_INFO_NOTIF(1, INFO_LMS_FORFEIT,                    1, 0, "s1", "",                          "",                     _("^BG%s^F3 forfeited"), "") \
        MSG_INFO_NOTIF(1, INFO_LMS_NOLIVES,                    1, 0, "s1", "",                          "",                     _("^BG%s^F3 has no more lives left"), "") \
+       MSG_INFO_NOTIF(1, INFO_MONSTERS_DISABLED,              0, 0, "", "",                            "",                     _("^BGMonsters are currently disabled"), "") \
        MSG_INFO_NOTIF(1, INFO_POWERUP_INVISIBILITY,           1, 0, "s1", "s1",                        "strength",             _("^BG%s^K1 picked up Invisibility"), "") \
        MSG_INFO_NOTIF(1, INFO_POWERUP_SHIELD,                 1, 0, "s1", "s1",                        "shield",               _("^BG%s^K1 picked up Shield"), "") \
        MSG_INFO_NOTIF(1, INFO_POWERUP_SPEED,                  1, 0, "s1", "s1",                        "shield",               _("^BG%s^K1 picked up Speed"), "") \
        MSG_INFO_NOTIF(2, INFO_QUIT_KICK_IDLING,               1, 0, "s1", "",                          "",                     _("^BG%s^F3 was kicked for idling"), "") \
        MSG_INFO_NOTIF(1, INFO_QUIT_KICK_SPECTATING,           0, 0, "", "",                            "",                     _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "") \
        MSG_INFO_NOTIF(1, INFO_QUIT_SPECTATE,                  1, 0, "s1", "",                          "",                     _("^BG%s^F3 is now spectating"), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_ABANDONED,                 1, 0, "s1", "",                                                        "",                      _("^BG%s^BG has abandoned the race"), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_FAIL_RANKED,               1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1",        "race_newfail",          _("^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s"), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_FAIL_UNRANKED,             1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1",        "race_newfail",          _("^BG%s^BG couldn't break the %s%s^BG place record of %s%s %s"), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_FINISHED,                  1, 0, "s1", "",                                                        "",                      _("^BG%s^BG has finished the race"), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_NEW_BROKEN,                2, 3, "s1 s2 race_col f1ord race_col f2race_time race_diff", "s1 s2",  "race_newrankyellow",    _("^BG%s^BG broke %s^BG's %s%s^BG place record with %s%s %s"), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_NEW_IMPROVED,              1, 3, "s1 race_col f1ord race_col f2race_time race_diff", "s1",        "race_newtime",          _("^BG%s^BG improved their %s%s^BG place record with %s%s %s"), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_NEW_MISSING_UID,           1, 1, "s1 f1race_time", "s1",                                          "race_newfail",          _("^BG%s^BG scored a new record with ^F2%s^BG, but unfortunately lacks a UID and will be lost."), "") \
-       MSG_INFO_NOTIF(1, INFO_RACE_NEW_SET,                   1, 2, "s1 race_col f1ord race_col f2race_time", "s1",                  "race_newrecordserver",  _("^BG%s^BG set the %s%s^BG place record with %s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_ABANDONED,                 1, 0, "s1", "",                                                                    "",                      _("^BG%s^BG has abandoned the race"), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_FAIL_RANKED,               1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1 f3race_time",        "race_newfail",          _("^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s"), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_FAIL_UNRANKED,             1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1 f3race_time",        "race_newfail",          _("^BG%s^BG couldn't break the %s%s^BG place record of %s%s %s"), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_FINISHED,                  1, 0, "s1", "",                                                                    "",                      _("^BG%s^BG has finished the race"), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_NEW_BROKEN,                2, 3, "s1 s2 race_col f1ord race_col f2race_time race_diff", "s1 f2race_time",     "race_newrankyellow",    _("^BG%s^BG broke %s^BG's %s%s^BG place record with %s%s %s"), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_NEW_IMPROVED,              1, 3, "s1 race_col f1ord race_col f2race_time race_diff", "s1 f2race_time",        "race_newtime",          _("^BG%s^BG improved their %s%s^BG place record with %s%s %s"), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_NEW_MISSING_UID,           1, 1, "s1 f1race_time", "s1 f1race_time",                                          "race_newfail",          _("^BG%s^BG scored a new record with ^F2%s^BG, but unfortunately lacks a UID and will be lost."), "") \
+       MSG_INFO_NOTIF(1, INFO_RACE_NEW_SET,                   1, 2, "s1 race_col f1ord race_col f2race_time", "s1 f2race_time",                  "race_newrecordserver",  _("^BG%s^BG set the %s%s^BG place record with %s%s"), "") \
        MULTITEAM_INFO(1, INFO_SCORES_, 4,                     0, 0, "", "",                            "",                     _("^TC^TT ^BGteam scores!"), "") \
        MSG_INFO_NOTIF(1, INFO_SPECTATE_WARNING,               0, 1, "f1secs", "",                      "",                     _("^F2You have to become a player within the next %s, otherwise you will be kicked, because spectating isn't allowed at this time!"), "") \
        MSG_INFO_NOTIF(1, INFO_SUPERWEAPON_PICKUP,             1, 0, "s1", "s1",                        "strength",             _("^BG%s^K1 picked up a Superweapon"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ROUND_OVER,                  0, 0, "",             CPID_ROUND,          "0 0", _("^BGRound over, there's no winner"), "") \
        MSG_CENTER_NOTIF(1, CENTER_CAMPCHECK,                   0, 0, "",             CPID_CAMPCHECK,      "0 0", _("^F2Don't camp!"), "") \
        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_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_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"), "") \
 +      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_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"), "") \
 +      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_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_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!"), "") \
 +      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")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_FIRE,             0, 0, "",             NO_CPID,             "0 0", _("^K1You got a little bit too crispy!"), _("^K1You felt a little too hot!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_GENERIC,          0, 0, "",             NO_CPID,             "0 0", _("^K1You killed your own dumb self!"), _("^K1You need to be more careful!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_LAVA,             0, 0, "",             NO_CPID,             "0 0", _("^K1You couldn't stand the heat!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_MONSTER,          0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed by a monster!"), _("^K1You need to watch out for monsters!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE,                             0, 0, "",                         NO_CPID,                         "0 0", _("^K1You forgot to put the pin back in!"), _("^K1Tastes like chicken!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NOAMMO,           0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed for running out of ammo..."), _("^K1You are respawning for running out of ammo...")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_ROT,              0, 0, "",             NO_CPID,             "0 0", _("^K1You grew too old without taking your medicine"), _("^K1You need to preserve your health")) \
        MSG_CENTER_NOTIF(1, CENTER_ROUND_PLAYER_WIN,            1, 0, "s1",           CPID_ROUND,          "0 0", _("^BG%s^BG wins the round"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF,              0, 0, "",             NO_CPID,             "0 0", _("^K1You froze yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE,        0, 0, "",             NO_CPID,             "0 0", _("^K1Round already started, you spawn as frozen"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_INVASION_SUPERMONSTER,       1, 0, "s1",           NO_CPID,             "0 0", _("^K1A %s has arrived!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE,        0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP,            1, 1, "item_wepname item_wepammo",         CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT,             0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_FALL,                    NO_MSG,        INFO_DEATH_MURDER_FALL,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_FIRE,                    NO_MSG,        INFO_DEATH_MURDER_FIRE,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA,                    NO_MSG,        INFO_DEATH_MURDER_LAVA,                    NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_MONSTER,                 NO_MSG,        INFO_DEATH_MURDER_MONSTER,                 CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE,                    NO_MSG,        INFO_DEATH_MURDER_NADE,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR,           NO_MSG,        INFO_DEATH_MURDER_SHOOTING_STAR,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME,                   NO_MSG,        INFO_DEATH_MURDER_SLIME,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_FIRE,                      NO_MSG,        INFO_DEATH_SELF_FIRE,                      CENTER_DEATH_SELF_FIRE) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_GENERIC,                   NO_MSG,        INFO_DEATH_SELF_GENERIC,                   CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_LAVA,                      NO_MSG,        INFO_DEATH_SELF_LAVA,                      CENTER_DEATH_SELF_LAVA) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_MAGE,                                  NO_MSG,        INFO_DEATH_SELF_MON_MAGE,                                  CENTER_DEATH_SELF_MONSTER) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_CLAW,                 NO_MSG,        INFO_DEATH_SELF_MON_SHAMBLER_CLAW,                 CENTER_DEATH_SELF_MONSTER) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_SMASH,                NO_MSG,        INFO_DEATH_SELF_MON_SHAMBLER_SMASH,                CENTER_DEATH_SELF_MONSTER) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_ZAP,                  NO_MSG,        INFO_DEATH_SELF_MON_SHAMBLER_ZAP,                  CENTER_DEATH_SELF_MONSTER) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SPIDER,                                NO_MSG,        INFO_DEATH_SELF_MON_SPIDER,                                CENTER_DEATH_SELF_MONSTER) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_WYVERN,                                NO_MSG,        INFO_DEATH_SELF_MON_WYVERN,                                CENTER_DEATH_SELF_MONSTER) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_JUMP,                   NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_JUMP,                   CENTER_DEATH_SELF_MONSTER) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_MELEE,                  NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_MELEE,                  CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NADE,                                              NO_MSG,                INFO_DEATH_SELF_NADE,                                      CENTER_DEATH_SELF_NADE) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO,                    NO_MSG,        INFO_DEATH_SELF_NOAMMO,                    CENTER_DEATH_SELF_NOAMMO) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_ROT,                       NO_MSG,        INFO_DEATH_SELF_ROT,                       CENTER_DEATH_SELF_ROT) \
        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_ENEMY,          MSG_CENTER,  CENTER_CTF_PICKUP_ENEMY,          CENTER_CTF_PICKUP_ENEMY_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) \
@@@ -952,9 -970,9 +975,9 @@@ string arg_slot[NOTIF_MAX_ARGS]
      ARG_CASE(ARG_CS,        "f1secs",        count_seconds(f1)) \
      ARG_CASE(ARG_CS_SV,     "f1ord",         count_ordinal(f1)) \
      ARG_CASE(ARG_CS,        "f1time",        process_time(2, f1)) \
-     ARG_CASE(ARG_CS_SV,     "f1race_time",   mmssss(f1)) \
-     ARG_CASE(ARG_CS_SV,     "f2race_time",   mmssss(f2)) \
-     ARG_CASE(ARG_CS_SV,     "f3race_time",   mmssss(f3)) \
+     ARG_CASE(ARG_CS_SV_HA,  "f1race_time",   mmssss(f1)) \
+     ARG_CASE(ARG_CS_SV_HA,  "f2race_time",   mmssss(f2)) \
+     ARG_CASE(ARG_CS_SV_HA,  "f3race_time",   mmssss(f3)) \
      ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
      ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
      ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
@@@ -1187,7 -1205,7 +1210,7 @@@ float NOTIF_CPID_COUNT
  #define NOTIF_INFO_MAX    300
  #define NOTIF_CENTER_MAX  200
  #define NOTIF_MULTI_MAX   200
 -#define NOTIF_CHOICE_MAX  20
 +#define NOTIF_CHOICE_MAX  30
  
  // notification entities
  entity msg_annce_notifs[NOTIF_ANNCE_MAX];
@@@ -1470,7 -1488,7 +1493,7 @@@ void RegisterNotifications_First(
        #define dedi ""
        #endif
  
-       print(sprintf("Beginning notification initialization on %s%s program...\n", dedi, PROGNAME));
+       printf("Beginning notification initialization on %s%s program...\n", dedi, PROGNAME);
  
        // maybe do another implementation of this with checksums? for now, we don't need versioning
        /*if(autocvar_notification_version != NOTIF_VERSION)
                #endif
                        notif_global_error = TRUE;
  
-               print(sprintf("^1NOTIFICATION VERSION MISMATCH: ^7program = %s, config = %d, code = %d.\n",
-                       PROGNAME, autocvar_notification_version, NOTIF_VERSION));
+               printf("^1NOTIFICATION VERSION MISMATCH: ^7program = %s, config = %d, code = %d.\n",
+                       PROGNAME, autocvar_notification_version, NOTIF_VERSION);
        }*/
  }
  
index fde95275f2aa00431ab64547d9eba8a0abbe55c6,fc9181d56c473061faf366bf2b1c697f3bd969de..8da96a275337e9ff2f29c8cd1da8ad50979ebabb
@@@ -83,30 -83,26 +83,26 @@@ float autocvar_g_balance_armor_start
  float autocvar_g_balance_rifle_bursttime;
  float autocvar_g_balance_rifle_primary_ammo;
  float autocvar_g_balance_rifle_primary_animtime;
- float autocvar_g_balance_rifle_primary_bulletconstant;
  float autocvar_g_balance_rifle_primary_bullethail;
  float autocvar_g_balance_rifle_primary_burstcost;
  float autocvar_g_balance_rifle_primary_damage;
  float autocvar_g_balance_rifle_primary_force;
- float autocvar_g_balance_rifle_primary_lifetime;
  float autocvar_g_balance_rifle_primary_refire;
  float autocvar_g_balance_rifle_primary_shots;
- float autocvar_g_balance_rifle_primary_speed;
+ float autocvar_g_balance_rifle_primary_solidpenetration;
  float autocvar_g_balance_rifle_primary_spread;
  float autocvar_g_balance_rifle_primary_tracer;
  float autocvar_g_balance_rifle_secondary;
  float autocvar_g_balance_rifle_secondary_ammo;
  float autocvar_g_balance_rifle_secondary_animtime;
- float autocvar_g_balance_rifle_secondary_bulletconstant;
  float autocvar_g_balance_rifle_secondary_bullethail;
  float autocvar_g_balance_rifle_secondary_burstcost;
  float autocvar_g_balance_rifle_secondary_damage;
  float autocvar_g_balance_rifle_secondary_force;
- float autocvar_g_balance_rifle_secondary_lifetime;
  float autocvar_g_balance_rifle_secondary_reload;
  float autocvar_g_balance_rifle_secondary_refire;
  float autocvar_g_balance_rifle_secondary_shots;
- float autocvar_g_balance_rifle_secondary_speed;
+ float autocvar_g_balance_rifle_secondary_solidpenetration;
  float autocvar_g_balance_rifle_secondary_spread;
  float autocvar_g_balance_rifle_secondary_tracer;
  float autocvar_g_balance_rifle_reload_ammo;
@@@ -340,7 -336,6 +336,6 @@@ float autocvar_g_balance_health_regenst
  float autocvar_g_balance_health_rot;
  float autocvar_g_balance_health_rotlinear;
  float autocvar_g_balance_health_rotstable;
- float autocvar_g_balance_health_start;
  float autocvar_g_balance_hlac_primary_ammo;
  float autocvar_g_balance_hlac_primary_animtime;
  float autocvar_g_balance_hlac_primary_damage;
@@@ -621,12 -616,11 +616,11 @@@ float autocvar_g_balance_seeker_reload_
  float autocvar_g_balance_selfdamagepercent;
  float autocvar_g_balance_shotgun_primary_ammo;
  float autocvar_g_balance_shotgun_primary_animtime;
- float autocvar_g_balance_shotgun_primary_bulletconstant;
  float autocvar_g_balance_shotgun_primary_bullets;
  float autocvar_g_balance_shotgun_primary_damage;
  float autocvar_g_balance_shotgun_primary_force;
  float autocvar_g_balance_shotgun_primary_refire;
- float autocvar_g_balance_shotgun_primary_speed;
+ float autocvar_g_balance_shotgun_primary_solidpenetration;
  float autocvar_g_balance_shotgun_primary_spread;
  float autocvar_g_balance_shotgun_secondary;
  float autocvar_g_balance_shotgun_secondary_animtime;
@@@ -654,7 -648,6 +648,6 @@@ float autocvar_g_balance_tuba_edgedamag
  float autocvar_g_balance_tuba_force;
  float autocvar_g_balance_tuba_radius;
  float autocvar_g_balance_tuba_refire;
- float autocvar_g_balance_uzi_bulletconstant;
  float autocvar_g_balance_uzi_burst;
  float autocvar_g_balance_uzi_burst_ammo;
  float autocvar_g_balance_uzi_burst_animtime;
@@@ -668,7 -661,7 +661,7 @@@ float autocvar_g_balance_uzi_first_forc
  float autocvar_g_balance_uzi_first_refire;
  float autocvar_g_balance_uzi_first_spread;
  float autocvar_g_balance_uzi_mode;
- float autocvar_g_balance_uzi_speed;
+ float autocvar_g_balance_uzi_solidpenetration;
  float autocvar_g_balance_uzi_spread_add;
  float autocvar_g_balance_uzi_spread_max;
  float autocvar_g_balance_uzi_spread_min;
@@@ -681,7 -674,6 +674,6 @@@ float autocvar_g_balance_uzi_reload_amm
  float autocvar_g_balance_uzi_reload_time;
  float autocvar_g_ballistics_density_corpse;
  float autocvar_g_ballistics_density_player;
- float autocvar_g_ballistics_materialconstant;
  float autocvar_g_ballistics_mindistance;
  float autocvar_g_ban_default_bantime;
  float autocvar_g_ban_default_masksize;
@@@ -702,6 -694,7 +694,7 @@@ float autocvar_g_ca_round_timelimit
  float autocvar_g_ca_spectate_enemies;
  float autocvar_g_ca_teams;
  float autocvar_g_ca_teams_override;
+ float autocvar_g_ca_team_spawns;
  float autocvar_g_ca_warmup;
  float autocvar_g_campaign;
  #define autocvar_g_campaign_forceteam cvar("g_campaign_forceteam")
@@@ -723,6 -716,7 +716,7 @@@ float autocvar_g_chat_teamcolors
  float autocvar_g_chat_tellprivacy;
  float autocvar_g_ctf_allow_vehicle_carry;
  float autocvar_g_ctf_allow_vehicle_touch;
+ float autocvar_g_ctf_allow_monster_touch;
  float autocvar_g_ctf_throw;
  float autocvar_g_ctf_throw_angle_max;
  float autocvar_g_ctf_throw_angle_min;
@@@ -755,18 -749,12 +749,18 @@@ float autocvar_g_ctf_flag_dropped_waypo
  float autocvar_g_ctf_flag_dropped_floatinwater;
  float autocvar_g_ctf_flag_glowtrails;
  float autocvar_g_ctf_flag_health;
 +string autocvar_g_ctf_flag_pink_model;
 +float autocvar_g_ctf_flag_pink_skin;
  string autocvar_g_ctf_flag_red_model;
  float autocvar_g_ctf_flag_red_skin;
 +float autocvar_g_ctf_flag_return;
 +float autocvar_g_ctf_flag_return_carried_radius;
  float autocvar_g_ctf_flag_return_time;
  float autocvar_g_ctf_flag_return_when_unreachable;
  float autocvar_g_ctf_flag_return_damage;
  float autocvar_g_ctf_flag_return_dropped;
 +string autocvar_g_ctf_flag_yellow_model;
 +float autocvar_g_ctf_flag_yellow_skin;
  float autocvar_g_ctf_flagcarrier_auto_helpme_damage;
  float autocvar_g_ctf_flagcarrier_auto_helpme_time;
  float autocvar_g_ctf_flagcarrier_selfdamagefactor;
@@@ -825,6 -813,7 +819,7 @@@ float autocvar_g_freezetag_revive_clear
  float autocvar_g_freezetag_round_timelimit;
  float autocvar_g_freezetag_teams;
  float autocvar_g_freezetag_teams_override;
+ float autocvar_g_freezetag_team_spawns;
  float autocvar_g_freezetag_warmup;
  #define autocvar_g_friendlyfire cvar("g_friendlyfire")
  #define autocvar_g_friendlyfire_virtual cvar("g_friendlyfire_virtual")
@@@ -1227,10 -1216,36 +1222,36 @@@ float autocvar_physics_ode
  float autocvar_g_physical_items;
  float autocvar_g_physical_items_damageforcescale;
  float autocvar_g_physical_items_reset;
+ float autocvar_g_monsters;
+ float autocvar_g_monsters_edit;
+ float autocvar_g_monsters_think_delay;
+ float autocvar_g_monsters_max;
+ float autocvar_g_monsters_max_perplayer;
+ float autocvar_g_monsters_target_range;
+ float autocvar_g_monsters_target_infront;
+ float autocvar_g_monsters_attack_range;
+ float autocvar_g_monsters_score_kill;
+ float autocvar_g_monsters_score_spawned;
+ float autocvar_g_monsters_typefrag;
+ float autocvar_g_monsters_owners;
+ float autocvar_g_monsters_miniboss_chance;
+ float autocvar_g_monsters_miniboss_healthboost;
+ float autocvar_g_monsters_drop_time;
+ float autocvar_g_monsters_spawnshieldtime;
+ float autocvar_g_monsters_teams;
+ float autocvar_g_monsters_respawn_delay;
+ float autocvar_g_monsters_respawn;
+ float autocvar_g_monsters_armor_blockpercent;
  float autocvar_g_touchexplode_radius;
  float autocvar_g_touchexplode_damage;
  float autocvar_g_touchexplode_edgedamage;
  float autocvar_g_touchexplode_force;
+ float autocvar_g_invasion_round_timelimit;
+ #define autocvar_g_invasion_round_limit cvar("g_invasion_round_limit")
+ float autocvar_g_invasion_warmup;
+ float autocvar_g_invasion_monster_count;
+ float autocvar_g_invasion_zombies_only;
+ float autocvar_g_invasion_spawn_delay;
  #define autocvar_g_bloodloss cvar("g_bloodloss")
  float autocvar_g_random_gravity_negative_chance;
  float autocvar_g_random_gravity_min;
index 0c61e4a94f3fdb48f3698a4f6fa41d06a7644b79,66c7b8715f900707381039f7913cf10faddcbc08..937eb648e2c97cafe3ac6a8290a8c32aa17f5a38
@@@ -27,9 -27,9 +27,9 @@@ void ctf_CaptureRecord(entity flag, ent
        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_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_captimerecord) || (cap_time < cap_record))
@@@ -111,26 -111,6 +111,6 @@@ float ctf_CheckPassDirection(vector hea
        else { return TRUE; }
  }
  
- float ctf_Stalemate_customizeentityforclient()
- {
-       // 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;
- }
  
  // =======================
  // CaptureShield Functions
  
  float ctf_CaptureShield_CheckStatus(entity p)
  {
 -      float s, se;
 +      float s, s2, s3, s4, se, se2, se3, se4, sr, ser;
        entity e;
        float 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;
        }
@@@ -194,7 -162,7 +174,7 @@@ void ctf_CaptureShield_Update(entity pl
  float 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;
@@@ -251,7 -219,7 +231,7 @@@ void ctf_Handle_Drop(entity flag, entit
        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, APP_TEAM_ENT_4(flag, INFO_CTF_LOST_), player.netname);
        sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("dropped", player.team, player);
  
@@@ -304,11 -272,11 +284,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, APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_SENT_), 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, APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_RECEIVED_), 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, APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_OTHER_), sender.netname, player.netname);
        }
  
        // create new waypoint
@@@ -418,13 -386,12 +398,13 @@@ void ctf_Handle_Capture(entity flag, en
        entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper);
        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; }
  
        // 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, APP_TEAM_ENT_4(enemy_flag, CENTER_CTF_CAPTURE_));
        ctf_CaptureRecord(enemy_flag, player);
 -      sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTEN_NONE);
 +      sound(player, CH_TRIGGER, ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture), VOL_BASE, ATTEN_NONE);
  
        switch(capturetype)
        {
  void ctf_Handle_Return(entity flag, entity player)
  {
        // messages and sounds
-       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);
+       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
+       {
 -              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);
  
        // scoring
-       PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return
-       PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
+       if(IS_PLAYER(player))
+       {
+               PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return
+               PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
+       }
  
        TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it
  
                ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag
                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);
  }
@@@ -494,7 -467,6 +484,7 @@@ void ctf_Handle_Pickup(entity flag, ent
  {
        // 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, APP_TEAM_ENT_4(flag, INFO_CTF_PICKUP_), player.netname);
        if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); }
 +      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, 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);
 +      Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_PICKUP_TEAM_), Team_ColorCode(player.team), player.netname);
 +
 +      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);
  
@@@ -590,14 -554,14 +580,14 @@@ void ctf_CheckFlagReturn(entity flag, f
                {
                        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, APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DROPPED_)); break;
 +                              case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DAMAGED_)); break;
 +                              case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_), ctf_captimerecord); break;
 +                              case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_NEEDKILL_)); 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, APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_TIMEOUT_)); break; }
                        }
                        sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE);
                        ctf_EventLog("returned", flag.team, world);
        }
  }
  
++float 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;
 +      float stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0;
        entity tmp_entity;
  
        entity ctf_staleflaglist = world; // reset the list, we need to build the list each time this function runs
                        {
                                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;
                        }
                }
        }
  
 -      if(stale_red_flags && stale_blue_flags)
 +      stale_flags = (stale_red_flags >= 1) + (stale_blue_flags >= 1) + (stale_yellow_flags >= 1) + (stale_pink_flags >= 1);
 +
 +      if(stale_flags == ctf_teams)
                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 < ctf_teams && 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("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_customizeentityforclient;
++                              tmp_entity.owner.wps_enemyflagcarrier.customizeentityforclient = ctf_Stalemate_Customize;
 +                      }
                }
  
                if (!wpforenemy_announced)
@@@ -784,13 -741,6 +789,13 @@@ void ctf_FlagThink(
                                        wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check)
                                }
                        }
 +                      if(CTF_SAMETEAM(self, self.owner))
 +                      {
 +                              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;
                }
  
  
                        if((self.pass_target == world)
                                || (self.pass_target.deadflag != DEAD_NO)
 +                              || (self.pass_target.flagcarried)
                                || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius)
                                || ((trace_fraction < 1) && (trace_ent != self.pass_target))
                                || (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit))
@@@ -831,6 -780,7 +836,7 @@@ void ctf_FlagTouch(
        if(gameover) { return; }
  
        entity toucher = other;
+       float is_not_monster = (!(toucher.flags & FL_MONSTER));
  
        // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces
        if(ITEM_TOUCH_NEEDKILL())
        // special touch behaviors
        if(toucher.vehicle_flags & VHF_ISVEHICLE)
        {
 -              if(autocvar_g_ctf_allow_vehicle_touch)
 +              if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner)
                        toucher = toucher.owner; // the player is actually the vehicle owner, not other
                else
                        return; // do nothing
        }
+       else if(toucher.flags & FL_MONSTER)
+       {
+               if(!autocvar_g_ctf_allow_monster_touch)
+                       return; // do nothing
+       }
        else if (!IS_PLAYER(toucher)) // The flag just touched an object, most likely the world
        {
                if(time > self.wait) // if we haven't in a while, play a sound/effect
        {
                case FLAG_BASE:
                {
-                       if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self))
 -                      if(SAME_TEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster)
++                      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(CTF_DIFFTEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time))
 -                      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)
                                ctf_Handle_Return(self, toucher); // toucher just returned his own flag
-                       else if((!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
+                       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
                        break;
                }
@@@ -912,7 -867,9 +923,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;
                        ctf_FakeTimeLimit(flag.owner, -1);
        }
  
 -      if((flag.ctf_status == FLAG_DROPPED) && (flag.wps_flagdropped))
 +      if(flag.ctf_status == FLAG_DROPPED)
                { WaypointSprite_Kill(flag.wps_flagdropped); }
  
        // reset the flag
@@@ -963,26 -920,17 +974,26 @@@ 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));
 +      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;
 +      }
 +
 +      WaypointSprite_SpawnFixed(basename, 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));
  
        // 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 ctf_FlagSetup(float 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.
        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 = ((teamnumber == NUM_TEAM_1) ? "^1RED^7 flag" : ((teamnumber == NUM_TEAM_2) ? "^2BLUE^7 flag" : ((teamnumber == NUM_TEAM_3) ? "^3YELLOW^7 flag" : "^6PINK^7 flag")));
 +      flag.team = teamnumber;
        flag.classname = "item_flag_team";
        flag.target = "###item###"; // wut?
        flag.flags = FL_ITEM | FL_NOTARGET;
        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.model == "")       { flag.model = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_model : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_model : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_model : autocvar_g_ctf_flag_pink_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"); }
 +      if(!flag.skin)             { flag.skin = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_skin : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_skin : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_skin : autocvar_g_ctf_flag_pink_skin))); }
 +      if(flag.toucheffect == "") { flag.toucheffect = ((teamnumber == NUM_TEAM_1) ? "redflag_touch" : ((teamnumber == NUM_TEAM_2) ? "blueflag_touch" : ((teamnumber == NUM_TEAM_3) ? "yellowflag_touch" : "pinkflag_touch"))); }
 +      if(flag.passeffect == "")  { flag.passeffect = ((teamnumber == NUM_TEAM_1) ? "red_pass" : ((teamnumber == NUM_TEAM_2) ? "blue_pass" : ((teamnumber == NUM_TEAM_3) ? "yellow_pass" : "pink_pass"))); }
 +      if(flag.capeffect == "")   { flag.capeffect = ((teamnumber == NUM_TEAM_1) ? "red_cap" : ((teamnumber == NUM_TEAM_2) ? "blue_cap" : ((teamnumber == NUM_TEAM_3) ? "yellow_cap" : "pink_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_taken == "")    { flag.snd_flag_taken = ((teamnumber == NUM_TEAM_1) ? "ctf/red_taken.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_taken.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_taken.wav" : "ctf/pink_taken.wav"))); }
 +      if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber == NUM_TEAM_1) ? "ctf/red_returned.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_returned.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_returned.wav" : "ctf/pink_returned.wav"))); }
 +      if(flag.snd_flag_capture == "")  { flag.snd_flag_capture = ((teamnumber == NUM_TEAM_1) ? "ctf/red_capture.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_capture.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_capture.wav" : "ctf/pink_capture.wav"))); }
 +      if(flag.snd_flag_dropped == "")  { flag.snd_flag_dropped = ((teamnumber == NUM_TEAM_1) ? "ctf/red_dropped.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_dropped.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_dropped.wav" : "ctf/pink_dropped.wav"))); }
        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(autocvar_g_ctf_flag_glowtrails)
        {
 -              flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
 +              flag.glow_color = ((teamnumber == NUM_TEAM_1) ? 251 : ((teamnumber == NUM_TEAM_2) ? 210 : ((teamnumber == NUM_TEAM_3) ? 110 : 145)));
                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;
 +              }
 +      }
  
        // flag placement
        if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location
@@@ -1120,7 -1060,7 +1131,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;
        }
@@@ -1133,7 -1073,7 +1144,7 @@@ entity havocbot_ctf_find_enemy_flag(ent
        f = ctf_worldflaglist;
        while (f)
        {
 -              if (bot.team != f.team)
 +              if (CTF_DIFFTEAM(bot, f))
                        return f;
                f = f.ctf_worldflagnext;
        }
@@@ -1166,7 -1106,7 +1177,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;
        }
@@@ -1180,7 -1120,7 +1191,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;
        }
@@@ -1196,7 -1136,7 +1207,7 @@@ void havocbot_goalrating_ctf_enemyflag(
        head = ctf_worldflaglist;
        while (head)
        {
 -              if (self.team != head.team)
 +              if(CTF_DIFFTEAM(self, head))
                        break;
                head = head.ctf_worldflagnext;
        }
@@@ -1751,37 -1691,28 +1762,37 @@@ void havocbot_role_ctf_setrole(entity b
  MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
  {
        entity flag;
 +      float 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.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_YELLOW_FLAG_CARRYING | IT_YELLOW_FLAG_TAKEN      | IT_YELLOW_FLAG_LOST
 +                                | IT_PINK_FLAG_CARRYING       | IT_PINK_FLAG_TAKEN    | IT_PINK_FLAG_LOST
 +                                | IT_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 = IT_RED_FLAG_CARRYING;         t2 = IT_RED_FLAG_TAKEN;         t3 = IT_RED_FLAG_LOST; }
 +              if(flag.team == NUM_TEAM_2) { t = IT_BLUE_FLAG_CARRYING;        t2 = IT_BLUE_FLAG_TAKEN;        t3 = IT_BLUE_FLAG_LOST; }
 +              if(flag.team == NUM_TEAM_3) { t = IT_YELLOW_FLAG_CARRYING;      t2 = IT_YELLOW_FLAG_TAKEN;      t3 = IT_YELLOW_FLAG_LOST; }
 +              if(flag.team == NUM_TEAM_4) { t = IT_PINK_FLAG_CARRYING;        t2 = IT_PINK_FLAG_TAKEN;        t3 = IT_PINK_FLAG_LOST; }
 +
                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
 +                                      self.items |= t; // 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.items |= 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.items |= t3; // lost: the flag is dropped somewhere on the map
                                break;
                        }
                }
@@@ -1813,7 -1744,7 +1824,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)
@@@ -2020,7 -1951,7 +2031,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, APP_TEAM_ENT_4(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_));
                ctf_RespawnFlag(self.flagcarried);
                return TRUE;
        }
@@@ -2068,11 -1999,6 +2079,11 @@@ MUTATOR_HOOKFUNCTION(ctf_BotRoles
        return TRUE;
  }
  
 +MUTATOR_HOOKFUNCTION(ctf_GetTeamCount)
 +{
 +      ret_float = ctf_teams;
 +      return 0;
 +}
  
  // ==========
  // Spawnfuncs
@@@ -2139,7 -2065,7 +2150,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)
@@@ -2157,43 -2083,7 +2168,43 @@@ 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 two (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_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
@@@ -2224,10 -2114,9 +2235,10 @@@ void spawnfunc_team_CTF_bluespawn()  { 
  // ==============
  
  // scoreboard setup
 -void ctf_ScoreRules()
 +void ctf_ScoreRules(float 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);
@@@ -2255,30 -2144,15 +2266,30 @@@ void ctf_SpawnTeam (string teamname, fl
  
  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); }
 +      }
 +
 +      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()
@@@ -2309,7 -2183,6 +2320,7 @@@ MUTATOR_DEFINITION(gamemode_ctf
        MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
        MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(GetTeamCount, ctf_GetTeamCount, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
        {