]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/weapons_new
authorMario <zacjardine@y7mail.com>
Fri, 5 Dec 2014 12:03:18 +0000 (23:03 +1100)
committerMario <zacjardine@y7mail.com>
Fri, 5 Dec 2014 12:03:18 +0000 (23:03 +1100)
1  2 
qcsrc/client/Main.qc
qcsrc/client/main.qh
qcsrc/client/scoreboard.qc
qcsrc/common/constants.qh
qcsrc/common/notifications.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_ca.qc
qcsrc/server/teamplay.qc
qcsrc/server/vehicles/vehicles.qc

diff --combined qcsrc/client/Main.qc
index 7ccb8968ffb485c666c17fd4408f5c05e1cb10c6,2a97056a43d21b7568ea143c620e911b6e7d41c5..cbc1247cc8a5ae9b881862d6f140a3290bc74f58
@@@ -96,6 -96,8 +96,8 @@@ void CSQC_Init(void
        for(i = 0; i < MAX_HUD_FIELDS; ++i)
                hud_title[i] = strzone("(null)");
  
+       Cmd_HUD_SetFields(0);
        postinit = false;
  
        calledhooks = 0;
        Hook_Precache();
        GibSplash_Precache();
        Casings_Precache();
 -      DamageInfo_Precache();
        Vehicles_Precache();
        turrets_precache();
        Tuba_Precache();
  
        if(autocvar_cl_reticle)
        {
 -              if(autocvar_cl_reticle_item_normal) { precache_pic("gfx/reticle_normal"); }
 -              if(autocvar_cl_reticle_item_nex) { precache_pic("gfx/reticle_nex"); }
 +              precache_pic("gfx/reticle_normal");
 +              // weapon reticles are precached in weapon files
        }
  
        get_mi_min_max_texcoords(1); // try the CLEVER way first
@@@ -315,8 -318,6 +317,6 @@@ void Porto_Init()
  void TrueAim_Init();
  void PostInit(void)
  {
-       localcmd(strcat("\nscoreboard_columns_set ", autocvar_scoreboard_columns, ";\n"));
        entity playerchecker;
        playerchecker = spawn();
        playerchecker.think = Playerchecker_Think;
@@@ -605,6 -606,27 +605,27 @@@ void Ent_Nagger(
        warmup_stage = (nags & 16);
  }
  
+ void Ent_EliminatedPlayers()
+ {
+       float sf, i, j, b, f;
+       sf = ReadByte();
+       if(sf & 1)
+       {
+               for(j = 0; j < maxclients; ++j)
+                       if(playerslots[j])
+                               playerslots[j].eliminated = 1;
+               for(i = 1; i <= maxclients; i += 8)
+               {
+                       f = ReadByte();
+                       for(j = i-1, b = 1; b < 256; b *= 2, ++j)
+                               if (!(f & b))
+                                       if(playerslots[j])
+                                               playerslots[j].eliminated = 0;
+               }
+       }
+ }
  void Ent_RandomSeed()
  {
        float s;
@@@ -803,6 -825,7 +824,7 @@@ void CSQC_Ent_Update(float bIsNewEntity
                case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break;
                case ENT_CLIENT_LASER: Ent_Laser(); break;
                case ENT_CLIENT_NAGGER: Ent_Nagger(); break;
+               case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break;
                case ENT_CLIENT_WAYPOINT: Ent_WaypointSprite(); break;
                case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break;
                case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break;
                case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break;
                case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break;
                case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break;
 -              case ENT_CLIENT_LGBEAM: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_LGBEAM); break;
 -              case ENT_CLIENT_GAUNTLET: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_GAUNTLET); break;
 +              case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break;
                case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break;
                case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break;
                case ENT_CLIENT_TURRET: ent_turret(); break;
@@@ -940,11 -964,15 +962,15 @@@ void Ent_ScoresInfo(
        HUD_ModIcons_SetFunc();
        for(i = 0; i < MAX_SCORE; ++i)
        {
+               if(scores_label[i])
+                       strunzone(scores_label[i]);
                scores_label[i] = strzone(ReadString());
                scores_flags[i] = ReadByte();
        }
        for(i = 0; i < MAX_TEAMSCORE; ++i)
        {
+               if(teamscores_label[i])
+                       strunzone(teamscores_label[i]);
                teamscores_label[i] = strzone(ReadString());
                teamscores_flags[i] = ReadByte();
        }
@@@ -962,10 -990,14 +988,10 @@@ void Ent_Init(
        hook_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
        hook_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
        hook_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
  
        if(forcefog)
                strunzone(forcefog);
  
        armorblockpercent = ReadByte() / 255.0;
  
 -      g_balance_grenadelauncher_bouncefactor = ReadCoord();
 -      g_balance_grenadelauncher_bouncestop = ReadCoord();
 +      g_balance_mortar_bouncefactor = ReadCoord();
 +      g_balance_mortar_bouncestop = ReadCoord();
        g_balance_electro_secondary_bouncefactor = ReadCoord();
        g_balance_electro_secondary_bouncestop = ReadCoord();
  
 -      nex_scope = !ReadByte();
 +      vortex_scope = !ReadByte();
        rifle_scope = !ReadByte();
  
        serverflags = ReadByte();
@@@ -1175,7 -1207,7 +1201,7 @@@ void Net_WeaponComplain(
  
        if(complain_weapon_name)
                strunzone(complain_weapon_name);
 -      complain_weapon_name = strzone(ReadString());
 +      complain_weapon_name = strzone(WEP_NAME(complain_weapon));
  
        complain_weapon_type = ReadByte();
  
@@@ -1219,16 -1251,16 +1245,16 @@@ float CSQC_Parse_TempEntity(
                        Net_ReadRace();
                        bHandled = true;
                        break;
 -              case TE_CSQC_NEXGUNBEAMPARTICLE:
 -                      Net_ReadNexgunBeamParticle();
 +              case TE_CSQC_VORTEXBEAMPARTICLE:
 +                      Net_ReadVortexBeamParticle();
                        bHandled = true;
                        break;
                case TE_CSQC_TEAMNAGGER:
                        Net_TeamNagger();
                        bHandled = true;
                        break;
 -              case TE_CSQC_LIGHTNINGARC:
 -                      Net_ReadLightningarc();
 +              case TE_CSQC_ARC:
 +                      Net_ReadArc();
                        bHandled = true;
                        break;
                case TE_CSQC_PINGPLREPORT:
                        cl_notice_read();
                        bHandled = true;
                        break;
 +              case TE_CSQC_SHOCKWAVEPARTICLE:
 +                      Net_ReadShockwaveParticle();
 +                      bHandled = true;
 +                      break;
                default:
                        // No special logic for this temporary entity; return 0 so the engine can handle it
                        bHandled = false;
diff --combined qcsrc/client/main.qh
index 2cf9a2a34c957902c51c5029fa27cbe4e920e4ec,5bf3e5bc61fc47e2b5d8fce0e20cce11e0d5024d..c9aa2fb40f8a9e86b105dcf317edc9a49cd63f1d
@@@ -86,6 -86,7 +86,7 @@@ entity teamslots[17];    // 17 teams (i
  .float gotscores;
  .entity owner;
  .float ready;
+ .float eliminated;
  
  .void(void) draw;
  .void(void) draw2d;
@@@ -136,8 -137,8 +137,8 @@@ float calledhooks
  
  .float ping, ping_packetloss, ping_movementloss;
  
 -float g_balance_grenadelauncher_bouncefactor;
 -float g_balance_grenadelauncher_bouncestop;
 +float g_balance_mortar_bouncefactor;
 +float g_balance_mortar_bouncestop;
  float g_balance_electro_secondary_bouncefactor;
  float g_balance_electro_secondary_bouncestop;
  float g_trueaim_minrange;
index 98b0bab46548b574c71ece01a580366620fd2938,657fa72ff9789bddf1b6e06c9f4e69a54027bd8d..f6c8a30512c6eadb4a8b30f81fa9c1eff0f02dc4
@@@ -287,19 -287,15 +287,15 @@@ void Cmd_HUD_Help(
                "other gamemodes except DM.\n"));
  }
  
- string HUD_DefaultColumnLayout()
- {
-       return strcat( // fteqcc sucks
-               "ping pl name | ",
-               "-teams,race,lms/kills +freezetag/kills -teams,lms/deaths +freezetag/deaths -teams,lms,race,ka/suicides +freezetag/suicides -race,dm,tdm,ka,freezetag/frags ", // tdm already has this in "score"
-               "+tdm/kills +tdm/deaths +tdm/suicides ",
-               "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns ",
-               "+lms/lives +lms/rank ",
-               "+kh/caps +kh/pushes +kh/destroyed ",
-               "?+race/laps ?+race/time ?+race/fastest ",
-               "+as/objectives +nexball/faults +nexball/goals +ka/pickups +ka/bckills +ka/bctime +freezetag/revivals ",
-               "-lms,race,nexball/score");
- }
+ #define HUD_DefaultColumnLayout() \
+ "ping pl name | " \
+ "-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
+ "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
+ "+lms/lives +lms/rank " \
+ "+kh/caps +kh/pushes +kh/destroyed " \
+ "?+race/laps ?+race/time ?+race/fastest " \
+ "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
+ "-lms,race,nb/score"
  
  void Cmd_HUD_SetFields(float argc)
  {
        float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
        float missing;
  
+       if(!gametype)
+       {
+               // set up a temporary scoreboard layout
+               // no layout can be properly set up until score_info data haven't been received
+               argc = tokenizebyseparator("0 1 ping pl name | score", " ");
+               ps_primary = 0;
+               scores_label[ps_primary] = strzone("score");
+               scores_flags[ps_primary] = SFL_ALLOW_HIDE;
+       }
        // TODO: re enable with gametype dependant cvars?
        if(argc < 3) // no arguments provided
                argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
@@@ -670,7 -676,7 +676,7 @@@ string HUD_FixScoreboardColumnWidth(flo
        return str;
  }
  
- void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
+ void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, float pl_number)
  {
        vector tmp, rgb;
        rgb = Team_ColorRGB(pl.team);
                rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
                rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
  
-       // Layout:
-       tmp_x = sbwidth;
-       tmp_y = hud_fontsize_y * 1.25;
-       tmp_z = 0;
+       vector h_pos = pos - '1 1 0';
+       vector h_size = item_size + '2 0 0';
        // alternated rows highlighting
        if(is_self)
-               drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
+               drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
        else if((scoreboard_highlight) && (!mod(pl_number,2)))
-               drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
+               drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
  
+       tmp_x = item_size_x;
        tmp_y = 0;
+       tmp_z = 0;
  
        for(i = 0; i < hud_num_fields; ++i)
        {
                        pos_x -= hud_size[i] + hud_fontsize_x;
                }
        }
+       if(pl.eliminated)
+               drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
  }
  
  /*
@@@ -907,6 -915,10 +915,10 @@@ vector HUD_Scoreboard_MakeTable(vector 
        pos_y += 1.25 * hud_fontsize_y; // skip the header
        pos_y += autocvar_scoreboard_border_thickness;
  
+       // item size
+       tmp_x = sbwidth;
+       tmp_y = hud_fontsize_y * 1.25;
        // fill the table and draw the rows
        i = 0;
        if (teamplay)
                {
                        if(pl.team != tm.team)
                                continue;
-                       HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
+                       HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
                        pos_y += 1.25 * hud_fontsize_y;
                        ++i;
                }
                {
                        if(pl.team == NUM_SPECTATOR)
                                continue;
-                       HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
+                       HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
                        pos_y += 1.25 * hud_fontsize_y;
                        ++i;
                }
@@@ -955,7 -967,7 +967,7 @@@ float average_accuracy
  vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
  {
        float i;
 -      float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
 +      float weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
        float rows;
        if(autocvar_scoreboard_accuracy_doublerows)
                rows = 2;
        if(rows == 2)
                pos_x += weapon_width / 2;
  
 -      if(switchweapon == WEP_MINSTANEX)
 +      if(switchweapon == WEP_VAPORIZER)
                g_instagib = 1; // TODO: real detection for instagib?
  
        float weapon_stats;
                self = get_weaponinfo(i);
                if (!self.weapon)
                        continue;
 -              if ((i == WEP_NEX && g_instagib) || i == WEP_PORTO || (i == WEP_MINSTANEX && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba
 +              if ((i == WEP_VORTEX && g_instagib) || i == WEP_PORTO || (i == WEP_VAPORIZER && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba
                        continue;
                weapon_stats = weapon_accuracy[i-WEP_FIRST];
  
                        weapon_alpha = 0.2 * scoreboard_alpha_fg;
  
                // weapon icon
 -              drawpic_aspect_skin(pos, strcat("weapon", self.netname), '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
 +              drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
                // the accuracy
                if(weapon_stats >= 0) {
                        weapons_with_stats += 1;
@@@ -1330,12 -1342,16 +1342,16 @@@ void HUD_DrawScoreboard(
        float specs;
        specs = 0;
        tmp = pos;
+       vector item_size;
+       item_size_x = sbwidth;
+       item_size_y = hud_fontsize_y * 1.25;
+       item_size_z = 0;
        for(pl = players.sort_next; pl; pl = pl.sort_next)
        {
                if(pl.team != NUM_SPECTATOR)
                        continue;
                pos_y += 1.25 * hud_fontsize_y;
-               HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs);
+               HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
                ++specs;
        }
  
index 2d0c83a498584a4f247a5ff76c893ac9f3c73e38,2be0112d79ea149954224636dfb225c70704392a..21216452cf6ba7b074d2ec925fd494251ee278ba
@@@ -30,18 -30,17 +30,18 @@@ const float AS_FLOAT = 8
  
  const float TE_CSQC_PICTURE = 100;
  const float TE_CSQC_RACE = 101;
 -const float TE_CSQC_NEXGUNBEAMPARTICLE = 103;
 -const float TE_CSQC_LIGHTNINGARC = 104;
 +const float TE_CSQC_VORTEXBEAMPARTICLE = 103;
 +const float TE_CSQC_ARC = 104;
  const float TE_CSQC_TEAMNAGGER = 105;
  const float TE_CSQC_PINGPLREPORT = 106;
  const float TE_CSQC_TARGET_MUSIC = 107;
  const float TE_CSQC_WEAPONCOMPLAIN = 108;
 -const float TE_CSQC_NEX_SCOPE = 109;
 +const float TE_CSQC_VORTEX_SCOPE = 109;
  const float TE_CSQC_MINELAYER_MAXMINES = 110;
  const float TE_CSQC_HAGAR_MAXROCKETS = 111;
  const float TE_CSQC_VEHICLESETUP = 112;
  const float TE_CSQC_SVNOTICE = 113;
 +const float TE_CSQC_SHOCKWAVEPARTICLE = 114;
  
  const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
  const float RACE_NET_CHECKPOINT_CLEAR = 1;
@@@ -86,7 -85,8 +86,7 @@@ const float ENT_CLIENT_WARPZONE = 24
  const float ENT_CLIENT_WARPZONE_CAMERA = 25;
  const float ENT_CLIENT_TRIGGER_MUSIC = 26;
  const float ENT_CLIENT_HOOK = 27;
 -const float ENT_CLIENT_LGBEAM = 28;
 -const float ENT_CLIENT_GAUNTLET = 29;
 +const float ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers
  const float ENT_CLIENT_ACCURACY = 30;
  const float ENT_CLIENT_SHOWNAMES = 31;
  const float ENT_CLIENT_WARPZONE_TELEPORTED = 32;
@@@ -96,7 -96,7 +96,7 @@@ const float ENT_CLIENT_BUMBLE_RAYGUN = 
  const float ENT_CLIENT_SPAWNPOINT = 36;
  const float ENT_CLIENT_SPAWNEVENT = 37;
  const float ENT_CLIENT_NOTIFICATION = 38;
+ const float ENT_CLIENT_ELIMINATEDPLAYERS = 39;
  const float ENT_CLIENT_TURRET = 40;
  const float ENT_CLIENT_AUXILIARYXHAIR = 50;
  const float ENT_CLIENT_VEHICLE = 60;
@@@ -238,7 -238,7 +238,7 @@@ const float ATTEN_MAX = 3.984375
  #define VOL_BASE 0.7
  #define VOL_BASEVOICE 1.0
  
 -// this sets sounds and other properties of the projectiles in csqc
 +// WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc
  const float PROJECTILE_ELECTRO = 1;
  const float PROJECTILE_ROCKET = 2;
  const float PROJECTILE_TAG = 3;
@@@ -247,7 -247,7 +247,7 @@@ const float PROJECTILE_ELECTRO_BEAM = 6
  const float PROJECTILE_GRENADE = 7;
  const float PROJECTILE_GRENADE_BOUNCING = 8;
  const float PROJECTILE_MINE = 9;
 -const float PROJECTILE_LASER = 10;
 +const float PROJECTILE_BLASTER = 10;
  const float PROJECTILE_HLAC = 11;
  const float PROJECTILE_SEEKER = 12;
  const float PROJECTILE_FLAC = 13;
@@@ -292,6 -292,25 +292,6 @@@ const float WATERLEVEL_NONE = 0
  const float WATERLEVEL_WETFEET = 1;
  const float WATERLEVEL_SWIMMING = 2;
  const float WATERLEVEL_SUBMERGED = 3;
 -
 -const float MAX_SHOT_DISTANCE = 32768;
 -
 -// weapon requests
 -const float WR_SETUP = 1; // (SVQC) setup weapon data
 -const float WR_THINK = 2; // (SVQC) logic to run every frame
 -const float WR_CHECKAMMO1 = 3; // (SVQC) checks ammo for weapon
 -const float WR_CHECKAMMO2 = 4; // (SVQC) checks ammo for weapon
 -const float WR_AIM = 5; // (SVQC) runs bot aiming code for this weapon
 -const float WR_PRECACHE = 6; // (CSQC and SVQC) precaches models/sounds used by this weapon
 -const float WR_SUICIDEMESSAGE = 7; // (SVQC) notification number for suicide message (may inspect w_deathtype for details)
 -const float WR_KILLMESSAGE = 8; // (SVQC) notification number for kill message (may inspect w_deathtype for details)
 -const float WR_RELOAD = 9; // (SVQC) does not need to do anything
 -const float WR_RESETPLAYER = 10; // (SVQC) does not need to do anything
 -const float WR_IMPACTEFFECT = 11; // (CSQC) impact effect
 -const float WR_SWITCHABLE = 12; // (CSQC) impact effect
 -const float WR_PLAYERDEATH = 13; // (SVQC) does not need to do anything
 -const float WR_GONETHINK = 14; // (SVQC) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed
 -
  #define SERVERFLAG_ALLOW_FULLBRIGHT 1
  #define SERVERFLAG_TEAMPLAY 2
  #define SERVERFLAG_PLAYERSTATS 4
index 96d5377d58fe88e4f3e972742a4dd5a01de5fd2b,fe7f4ec2aa507680dd6ae545cc1c86730a68aa64..471e22cfd5d6407608e8a0159d3a596074081551
@@@ -438,6 -438,8 +438,8 @@@ void Send_Notification_WOCOVA
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET,      2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_void",          _("^BG%s^K1 was in the wrong place%s%s"), "") \
        MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4,             3, 1, "s1 s2 s3loc spree_end", "s2 s1",  "notify_teamkill_%s",   _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE,                   0, 0, "", "",                            "",                     _("^F1Round already started, you will join the game in the next round"), "") \
+       MSG_INFO_NOTIF(1, INFO_CA_LEAVE,                       0, 0, "", "",                            "",                     _("^F2You will spectate in the next round"), "") \
        MSG_INFO_NOTIF(1, INFO_DOMINATION_CAPTURE_TIME,        2, 2, "s1 s2 f1 f2", "",                 "",                     _("^BG%s^BG%s^BG (%s points every %s seconds)"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K1 was frozen by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WATERMARK,                      1, 0, "s1", "",                          "",                     _("^F3SVQC Build information: ^F4%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Accordeon%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_SUICIDE,             2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ARC_MURDER,                    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhlac",             _("^BG%s%s^K1 was electrocuted by ^BG%s^K1's Arc%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_BLASTER_MURDER,                3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponlaser",            _("^BG%s%s^K1 was shot to death by ^BG%s^K1's Blaster%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_BLASTER_SUICIDE,               2, 1, "s1 s2loc spree_lost", "s1",                 "weaponlaser",            _("^BG%s^K1 shot themself to hell with their Blaster%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_CRYLINK_MURDER,                3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponcrylink",          _("^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_CRYLINK_SUICIDE,               2, 1, "s1 s2loc spree_lost", "s1",                 "weaponcrylink",          _("^BG%s^K1 felt the strong pull of their Crylink%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_MURDER_DIRECT,      3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_MURDER_SPLASH,      3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_SUICIDE,            2, 1, "s1 s2loc spree_lost", "s1",                 "weaponrocketlauncher",   _("^BG%s^K1 blew themself up with their Devastator%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_BOLT,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_COMBO,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 felt the electrifying air of ^BG%s^K1's Electro combo%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_ORBS,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 got too close to ^BG%s^K1's Electro plasma%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_BOLT,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 played with Electro plasma%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_ORBS,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 could not remember where they put their Electro plasma%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_ORBS,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 got too close to ^BG%s^K1's Electro orb%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_BOLT,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 played with Electro bolts%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_ORBS,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 could not remember where they put their Electro orb%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_MURDER_BLAST,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponfireball",         _("^BG%s%s^K1 got too close to ^BG%s^K1's fireball%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_MURDER_FIREMINE,      3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponfireball",         _("^BG%s%s^K1 got burnt by ^BG%s^K1's firemine%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_SUICIDE_BLAST,        2, 1, "s1 s2loc spree_lost", "s1",                 "weaponfireball",         _("^BG%s^K1 should have used a smaller gun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER,                   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhook",             _("^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_MURDER,            3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_SUICIDE,           2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_MURDER,                  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponlaser",            _("^BG%s%s^K1 was shot to death by ^BG%s^K1's Laser%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_SUICIDE,                 2, 1, "s1 s2loc spree_lost", "s1",                 "weaponlaser",            _("^BG%s^K1 shot themself to hell with their Laser%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SNIPE,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SPRAY,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminelayer",        _("^BG%s%s^K1 got too close to ^BG%s^K1's mine%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_SUICIDE,             2, 1, "s1 s2loc spree_lost", "s1",                 "weaponminelayer",        _("^BG%s^K1 forgot about their mine%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_MINSTANEX_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminstanex",        _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Minstanex%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_BOUNCE,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapongrenadelauncher",  _("^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_EXPLODE,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapongrenadelauncher",  _("^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE,         2, 1, "s1 s2loc spree_lost", "s1",                 "weapongrenadelauncher",  _("^BG%s^K1 didn't see their own Mortar grenade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE,        2, 1, "s1 s2loc spree_lost", "s1",                 "weapongrenadelauncher",  _("^BG%s^K1 blew themself up with their own Mortar%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_NEX_MURDER,                    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponnex",              _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Nex%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER,                  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING,    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_PIERCING,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_SUICIDE,        2, 1, "s1 s2loc spree_lost", "s1",                 "weaponrocketlauncher",   _("^BG%s^K1 blew themself up with their Rocketlauncher%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_MURDER_SPRAY,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponseeker",           _("^BG%s%s^K1 was pummeled by ^BG%s^K1's Seeker rockets%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_MURDER_TAG,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponseeker",           _("^BG%s%s^K1 was tagged by ^BG%s^K1's Seeker%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_SUICIDE,                2, 1, "s1 s2loc spree_lost", "s1",                 "weaponseeker",           _("^BG%s^K1 played with tiny Seeker rockets%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_SHOCKWAVE_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponshotgun",          _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_SHOCKWAVE_MURDER_SLAP,         3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1",  "notify_melee_shotgun",   _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SHOTGUN_MURDER,                3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponshotgun",          _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SHOTGUN_MURDER_SLAP,           3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1",  "notify_melee_shotgun",   _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_THINKING_WITH_PORTALS,         2, 1, "s1 s2loc spree_lost", "s1",                 "notify_selfkill",        _("^BG%s^K1 is now thinking with portals%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_MURDER,                   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Tuba%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_SUICIDE,                  2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SNIPE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SPRAY,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "")
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_VAPORIZER_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminstanex",        _("^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_VORTEX_MURDER,                 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponnex",              _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Vortex%s%s"), "")
  
  #define MULTITEAM_CENTER2(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) \
        MSG_CENTER_NOTIF(default, prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(NAME_TEAM_1))) \
        MULTITEAM_CENTER##teams(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle)
  
  #define MSG_CENTER_NOTIFICATIONS \
+       MSG_CENTER_NOTIF(1, CENTER_ALONE,                       0, 0, "",             NO_CPID,             "0 0", _("^F4You are now alone!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ASSAULT_ATTACKING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are attacking!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ASSAULT_DEFENDING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are defending!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN,             0, 0, "",             CPID_ROUND,          "2 0", _("^F4Begin!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART,          0, 1, "",              CPID_KEYHUNT_OTHER,    "1 f1", _("^F4Round will start in ^COUNT"), "") \
        MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN,                0, 1, "",              CPID_KEYHUNT_OTHER,    "f1 0", _("^BGScanning frequency range..."), "") \
        MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4,           0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 4, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 4, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 1, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 1, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS,             0, 1, "f1",            CPID_MISSING_PLAYERS,  "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
        MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO,             0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO_FIRST,       0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
        MSG_MULTI_NOTIF(1, MULTI_MINSTA_FINDAMMO,                ANNCE_NUM_10,  NO_MSG,                                    CENTER_MINSTA_FINDAMMO_FIRST) \
        MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER,              NO_MSG,        INFO_WEAPON_ACCORDEON_MURDER,              NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_SUICIDE,             NO_MSG,        INFO_WEAPON_ACCORDEON_SUICIDE,             CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_ARC_MURDER,                    NO_MSG,        INFO_WEAPON_ARC_MURDER,                    NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_BLASTER_MURDER,                NO_MSG,        INFO_WEAPON_BLASTER_MURDER,                NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_BLASTER_SUICIDE,               NO_MSG,        INFO_WEAPON_BLASTER_SUICIDE,               CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_MURDER,                NO_MSG,        INFO_WEAPON_CRYLINK_MURDER,                NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_SUICIDE,               NO_MSG,        INFO_WEAPON_CRYLINK_SUICIDE,               CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_DIRECT,      NO_MSG,        INFO_WEAPON_DEVASTATOR_MURDER_DIRECT,      NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_SPLASH,      NO_MSG,        INFO_WEAPON_DEVASTATOR_MURDER_SPLASH,      NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_SUICIDE,            NO_MSG,        INFO_WEAPON_DEVASTATOR_SUICIDE,            CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_BOLT,           NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_BOLT,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_COMBO,          NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_COMBO,          NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_ORBS,           NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_ORBS,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER,                   NO_MSG,        INFO_WEAPON_HOOK_MURDER,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_MURDER,            NO_MSG,        INFO_WEAPON_KLEINBOTTLE_MURDER,            NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_SUICIDE,           NO_MSG,        INFO_WEAPON_KLEINBOTTLE_SUICIDE,           CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_LASER_MURDER,                  NO_MSG,        INFO_WEAPON_LASER_MURDER,                  NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_LASER_SUICIDE,                 NO_MSG,        INFO_WEAPON_LASER_SUICIDE,                 CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SNIPE,       NO_MSG,        INFO_WEAPON_MACHINEGUN_MURDER_SNIPE,       NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SPRAY,       NO_MSG,        INFO_WEAPON_MACHINEGUN_MURDER_SPRAY,       NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_MURDER,              NO_MSG,        INFO_WEAPON_MINELAYER_MURDER,              NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_SUICIDE,             NO_MSG,        INFO_WEAPON_MINELAYER_SUICIDE,             CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_MINSTANEX_MURDER,              NO_MSG,        INFO_WEAPON_MINSTANEX_MURDER,              NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_BOUNCE,          NO_MSG,        INFO_WEAPON_MORTAR_MURDER_BOUNCE,          NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_EXPLODE,         NO_MSG,        INFO_WEAPON_MORTAR_MURDER_EXPLODE,         NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_BOUNCE,         NO_MSG,        INFO_WEAPON_MORTAR_SUICIDE_BOUNCE,         CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_EXPLODE,        NO_MSG,        INFO_WEAPON_MORTAR_SUICIDE_EXPLODE,        CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_NEX_MURDER,                    NO_MSG,        INFO_WEAPON_NEX_MURDER,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER,                  NO_MSG,        INFO_WEAPON_RIFLE_MURDER,                  NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL,             NO_MSG,        INFO_WEAPON_RIFLE_MURDER_HAIL,             NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL_PIERCING,    NO_MSG,        INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING,    NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_PIERCING,         NO_MSG,        INFO_WEAPON_RIFLE_MURDER_PIERCING,         NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_DIRECT,  NO_MSG,        INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT,  NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_SPLASH,  NO_MSG,        INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH,  NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_SUICIDE,        NO_MSG,        INFO_WEAPON_ROCKETLAUNCHER_SUICIDE,        CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_SPRAY,           NO_MSG,        INFO_WEAPON_SEEKER_MURDER_SPRAY,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_TAG,             NO_MSG,        INFO_WEAPON_SEEKER_MURDER_TAG,             NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SEEKER_SUICIDE,                NO_MSG,        INFO_WEAPON_SEEKER_SUICIDE,                CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_SHOCKWAVE_MURDER,              NO_MSG,        INFO_WEAPON_SHOCKWAVE_MURDER,              NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_SHOCKWAVE_MURDER_SLAP,         NO_MSG,        INFO_WEAPON_SHOCKWAVE_MURDER_SLAP,         NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER,                NO_MSG,        INFO_WEAPON_SHOTGUN_MURDER,                NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER_SLAP,           NO_MSG,        INFO_WEAPON_SHOTGUN_MURDER_SLAP,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_THINKING_WITH_PORTALS,         NO_MSG,        INFO_WEAPON_THINKING_WITH_PORTALS,         CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_TUBA_MURDER,                   NO_MSG,        INFO_WEAPON_TUBA_MURDER,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_TUBA_SUICIDE,                  NO_MSG,        INFO_WEAPON_TUBA_SUICIDE,                  CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SNIPE,              NO_MSG,        INFO_WEAPON_UZI_MURDER_SNIPE,              NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SPRAY,              NO_MSG,        INFO_WEAPON_UZI_MURDER_SPRAY,              NO_MSG)
 +      MSG_MULTI_NOTIF(1, WEAPON_VAPORIZER_MURDER,              NO_MSG,        INFO_WEAPON_VAPORIZER_MURDER,              NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_VORTEX_MURDER,                 NO_MSG,        INFO_WEAPON_VORTEX_MURDER,                 NO_MSG)
  
  #define MULTITEAM_CHOICE2(default,challow,prefix,chtype,optiona,optionb) \
        MSG_CHOICE_NOTIF(default, challow, prefix##RED, chtype, optiona##RED, optionb##RED) \
@@@ -1023,7 -1020,7 +1026,7 @@@ string arg_slot[NOTIF_MAX_ARGS]
      ARG_CASE(ARG_CS_SV_HA,  "f3race_time",   mmssss(f3)) \
      ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
      ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
-     ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
+     ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1)) \
      ARG_CASE(ARG_CS,        "pass_key",      ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
      ARG_CASE(ARG_CS,        "frag_ping",     notif_arg_frag_ping(TRUE, f2)) \
      ARG_CASE(ARG_CS,        "frag_stats",    notif_arg_frag_stats(f2, f3, f4)) \
      ARG_CASE(ARG_CS_SV,     "spree_inf",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(1, input, s2, f2) : "")) \
      ARG_CASE(ARG_CS_SV,     "spree_end",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
      ARG_CASE(ARG_CS_SV,     "spree_lost",    (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
 -    ARG_CASE(ARG_CS_SV,     "item_wepname",  W_Name(f1)) \
 +    ARG_CASE(ARG_CS_SV,     "item_wepname",  WEP_NAME(f1)) \
      ARG_CASE(ARG_CS_SV,     "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f1)), Buff_PrettyName(f1))) \
      ARG_CASE(ARG_CS_SV,     "item_wepammo",  (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \
      ARG_CASE(ARG_DC,        "item_centime",  ftos(autocvar_notification_item_centerprinttime)) \
@@@ -1068,26 -1065,26 +1071,26 @@@ string notif_arg_frag_stats(float fheal
                return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(FALSE, fping));
  }
  
- string notif_arg_missing_teams(float f1, float f2, float f3, float f4)
+ string notif_arg_missing_teams(float f1)
  {
        return sprintf("%s%s%s%s",
-               (f1 ?
-                       sprintf("%s%s", Team_ColoredFullName(f1 - 1), ((f2 + f3 + f4) ? ", " : ""))
+               ((f1 & 1) ?
+                       sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_1), ((f1 & (2 + 4 + 8)) ? ", " : ""))
                        :
                        ""
                ),
-               (f2 ?
-                       sprintf("%s%s", Team_ColoredFullName(f2 - 1), ((f3 + f4) ? ", " : ""))
+               ((f1 & 2) ?
+                       sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_2), ((f1 & (4 + 8)) ? ", " : ""))
                        :
                        ""
                ),
-               (f3 ?
-                       sprintf("%s%s", Team_ColoredFullName(f3 - 1), (f4 ? ", " : ""))
+               ((f1 & 4) ?
+                       sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_3), ((f1 & 8) ? ", " : ""))
                        :
                        ""
                ),
-               (f4 ?
-                       Team_ColoredFullName(f4 - 1)
+               ((f1 & 8) ?
+                       Team_ColoredFullName(NUM_TEAM_4)
                        :
                        ""
                )
index 51d7553b1bc79fac5b3527f9b3eef54831e0e3d8,578e8707e026796ce227b5724d201a637554b3d2..1aa07d6e07e5f06992e6cdde3655c3ed99d74d38
@@@ -73,8 -73,6 +73,8 @@@ void ClientData_Touch(entity e
  
  .string netname_previous;
  
 +void SetSpectator(entity player, entity spectatee);
 +
  
  /*
  =============
@@@ -260,6 -258,7 +260,7 @@@ void PutObserverInServer (void
        self.punchvector = '0 0 0';
        self.oldvelocity = self.velocity;
        self.fire_endtime = -1;
+       self.event_damage = func_null;
  }
  
  .float model_randomizer;
@@@ -368,8 -367,6 +369,8 @@@ void PutClientInServer (void
                WriteEntity(MSG_ONE, self);
        }
  
 +      SetSpectator(self, world);
 +
        // reset player keys
        self.itemkeys = 0;
  
                self.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
                self.air_finished = time + 12;
                self.dmg = 2;
 -              if(autocvar_g_balance_nex_charge)
 +              if(WEP_CVAR(vortex, charge))
                {
 -                      if(autocvar_g_balance_nex_secondary_chargepool)
 -                              self.nex_chargepool_ammo = 1;
 -                      self.nex_charge = autocvar_g_balance_nex_charge_start;
 +                      if(WEP_CVAR_SEC(vortex, chargepool))
 +                              self.vortex_chargepool_ammo = 1;
 +                      self.vortex_charge = WEP_CVAR(vortex, charge_start);
                }
  
                if(warmup_stage)
                        self.ammo_nails = warmup_start_ammo_nails;
                        self.ammo_rockets = warmup_start_ammo_rockets;
                        self.ammo_cells = warmup_start_ammo_cells;
 +                      self.ammo_plasma = warmup_start_ammo_plasma;
                        self.ammo_fuel = warmup_start_ammo_fuel;
                        self.health = warmup_start_health;
                        self.armorvalue = warmup_start_armorvalue;
                        self.ammo_nails = start_ammo_nails;
                        self.ammo_rockets = start_ammo_rockets;
                        self.ammo_cells = start_ammo_cells;
 +                      self.ammo_plasma = start_ammo_plasma;
                        self.ammo_fuel = start_ammo_fuel;
                        self.health = start_health;
                        self.armorvalue = start_armorvalue;
                else
                        self.superweapons_finished = 0;
  
 -              if(g_weaponarena_random)
 +              if(g_weaponarena_random) // WEAPONTODO: more stuff that should be in a mutator. also: rename those cvars
                {
 -                      if(g_weaponarena_random_with_laser)
 -                              self.weapons &= ~WEPSET_LASER;
 +                      if(g_weaponarena_random_with_blaster)
 +                              self.weapons &= ~WEPSET_BLASTER;
                        W_RandomWeapons(self, g_weaponarena_random);
 -                      if(g_weaponarena_random_with_laser)
 -                              self.weapons |= WEPSET_LASER;
 +                      if(g_weaponarena_random_with_blaster)
 +                              self.weapons |= WEPSET_BLASTER;
                }
  
                self.items = start_items;
                // reset fields the weapons may use
                for (j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
 -                      weapon_action(j, WR_RESETPLAYER);
 +                      WEP_ACTION(j, WR_RESETPLAYER);
  
                        // all weapons must be fully loaded when we spawn
                        entity e;
                        e = get_weaponinfo(j);
                        if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
 -                              self.(weapon_load[j]) = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));
 +                              self.(weapon_load[j]) = e.reloading_ammo;
                }
  
                oldself = self;
@@@ -633,27 -628,30 +634,27 @@@ float ClientInit_SendEntity(entity to, 
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[1]));
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[2]));
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[3]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[0]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[1]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[2]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[3]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[0]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[1]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[2]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[3]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[0]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[1]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[2]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[3]));
 +
        if(sv_foginterval && world.fog != "")
                WriteString(MSG_ENTITY, world.fog);
        else
                WriteString(MSG_ENTITY, "");
        WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent
 -      WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_grenadelauncher_bouncefactor
 -      WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_grenadelauncher_bouncestop
 -      WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_grenadelauncher_bouncefactor
 -      WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_grenadelauncher_bouncestop
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_nex_secondary); // client has to know if it should zoom or not
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_rifle_secondary); // client has to know if it should zoom or not
 +      WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO
 +      WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_mortar_bouncestop
 +      WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_mortar_bouncefactor
 +      WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_mortar_bouncestop
 +      WriteByte(MSG_ENTITY, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO
 +      WriteByte(MSG_ENTITY, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO
        WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_minelayer_limit); // minelayer max mines
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_hagar_secondary_load_max); // hagar max loadable rockets
 +      WriteByte(MSG_ENTITY, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO
 +      WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO
        WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange);
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_porto_secondary);
 +      WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO
        return TRUE;
  }
  
@@@ -665,14 -663,14 +666,14 @@@ void ClientInit_CheckUpdate(
                self.count = autocvar_g_balance_armor_blockpercent;
                self.SendFlags |= 1;
        }
 -      if(self.bouncefactor != autocvar_g_balance_grenadelauncher_bouncefactor)
 +      if(self.bouncefactor != autocvar_g_balance_mortar_bouncefactor) // WEAPONTODO
        {
 -              self.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor;
 +              self.bouncefactor = autocvar_g_balance_mortar_bouncefactor;
                self.SendFlags |= 1;
        }
 -      if(self.bouncestop != autocvar_g_balance_grenadelauncher_bouncestop)
 +      if(self.bouncestop != autocvar_g_balance_mortar_bouncestop)
        {
 -              self.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop;
 +              self.bouncestop = autocvar_g_balance_mortar_bouncestop;
                self.SendFlags |= 1;
        }
        if(self.ebouncefactor != autocvar_g_balance_electro_secondary_bouncefactor)
@@@ -786,8 -784,8 +787,8 @@@ void ClientKill_Now(
        if(self.killindicator_teamchange)
                ClientKill_Now_TeamChange();
  
-       // in any case:
-       Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
+       if(IS_PLAYER(self))
+               Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
  
        // now I am sure the player IS dead
  }
@@@ -1199,7 -1197,13 +1200,7 @@@ void ClientConnect (void
        if(!sv_foginterval && world.fog != "")
                stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
  
 -      if(autocvar_g_hitplots || strstrofs(strcat(" ", autocvar_g_hitplots_individuals, " "), strcat(" ", self.netaddress, " "), 0) >= 0)
 -      {
 -              self.hitplotfh = fopen(strcat("hits-", matchid, "-", self.netaddress, "-", ftos(self.playerid), ".plot"), FILE_WRITE);
 -              fputs(self.hitplotfh, strcat("#name ", self.netname, "\n"));
 -      }
 -      else
 -              self.hitplotfh = -1;
 +      W_HitPlotOpen(self);
  
        if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
                send_CSQC_teamnagger();
@@@ -1241,7 -1245,11 +1242,7 @@@ void ClientDisconnect (void
  
        CheatShutdownClient();
  
 -      if(self.hitplotfh >= 0)
 -      {
 -              fclose(self.hitplotfh);
 -              self.hitplotfh = -1;
 -      }
 +      W_HitPlotClose(self);
  
        anticheat_report();
        anticheat_shutdown();
@@@ -1681,7 -1689,6 +1682,7 @@@ void SpectateCopy(entity spectatee) 
        self.armortype = spectatee.armortype;
        self.armorvalue = spectatee.armorvalue;
        self.ammo_cells = spectatee.ammo_cells;
 +      self.ammo_plasma = spectatee.ammo_plasma;
        self.ammo_shells = spectatee.ammo_shells;
        self.ammo_nails = spectatee.ammo_nails;
        self.ammo_rockets = spectatee.ammo_rockets;
        self.switchweapon = spectatee.switchweapon;
        self.switchingweapon = spectatee.switchingweapon;
        self.weapon = spectatee.weapon;
 -      self.nex_charge = spectatee.nex_charge;
 -      self.nex_chargepool_ammo = spectatee.nex_chargepool_ammo;
 +      self.vortex_charge = spectatee.vortex_charge;
 +      self.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
        self.hagar_load = spectatee.hagar_load;
 +      self.arc_heat_percent = spectatee.arc_heat_percent;
        self.minelayer_mines = spectatee.minelayer_mines;
        self.punchangle = spectatee.punchangle;
        self.view_ofs = spectatee.view_ofs;
      }
  }
  
 -float SpectateUpdate() {
 +float SpectateUpdate()
 +{
        if(!self.enemy)
            return 0;
  
 -      if (self == self.enemy)
 -              return 0;
 -
 -      if (!IS_PLAYER(self.enemy))
 +      if(!IS_PLAYER(self.enemy) || self == self.enemy)
 +      {
 +              SetSpectator(self, world);
                return 0;
 +      }
  
        SpectateCopy(self.enemy);
  
@@@ -1798,25 -1803,13 +1799,25 @@@ float SpectateSet(
        return TRUE;
  }
  
 +void SetSpectator(entity player, entity spectatee)
 +{
 +      entity old_spectatee = player.enemy;
 +
 +      player.enemy = spectatee;
 +
 +      // WEAPONTODO
 +      // these are required to fix the spectator bug with arc
 +      if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
 +      if(player.enemy && player.enemy.arc_beam) { player.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
 +}
 +
  float Spectate(entity pl)
  {
        if(g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
        if(pl.team != self.team)
                return 0;
  
 -      self.enemy = pl;
 +      SetSpectator(self, pl);
        return SpectateSet();
  }
  
@@@ -1856,7 -1849,8 +1857,7 @@@ float SpectateNext(
                        other = find(other, classname, "player");
        }
  
 -      if (other)
 -              self.enemy = other;
 +      if(other) { SetSpectator(self, other); }
  
        return SpectateSet();
  }
@@@ -1895,7 -1889,7 +1896,7 @@@ float SpectatePrev(
                else
                        other = first;
        }
 -      self.enemy = other;
 +      SetSpectator(self, other);
        return SpectateSet();
  }
  
@@@ -1990,7 -1984,7 +1991,7 @@@ float nJoinAllowed(entity ignore) 
  
        float currentlyPlaying = 0;
        FOR_EACH_REALCLIENT(e)
-               if(IS_PLAYER(e) || e.caplayer == 1)
+               if(IS_PLAYER(e) || e.caplayer)
                        currentlyPlaying += 1;
  
        if(currentlyPlaying < autocvar_g_maxplayers)
   * g_maxplayers_spectator_blocktime seconds
   */
  void checkSpectatorBlock() {
-       if(IS_SPEC(self) || IS_OBSERVER(self)) {
+       if(IS_SPEC(self) || IS_OBSERVER(self))
+       if(!self.caplayer)
+       if(IS_REAL_CLIENT(self))
+       {
                if( time > (self.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
                        Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
                        dropclient(self);
@@@ -2169,7 -2166,7 +2173,7 @@@ void PlayerPreThink (void
  
        self.stat_game_starttime = game_starttime;
        self.stat_round_starttime = round_starttime;
 -      self.stat_allow_oldnexbeam = autocvar_g_allow_oldnexbeam;
 +      self.stat_allow_oldvortexbeam = autocvar_g_allow_oldvortexbeam;
        self.stat_leadlimit = autocvar_leadlimit;
  
        if(frametime)
  
                if(frametime)
                {
 -                      if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
 +                      if(self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge))
                        {
 -                              self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
 -                              self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
 -                              self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
 +                              self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
 +                              self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
 +                              self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
  
 -                              if(self.nex_charge > autocvar_g_balance_nex_charge_animlimit)
 +                              if(self.vortex_charge > WEP_CVAR(vortex, charge_animlimit))
                                {
 -                                      self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
 -                                      self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
 -                                      self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
 +                                      self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
 +                                      self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
 +                                      self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
                                }
                        }
                        else
                        do_crouch = 0;
                if(self.frozen)
                        do_crouch = 0;
 -              if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
 +
 +              // WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY
 +              // It cannot be predicted by the engine! 
 +              if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
                        do_crouch = 0;
  
                if (do_crouch)
  
                player_regen();
  
 -              // rot nex charge to the charge limit
 -              if(autocvar_g_balance_nex_charge_rot_rate && self.nex_charge > autocvar_g_balance_nex_charge_limit && self.nex_charge_rottime < time)
 -                      self.nex_charge = bound(autocvar_g_balance_nex_charge_limit, self.nex_charge - autocvar_g_balance_nex_charge_rot_rate * frametime / W_TICSPERFRAME, 1);
 +              // WEAPONTODO: Add a weapon request for this 
 +              // rot vortex charge to the charge limit
 +              if(WEP_CVAR(vortex, charge_rot_rate) && self.vortex_charge > WEP_CVAR(vortex, charge_limit) && self.vortex_charge_rottime < time)
 +                      self.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), self.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
  
                if(frametime)
                        player_anim();
                SpectatorThink();
        }
  
 +      // WEAPONTODO: Add weapon request for this
        if(!zoomstate_set)
 -              SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_NEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && autocvar_g_balance_rifle_secondary == 0));
 +              SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_VORTEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)); // WEAPONTODO
  
        float oldspectatee_status;
        oldspectatee_status = self.spectatee_status;
  
        target_voicescript_next(self);
  
 +      // WEAPONTODO: Move into weaponsystem somehow
        // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
        if(!self.weapon)
                self.clip_load = self.clip_size = 0;
index eb3648521bc85f900b8b38362093dc066c6cc20a,a3221a1bc103f7e3fcfb49efc95b813abc20dd41..9ae3f25d759a0f2ef38ad1b5c0c85c7b6d0fb1e9
@@@ -1,3 -1,126 +1,3 @@@
 -.entity accuracy;
 -.float accuracy_frags[WEP_MAXCOUNT];
 -
 -float weaponstats_buffer;
 -
 -void WeaponStats_Init()
 -{
 -      if(autocvar_sv_weaponstats_file != "")
 -              weaponstats_buffer = buf_create();
 -      else
 -              weaponstats_buffer = -1;
 -}
 -
 -#define WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot) (((vwep) + (awep) * (WEP_LAST - WEP_FIRST + 1) - (WEP_FIRST + WEP_FIRST * (WEP_LAST - WEP_FIRST + 1))) * 4 + (abot) * 2 + (vbot))
 -
 -void WeaponStats_ready(entity fh, entity pass, float status)
 -{
 -      float i, j, n, ibot, jbot, idx;
 -      vector v;
 -      string prefix, s;
 -      switch(status)
 -      {
 -              case URL_READY_CANWRITE:
 -                      // we can write
 -                      prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t");
 -                      url_fputs(fh, "#begin statsfile\n");
 -                      url_fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"));
 -#ifdef WATERMARK
 -                      url_fputs(fh, strcat("#version ", WATERMARK, "\n"));
 -#endif
 -                      url_fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_purechanges)), "\n"));
 -                      url_fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n"));
 -                      n = tokenizebyseparator(cvar_purechanges, "\n");
 -                      for(i = 0; i < n; ++i)
 -                              url_fputs(fh, strcat("#cvar_purechange ", argv(i), "\n"));
 -                      for(i = WEP_FIRST; i <= WEP_LAST; ++i) for(ibot = 0; ibot <= 1; ++ibot)
 -                              for(j = WEP_FIRST; j <= WEP_LAST; ++j) for(jbot = 0; jbot <= 1; ++jbot)
 -                              {
 -                                      idx = WEAPONSTATS_GETINDEX(i, ibot, j, jbot);
 -                                      v = stov(bufstr_get(weaponstats_buffer, idx));
 -                                      if(v != '0 0 0')
 -                                      {
 -                                              //vector is: kills hits damage
 -                                              url_fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot));
 -                                              url_fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z));
 -                                      }
 -                              }
 -                      url_fputs(fh, "#end\n\n");
 -                      url_fclose(fh);
 -                      break;
 -              case URL_READY_CANREAD:
 -                      // url_fclose is processing, we got a response for writing the data
 -                      // this must come from HTTP
 -                      print("Got response from weapon stats server:\n");
 -                      while((s = url_fgets(fh)))
 -                              print("  ", s, "\n");
 -                      print("End of response.\n");
 -                      url_fclose(fh);
 -                      break;
 -              case URL_READY_CLOSED:
 -                      // url_fclose has finished
 -                      print("Weapon stats written\n");
 -                      buf_del(weaponstats_buffer);
 -                      weaponstats_buffer = -1;
 -                      break;
 -              case URL_READY_ERROR:
 -              default:
 -                      print("Weapon stats writing failed: ", ftos(status), "\n");
 -                      buf_del(weaponstats_buffer);
 -                      weaponstats_buffer = -1;
 -                      break;
 -      }
 -}
 -
 -void WeaponStats_Shutdown()
 -{
 -      if(weaponstats_buffer < 0)
 -              return;
 -      if(autocvar_sv_weaponstats_file != "")
 -      {
 -              url_multi_fopen(autocvar_sv_weaponstats_file, FILE_APPEND, WeaponStats_ready, world);
 -      }
 -      else
 -      {
 -              buf_del(weaponstats_buffer);
 -              weaponstats_buffer = -1;
 -      }
 -}
 -
 -void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item)
 -{
 -      float idx;
 -      if(weaponstats_buffer < 0)
 -              return;
 -      if(awep < WEP_FIRST || vwep < WEP_FIRST)
 -              return;
 -      if(awep > WEP_LAST || vwep > WEP_LAST)
 -              return;
 -      idx = WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot);
 -      bufstr_set(weaponstats_buffer, idx, vtos(stov(bufstr_get(weaponstats_buffer, idx)) + item));
 -}
 -void WeaponStats_LogDamage(float awep, float abot, float vwep, float vbot, float damage)
 -{
 -      if(damage < 0)
 -              error("negative damage?");
 -      WeaponStats_LogItem(awep, abot, vwep, vbot, '0 0 1' * damage + '0 1 0');
 -}
 -void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot)
 -{
 -      WeaponStats_LogItem(awep, abot, vwep, vbot, '1 0 0');
 -}
 -
 -// changes by LordHavoc on 03/29/04 and 03/30/04 at Vermeulen's request
 -// merged player_run and player_stand to player_anim
 -// added death animations to player_anim
 -// can now spawn thrown weapons from anywhere, not just from players
 -// thrown weapons now fade out after 20 seconds
 -// created PlayerGib function
 -// PlayerDie no longer uses hitloc or damage
 -// PlayerDie now supports dying animations as well as gibbing
 -// cleaned up PlayerDie a lot
 -// added CopyBody
 -
  .entity pusher;
  .float pushltime;
  .float istypefrag;
@@@ -145,6 -268,13 +145,6 @@@ void player_anim (void
        }
  }
  
 -void SpawnThrownWeapon (vector org, float w)
 -{
 -      if(self.weapons & WepSet_FromWeapon(self.weapon))
 -              if(W_IsWeaponThrowable(self.weapon))
 -                      W_ThrowNewWeapon(self, self.weapon, FALSE, org, randomvec() * 125 + '0 0 200');
 -}
 -
  void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
        float take, save;
  
  void calculate_player_respawn_time()
  {
+       if(g_ca)
+               return;
        float gametype_setting_tmp;
        float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
        float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
@@@ -431,7 -564,7 +434,7 @@@ void PlayerDamage (entity inflictor, en
                                        }
  
                                        if(sound_allowed(MSG_BROADCAST, attacker))
 -                                      if(!DEATH_ISWEAPON(deathtype, WEP_LASER) || attacker != self || self.health < 2 * autocvar_g_balance_laser_primary_damage * autocvar_g_balance_selfdamagepercent + 1)
 +                                      if((self.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !((get_weaponinfo(DEATH_WEAPONOF(deathtype))).spawnflags & WEP_FLAG_CANCLIMB) || attacker != self) // WEAPONTODO: create separate limit for pain notification with laser
                                        if(self.health > 1)
                                        // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two
                                        {
                frag_deathtype = deathtype;
                MUTATOR_CALLHOOK(PlayerDies);
  
 -              weapon_action(self.weapon, WR_PLAYERDEATH);
 +              WEP_ACTION(self.weapon, WR_PLAYERDEATH);
  
                RemoveGrapplingHook(self);
  
                self.flags &= ~FL_ONGROUND;
                // dying animation
                self.deadflag = DEAD_DYING;
                // when to allow respawn
                calculate_player_respawn_time();
  
                // reset fields the weapons may use just in case
                for (j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
 -                      weapon_action(j, WR_RESETPLAYER);
 +                      WEP_ACTION(j, WR_RESETPLAYER);
                        ATTACK_FINISHED_FOR(self, j) = 0;
                }
        }
index 2df6a50d4fe77aea40c23b300072cc4a19b3ecee,f3f4d71bc45e7a841599447ae9883925f7490d66..33010d174171b6b55e989669d3650e4b598b4caf
@@@ -355,13 -355,12 +355,13 @@@ string formatmessage(string msg
                                wep = self.switchweapon;
                        if (!wep)
                                wep = self.cnt;
 -                      replacement = W_Name(wep);
 +                      replacement = WEP_NAME(wep);
                } else if (escape == "W") {
                        if (self.items & IT_SHELLS) replacement = "shells";
                        else if (self.items & IT_NAILS) replacement = "bullets";
                        else if (self.items & IT_ROCKETS) replacement = "rockets";
                        else if (self.items & IT_CELLS) replacement = "cells";
 +                      else if (self.items & IT_PLASMA) replacement = "plasma";
                        else replacement = "batteries"; // ;)
                } else if (escape == "x") {
                        replacement = cursor_ent.netname;
@@@ -461,6 -460,7 +461,6 @@@ void GetCvars_handleFloatOnce(string th
                        stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
        }
  }
 -float w_getbestweapon(entity e);
  string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
  {
        string o;
@@@ -563,8 -563,6 +563,8 @@@ float g_pickup_rockets
  float g_pickup_rockets_max;
  float g_pickup_cells;
  float g_pickup_cells_max;
 +float g_pickup_plasma;
 +float g_pickup_plasma_max;
  float g_pickup_fuel;
  float g_pickup_fuel_jetpack;
  float g_pickup_fuel_max;
@@@ -597,7 -595,7 +597,7 @@@ float g_pickup_weapons_anyway
  float g_weaponarena;
  WepSet g_weaponarena_weapons;
  float g_weaponarena_random;
 -float g_weaponarena_random_with_laser;
 +float g_weaponarena_random_with_blaster;
  string g_weaponarena_list;
  float g_weaponspeedfactor;
  float g_weaponratefactor;
@@@ -613,7 -611,6 +613,7 @@@ float start_ammo_shells
  float start_ammo_nails;
  float start_ammo_rockets;
  float start_ammo_cells;
 +float start_ammo_plasma;
  float start_ammo_fuel;
  float start_health;
  float start_armorvalue;
@@@ -625,13 -622,14 +625,13 @@@ float warmup_start_ammo_shells
  float warmup_start_ammo_nails;
  float warmup_start_ammo_rockets;
  float warmup_start_ammo_cells;
 +float warmup_start_ammo_plasma;
  float warmup_start_ammo_fuel;
  float warmup_start_health;
  float warmup_start_armorvalue;
  float g_weapon_stay;
  
 -entity get_weaponinfo(float w);
 -
 -float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
 +float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done? 
  {
        var float i = weaponinfo.weapon;
        var float d = 0;
        else if (g_nexball)
                d = 0; // weapon is set a few lines later
        else
 -              d = (i == WEP_LASER || i == WEP_SHOTGUN);
 +              d = !(!weaponinfo.weaponstart);
  
        if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
                d |= (i == WEP_HOOK);
 -      if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
 +      if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
                d = 0;
  
 -      var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
 +      var float t = weaponinfo.weaponstartoverride;
  
        //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
  
@@@ -689,7 -687,6 +689,7 @@@ void readplayerstartcvars(
        start_ammo_nails = 0;
        start_ammo_rockets = 0;
        start_ammo_cells = 0;
 +      start_ammo_plasma = 0;
        start_health = cvar("g_balance_health_start");
        start_armorvalue = cvar("g_balance_armor_start");
  
                g_weaponarena_random = cvar("g_weaponarena_random");
        else
                g_weaponarena_random = 0;
 -      g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
 +      g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
  
        if (g_weaponarena)
        {
                for (i = WEP_FIRST; i <= WEP_LAST; ++i)
                {
                        e = get_weaponinfo(i);
 -                      float w = want_weapon("g_start_weapon_", e, FALSE);
 +                      float w = want_weapon(e, FALSE);
                        if(w & 1)
                                start_weapons |= WepSet_FromWeapon(i);
                        if(w & 2)
                start_ammo_rockets = 999;
                start_ammo_shells = 999;
                start_ammo_cells = 999;
 +              start_ammo_plasma = 999;
                start_ammo_nails = 999;
                start_ammo_fuel = 999;
        }
                start_ammo_nails = cvar("g_start_ammo_nails");
                start_ammo_rockets = cvar("g_start_ammo_rockets");
                start_ammo_cells = cvar("g_start_ammo_cells");
 +              start_ammo_plasma = cvar("g_start_ammo_plasma");
                start_ammo_fuel = cvar("g_start_ammo_fuel");
        }
  
                        for (i = WEP_FIRST; i <= WEP_LAST; ++i)
                        {
                                e = get_weaponinfo(i);
 -                              float w = want_weapon("g_start_weapon_", e, g_warmup_allguns);
 +                              float w = want_weapon(e, g_warmup_allguns);
                                if(w & 1)
                                        warmup_start_weapons |= WepSet_FromWeapon(i);
                                if(w & 2)
        {
                e = get_weaponinfo(i);
                if(precache_weapons & WepSet_FromWeapon(i))
 -                      weapon_action(i, WR_PRECACHE);
 +                      WEP_ACTION(i, WR_INIT);
        }
  
        start_ammo_shells = max(0, start_ammo_shells);
        start_ammo_nails = max(0, start_ammo_nails);
        start_ammo_cells = max(0, start_ammo_cells);
 +      start_ammo_plasma = max(0, start_ammo_plasma);
        start_ammo_rockets = max(0, start_ammo_rockets);
        start_ammo_fuel = max(0, start_ammo_fuel);
  
        warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
        warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
        warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
 +      warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
        warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
        warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
  }
@@@ -986,8 -979,6 +986,8 @@@ void readlevelcvars(void
        g_pickup_rockets_max = cvar("g_pickup_rockets_max");
        g_pickup_cells = cvar("g_pickup_cells");
        g_pickup_cells_max = cvar("g_pickup_cells_max");
 +      g_pickup_plasma = cvar("g_pickup_plasma");
 +      g_pickup_plasma_max = cvar("g_pickup_plasma_max");
        g_pickup_fuel = cvar("g_pickup_fuel");
        g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
        g_pickup_fuel_max = cvar("g_pickup_fuel_max");
@@@ -1366,6 -1357,18 +1366,6 @@@ void precache(
          precache_sound ("weapons/hook_impact.wav"); // hook
      }
  
 -    if(autocvar_sv_precacheweapons)
 -    {
 -        //precache weapon models/sounds
 -        float wep;
 -        wep = WEP_FIRST;
 -        while (wep <= WEP_LAST)
 -        {
 -            weapon_action(wep, WR_PRECACHE);
 -            wep = wep + 1;
 -        }
 -    }
 -
      precache_model("models/elaser.mdl");
      precache_model("models/laser.mdl");
      precache_model("models/ebomb.mdl");
@@@ -1587,6 -1590,44 +1587,44 @@@ void Net_LinkEntity(entity e, float doc
      }
  }
  
+ entity eliminatedPlayers;
+ .float(entity) isEliminated;
+ float EliminatedPlayers_SendEntity(entity to, float sendflags)
+ {
+       float i, f, b;
+       entity e;
+       WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
+       WriteByte(MSG_ENTITY, sendflags);
+       if(sendflags & 1)
+       {
+               for(i = 1; i <= maxclients; i += 8)
+               {
+                       for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
+                       {
+                               if(eliminatedPlayers.isEliminated(e))
+                                       f |= b;
+                       }
+                       WriteByte(MSG_ENTITY, f);
+               }
+       }
+       return TRUE;
+ }
+ void EliminatedPlayers_Init(float(entity) isEliminated_func)
+ {
+       if(eliminatedPlayers)
+       {
+               backtrace("Can't spawn eliminatedPlayers again!");
+               return;
+       }
+       Net_LinkEntity(eliminatedPlayers = spawn(), FALSE, 0, EliminatedPlayers_SendEntity);
+       eliminatedPlayers.isEliminated = isEliminated_func;
+ }
  void adaptor_think2touch()
  {
      entity o;
index 0c5e01a6acbb20dff70de980a39e7884cd27dc8d,e3c07f59e3807ddab5fa4f75eafb993fe1177650..d430f1b00748774083534ce19420b5f1abff0f16
@@@ -113,42 -113,57 +113,57 @@@ void CA_RoundStart(
                allowed_to_spawn = FALSE;
  }
  
- float prev_total_players;
+ float prev_missing_teams_mask;
  float CA_CheckTeams()
  {
        allowed_to_spawn = TRUE;
        CA_count_alive_players();
        if(CA_ALIVE_TEAMS_OK())
        {
-               if(prev_total_players > 0)
+               if(prev_missing_teams_mask > 0)
                        Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
-               prev_total_players = -1;
+               prev_missing_teams_mask = -1;
                return 1;
        }
-       if(prev_total_players != total_players)
+       if(total_players == 0)
        {
-               float p1 = 0, p2 = 0, p3 = 0, p4 = 0;
-               if(!redalive) p1 = NUM_TEAM_1;
-               if(!bluealive) p2 = NUM_TEAM_2;
-               if(ca_teams >= 3)
-               if(!yellowalive) p3 = NUM_TEAM_3;
-               if(ca_teams >= 4)
-               if(!pinkalive) p4 = NUM_TEAM_4;
-               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4);
-               prev_total_players = total_players;
+               if(prev_missing_teams_mask > 0)
+                       Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+               prev_missing_teams_mask = -1;
+               return 0;
+       }
+       float missing_teams_mask = (!redalive) + (!bluealive) * 2;
+       if(ca_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
+       if(ca_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+       if(prev_missing_teams_mask != missing_teams_mask)
+       {
+               Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
+               prev_missing_teams_mask = missing_teams_mask;
        }
        return 0;
  }
  
+ float ca_isEliminated(entity e)
+ {
+       if(e.caplayer == 1 && (e.deadflag != DEAD_NO || e.frags == FRAGS_LMS_LOSER))
+               return TRUE;
+       if(e.caplayer == 0.5)
+               return TRUE;
+       return FALSE;
+ }
  MUTATOR_HOOKFUNCTION(ca_PlayerSpawn)
  {
        self.caplayer = 1;
+       if(!warmup_stage)
+               eliminatedPlayers.SendFlags |= 1;
        return 1;
  }
  
  MUTATOR_HOOKFUNCTION(ca_PutClientInServer)
  {
        if(!allowed_to_spawn)
+       if(IS_PLAYER(self)) // this is true even when player is trying to join
        {
                self.classname = "observer";
                if(self.jointime != time) //not when connecting
                {
                        self.caplayer = 0.5;
                        if(IS_REAL_CLIENT(self))
-                               sprint(self, "You will join the game in the next round.\n");
+                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CA_JOIN_LATE);
                }
        }
        return 1;
@@@ -167,6 -182,11 +182,11 @@@ MUTATOR_HOOKFUNCTION(ca_reset_map_playe
        FOR_EACH_CLIENT(self)
        {
                self.killcount = 0;
+               if(!self.caplayer && IS_BOT_CLIENT(self))
+               {
+                       self.team = -1;
+                       self.caplayer = 1;
+               }
                if(self.caplayer)
                {
                        self.classname = "player";
@@@ -195,10 -215,47 +215,47 @@@ MUTATOR_HOOKFUNCTION(ca_GetTeamCount
        return 0;
  }
  
+ entity ca_LastPlayerForTeam()
+ {
+       entity pl, last_pl = world;
+       FOR_EACH_PLAYER(pl)
+       {
+               if(pl.health >= 1)
+               if(pl != self)
+               if(pl.team == self.team)
+               if(!last_pl)
+                       last_pl = pl;
+               else
+                       return world;
+       }
+       return last_pl;
+ }
+ void ca_LastPlayerForTeam_Notify()
+ {
+       if(round_handler_IsActive())
+       if(round_handler_IsRoundStarted())
+       {
+               entity pl = ca_LastPlayerForTeam();
+               if(pl)
+                       Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
+       }
+ }
  MUTATOR_HOOKFUNCTION(ca_PlayerDies)
  {
+       ca_LastPlayerForTeam_Notify();
        if(!allowed_to_spawn)
                self.respawn_flags =  RESPAWN_SILENT;
+       if(!warmup_stage)
+               eliminatedPlayers.SendFlags |= 1;
+       return 1;
+ }
+ MUTATOR_HOOKFUNCTION(ca_ClientDisconnect)
+ {
+       if(self.caplayer == 1)
+               ca_LastPlayerForTeam_Notify();
        return 1;
  }
  
@@@ -209,10 -266,14 +266,14 @@@ MUTATOR_HOOKFUNCTION(ca_ForbidPlayerSco
  
  MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver)
  {
+       if(self.caplayer == 1)
+               ca_LastPlayerForTeam_Notify();
        if(self.killindicator_teamchange == -2)
                self.caplayer = 0;
        if(self.caplayer)
                self.frags = FRAGS_LMS_LOSER;
+       if(!warmup_stage)
+               eliminatedPlayers.SendFlags |= 1;
        return 1;
  }
  
@@@ -236,7 -297,6 +297,7 @@@ MUTATOR_HOOKFUNCTION(ca_SetStartItems
        start_ammo_nails   = warmup_start_ammo_nails   = cvar("g_lms_start_ammo_nails");
        start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
        start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_lms_start_ammo_cells");
 +      start_ammo_plasma  = warmup_start_ammo_plasma  = cvar("g_lms_start_ammo_plasma");
        start_ammo_fuel    = warmup_start_ammo_fuel    = cvar("g_lms_start_ammo_fuel");
  
        return 0;
@@@ -300,6 -360,8 +361,8 @@@ void ca_Initialize(
        addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
        addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
        addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
+       EliminatedPlayers_Init(ca_isEliminated);
  }
  
  MUTATOR_DEFINITION(gamemode_ca)
        MUTATOR_HOOK(reset_map_players, ca_reset_map_players, CBC_ORDER_ANY);
        MUTATOR_HOOK(GetTeamCount, ca_GetTeamCount, CBC_ORDER_EXCLUSIVE);
        MUTATOR_HOOK(PlayerDies, ca_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientDisconnect, ca_ClientDisconnect, CBC_ORDER_ANY);
        MUTATOR_HOOK(ForbidPlayerScore_Clear, ca_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
        MUTATOR_HOOK(ForbidThrowCurrentWeapon, ca_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
        MUTATOR_HOOK(GiveFragsForKill, ca_GiveFragsForKill, CBC_ORDER_FIRST);
diff --combined qcsrc/server/teamplay.qc
index c8b02ed2db6441c59f6e24a8abf953cf9b1d2842,285e52c80f0a68580a14aa7cb13d4525017123b1..bd5d3607c127e2178a62cce0241d72a09ae7c32f
@@@ -291,7 -291,7 +291,7 @@@ string getwelcomemessage(void
                else
                        modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
        }
 -      if(autocvar_g_start_weapon_laser == 0)
 +      if(cvar("g_balance_blaster_weaponstart") == 0)
                modifications = strcat(modifications, ", No start weapons");
        if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
                modifications = strcat(modifications, ", Low gravity");
@@@ -515,7 -515,7 +515,7 @@@ void GetTeamCounts(entity ignore
        FOR_EACH_CLIENT(head)
        {
                float t;
-               if(IS_PLAYER(head))
+               if(IS_PLAYER(head) || head.caplayer)
                        t = head.team;
                else if(head.team_forced > 0)
                        t = head.team_forced; // reserve the spot
index 898274f090ec8e5bdd41b58e166a23da67ba7519,9042bd4e810be0b9382d845ee99dd49902a9bfda..785d39516f79968a6202dd6901fb2b778038a96e
@@@ -3,10 -3,10 +3,10 @@@ float autocvar_g_vehicles_crush_force
  float autocvar_g_vehicles_delayspawn;
  float autocvar_g_vehicles_delayspawn_jitter;
  
 -var float autocvar_g_vehicles_nex_damagerate = 0.5;
 -var float autocvar_g_vehicles_uzi_damagerate = 0.5;
 +var float autocvar_g_vehicles_vortex_damagerate = 0.5;
 +var float autocvar_g_vehicles_machinegun_damagerate = 0.5;
  var float autocvar_g_vehicles_rifle_damagerate = 0.75;
 -var float autocvar_g_vehicles_minstanex_damagerate = 0.001;
 +var float autocvar_g_vehicles_vaporizer_damagerate = 0.001;
  var float autocvar_g_vehicles_tag_damagerate = 5;
  
  float autocvar_g_vehicles;
@@@ -397,7 -397,7 +397,7 @@@ void vehicles_projectile_explode(
        PROJECTILE_TOUCH;
  
        self.event_damage = func_null;
 -    RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, self.shot_force, self.totalfrags, other);
 +    RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, world, self.shot_force, self.totalfrags, other);
  
      remove (self);
  }
@@@ -608,6 -608,12 +608,12 @@@ void vehicles_enter(
  
      if(self.phase > time)
          return;
+     if(other.frozen)
+         return;
+     if(other.vehicle)
+         return;
+     if(other.deadflag != DEAD_NO)
+         return;
  
      if(teamplay)
      if(self.team)
@@@ -908,18 -914,17 +914,18 @@@ void vehicles_damage(entity inflictor, 
  {
      self.dmg_time = time;
  
 -    if(DEATH_ISWEAPON(deathtype, WEP_NEX))
 -        damage *= autocvar_g_vehicles_nex_damagerate;
 +      // WEAPONTODO
 +    if(DEATH_ISWEAPON(deathtype, WEP_VORTEX))
 +        damage *= autocvar_g_vehicles_vortex_damagerate;
  
 -    if(DEATH_ISWEAPON(deathtype, WEP_UZI))
 -        damage *= autocvar_g_vehicles_uzi_damagerate;
 +    if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN))
 +        damage *= autocvar_g_vehicles_machinegun_damagerate;
  
      if(DEATH_ISWEAPON(deathtype, WEP_RIFLE))
          damage *= autocvar_g_vehicles_rifle_damagerate;
  
 -    if(DEATH_ISWEAPON(deathtype, WEP_MINSTANEX))
 -        damage *= autocvar_g_vehicles_minstanex_damagerate;
 +    if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
 +        damage *= autocvar_g_vehicles_vaporizer_damagerate;
  
      if(DEATH_ISWEAPON(deathtype, WEP_SEEKER))
          damage *= autocvar_g_vehicles_tag_damagerate;