]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote branch 'origin/master' into samual/mutator_ctf
authorSamual <samual@xonotic.org>
Wed, 8 Feb 2012 22:33:07 +0000 (17:33 -0500)
committerSamual <samual@xonotic.org>
Wed, 8 Feb 2012 22:33:07 +0000 (17:33 -0500)
Conflicts:
qcsrc/common/items.qh
qcsrc/server/bot/havocbot/role_ctf.qc
qcsrc/server/cl_client.qc
qcsrc/server/teamplay.qc

16 files changed:
1  2 
qcsrc/common/items.qh
qcsrc/server/arena.qc
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/role_ctf.qc
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/mutators.qh
qcsrc/server/nexball.qc
qcsrc/server/portals.qc
qcsrc/server/progs.src
qcsrc/server/teamplay.qc
qcsrc/server/vehicles/vehicles.qc

diff --combined qcsrc/common/items.qh
index 59f5e47409bee1a5d374b2d6893b27c1bf12c2f6,07a7625345dcddeb80745a044bebbaf9b57536a1..21c115fa38140767ed3fdf85a6492354e7b8ccb2
@@@ -2,53 -2,53 +2,53 @@@ float BOT_PICKUP_RATING_LOW   = 2500
  float BOT_PICKUP_RATING_MID   = 5000;
  float BOT_PICKUP_RATING_HIGH  = 10000;
  
- float WEP_TYPE_OTHER      = 0x00;     // e.g: Hook, Port-o-launch, etc
- float WEP_TYPE_SPLASH     = 0x01;
- float WEP_TYPE_HITSCAN            = 0x02;
- float WEP_TYPEMASK          = 0x0F;
- float WEP_FLAG_CANCLIMB     = 0x10;
- float WEP_FLAG_NORMAL       = 0x20;
- float WEP_FLAG_HIDDEN       = 0x40;
- float WEP_FLAG_RELOADABLE   = 0x80;
+ float WEP_TYPE_OTHER      =  0x00;    // e.g: Hook, Port-o-launch, etc
+ float WEP_TYPE_SPLASH     =  0x01;
+ float WEP_TYPE_HITSCAN            =  0x02;
+ float WEP_TYPEMASK          =  0x0F;
+ float WEP_FLAG_CANCLIMB     =  0x10;
+ float WEP_FLAG_NORMAL       =  0x20;
+ float WEP_FLAG_HIDDEN       =  0x40;
+ float WEP_FLAG_RELOADABLE   =  0x80;
+ float WEP_FLAG_SUPERWEAPON  = 0x100;
  
- float IT_UNLIMITED_WEAPON_AMMO  = 1;
+ float IT_UNLIMITED_WEAPON_AMMO     = 1;
  // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
- float IT_UNLIMITED_SUPERWEAPONS = 2;
- // when this bit is set, using a superweapon does not throw it away. Checkpoints can give this powerup.
- float IT_CTF_SHIELDED           = 4; // set for the flag shield
- // using jetpack
- float   IT_USING_JETPACK          = 8; // confirmation that button is pressed
- float   IT_JETPACK                = 16; // actual item
- float IT_FUEL_REGEN             = 32; // fuel regeneration trigger
- float IT_SHELLS                               = 256;
- float IT_NAILS                                = 512;
- float IT_ROCKETS                              = 1024;
- float IT_CELLS                                = 2048;
- float IT_SUPERWEAPON                  = 4096;
- float IT_FUEL                                 = 128;
- float IT_STRENGTH                             = 8192;
- float IT_INVINCIBLE                   = 16384;
- float IT_HEALTH                               = 32768;
+ float IT_UNLIMITED_SUPERWEAPONS    = 2;
+ // when this bit is set, superweapons don't expire. Checkpoints can give this powerup.
+ float   IT_CTF_SHIELDED              = 4; // set for the flag shield
+ float   IT_USING_JETPACK             = 8; // confirmation that button is pressed
+ float   IT_JETPACK                   = 16; // actual item
+ float   IT_FUEL_REGEN                = 32; // fuel regeneration trigger
+ float   IT_SHELLS                    = 256;
+ float   IT_NAILS                     = 512;
+ float   IT_ROCKETS                   = 1024;
+ float   IT_CELLS                     = 2048;
+ float   IT_SUPERWEAPON               = 4096;
+ float   IT_FUEL                      = 128;
+ float   IT_STRENGTH                  = 8192;
+ float   IT_INVINCIBLE                = 16384;
+ float   IT_HEALTH                    = 32768;
  // union:
 -        // for items:
 -        float   IT_KEY1              = 131072;
 -        float   IT_KEY2              = 262144;
 -        // for players:
 -        float   IT_RED_FLAG_TAKEN    = 32768;
 -        float   IT_RED_FLAG_LOST     = 65536;
 -        float   IT_RED_FLAG_CARRING  = 98304;
 -        float   IT_BLUE_FLAG_TAKEN   = 131072;
 -        float   IT_BLUE_FLAG_LOST    = 262144;
 -        float   IT_BLUE_FLAG_CARRING = 393216;
 +      // for items:
 +      float   IT_KEY1                                 = 131072;
 +      float   IT_KEY2                                 = 262144;
 +      // for players:
 +      float   IT_RED_FLAG_TAKEN               = 32768;
 +      float   IT_RED_FLAG_LOST                = 65536;
 +      float   IT_RED_FLAG_CARRYING            = 98304;
 +      float   IT_BLUE_FLAG_TAKEN              = 131072;
 +      float   IT_BLUE_FLAG_LOST               = 262144;
 +      float   IT_BLUE_FLAG_CARRYING   = 393216;
  // end
- float IT_5HP                                  = 524288;
- float IT_25HP                                 = 1048576;
- float IT_ARMOR_SHARD                  = 2097152;
- float IT_ARMOR                                = 4194304;
+ float   IT_5HP                       = 524288;
+ float   IT_25HP                      = 1048576;
+ float   IT_ARMOR_SHARD               = 2097152;
+ float   IT_ARMOR                     = 4194304;
  
- float IT_AMMO                                 = 8064; // IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_SUPERWEAPON | IT_FUEL;
- float IT_PICKUPMASK           = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately
- float IT_UNLIMITED_AMMO       = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO;
+ float   IT_AMMO                      = 3968; // IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_FUEL;
+ float   IT_PICKUPMASK                = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately
+ float   IT_UNLIMITED_AMMO            = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO;
  
  float AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
  
@@@ -94,6 -94,7 +94,7 @@@ float WEP_FIRST = 1
  float WEP_LAST;
  #define WEP_MAXCOUNT 24
  float WEPBIT_ALL;
+ float WEPBIT_SUPERWEAPONS;
  #define REGISTER_WEAPON_2(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
        float id; \
        float bit; \
        { \
                WEP_LAST = (id = WEP_FIRST + WEP_COUNT); \
                WEPBIT_ALL |= (bit = power2of(WEP_COUNT)); \
+               WEPBIT_SUPERWEAPONS |= (bit = power2of(WEP_COUNT)) * !!(weapontype & WEP_FLAG_SUPERWEAPON); \
                ++WEP_COUNT; \
                register_weapon(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname); \
        } \
diff --combined qcsrc/server/arena.qc
index 7c73f1fbbc31dccbf7976ee27bd7f86cccda0e6a,c89c2b2efada451a84b1990255d437641088195b..e2fb8191d3c43977273ba04fa4567988c303466f
@@@ -8,15 -8,13 +8,13 @@@ float arena_roundbased
  entity spawnqueue_first;
  entity spawnqueue_last;
  entity champion;
- string champion_name;
  float warmup;
- float ca_players;
- float required_ca_players;
+ float ca_teams_ok;
  .float caplayer;
  
  void PutObserverInServer();
  void PutClientInServer();
 -void(entity e) ReturnFlag;
 +void ctf_RespawnFlag(entity); // FIXCTF
  void dom_controlpoint_setup();
  void onslaught_generator_reset();
  void onslaught_controlpoint_reset();
@@@ -29,8 -27,8 +27,8 @@@ float stopalivecheck
  float redalive, bluealive, yellowalive, pinkalive;
  float totalalive;
  .float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
- float redspawned, bluespawned, yellowspawned, pinkspawned;
- float totalspawned;
+ float red_players, blue_players, yellow_players, pink_players;
+ float total_players;
  
  /**
   * Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map.
@@@ -134,7 -132,7 +132,7 @@@ void reset_map(float dorespawn
        }
  
        if(g_keyhunt)
-               kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round+(game_starttime - time), "", kh_StartRound);
+               kh_Controller_SetThink_NoMsg(autocvar_g_balance_keyhunt_delay_round+(game_starttime - time), kh_StartRound);
  
        if(g_arena)
        if(champion && champion.classname == "player" && player_count > 1)
@@@ -204,35 -202,31 +202,31 @@@ float roundStartTime_prev; // prevent n
  void Arena_Warmup()
  {
        float f;
-       string msg;
      entity e;
  
        if((!g_arena && !g_ca && !g_freezetag) || (g_arena && !arena_roundbased) || (time < game_starttime))
                return;
  
        f = ceil(warmup - time);
-       if(f > 0)
-               champion = world; // this is done because a if(champion) will not execute if champion = world
  
        allowed_to_spawn = 0;
  
        if(inWarmupStage)
                allowed_to_spawn = 1;
-       if(ca_players < required_ca_players)
+       if(g_ca && !ca_teams_ok)
                allowed_to_spawn = 1;
  
-       msg = NEWLINES;
        if(time < warmup && !inWarmupStage)
        {
                if (g_ca)
                        allowed_to_spawn = 1;
                if(champion && g_arena)
-                       msg = strcat("The Champion is ", champion_name, "^7\n");
-                       //centerprint(self, strcat(msg, "The Champion is ", champion.netname, "^7\n"));
+               {
+                       FOR_EACH_PLAYER(e)
+                               centerprint(e, strcat("The Champion is ", champion.netname));
+               }
  
                if(f != roundStartTime_prev) {
-                       msg = strcat(msg, "Round will start in ", ftos(f),"\n");
-                       //centerprint(self, strcat("Round will start in ", ftos(f),"\n"));
                        roundStartTime_prev = f;
                        if(f == 5)
                                Announce("prepareforbattle");
                        else if(f == 1)
                                Announce("1");
  
-             FOR_EACH_PLAYER(e)
-                 centerprint(e, msg);
+                       FOR_EACH_PLAYER(e)
+                               Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "Round will start in %d", 1, f);
                }
  
                if (g_arena) {
                        self.movement = '0 0 0';
                }
        }
        else if(f > -1 && f != roundStartTime_prev)
        {
                roundStartTime_prev = f;
                Announce("begin");
-               centerprint(self, "^1Begin!\n");
+               FOR_EACH_PLAYER(e)
+                       Send_CSQC_Centerprint_Generic(e, CPID_ROUND_STARTING, "^1Begin!", 1, 0);
  
                if(g_ca) {
-                       ca_players = 0;
+                       float start_red_ca_players, start_blue_ca_players;
  
-             FOR_EACH_PLAYER(e)
-                               ca_players += 1;
+                       FOR_EACH_PLAYER(e) {
+                               if (e.team == COLOR_TEAM1)
+                                       start_red_ca_players += 1;
+                               else if (e.team == COLOR_TEAM2)
+                                       start_blue_ca_players += 1;
+                       }
+                       // teams are ok if there's at least 1 player in each team
+                       ca_teams_ok = (start_red_ca_players && start_blue_ca_players);
                }
  
          if(self.classname == "player" && self.health > 0 && self.movetype == MOVETYPE_NONE)
              self.movetype = MOVETYPE_WALK;
        }
+       // clear champion to avoid centerprinting again the champion msg
+       if (champion)
+               champion = world;
  }
  
- void count_spawned_players()
+ void count_players()
  {
-       // TODO fix "*spawned" name, it should rather be "*players" or so
-       // not doing this now to prevent merge hell with Tag
-       // fix after merging with Tag
        // count amount of players in each team
-       totalspawned = redspawned = bluespawned = yellowspawned = pinkspawned = 0;
+       total_players = red_players = blue_players = yellow_players = pink_players = 0;
        FOR_EACH_PLAYER(self) {
                if (self.team == COLOR_TEAM1)
                {
-                       redspawned += 1;
-                       totalspawned += 1;
+                       red_players += 1;
+                       total_players += 1;
                }
                else if (self.team == COLOR_TEAM2)
                {
-                       bluespawned += 1;
-                       totalspawned += 1;
+                       blue_players += 1;
+                       total_players += 1;
                }
                else if (self.team == COLOR_TEAM3)
                {
-                       yellowspawned += 1;
-                       totalspawned += 1;
+                       yellow_players += 1;
+                       total_players += 1;
                }
                else if (self.team == COLOR_TEAM4)
                {
-                       pinkspawned += 1;
-                       totalspawned += 1;
+                       pink_players += 1;
+                       total_players += 1;
                }
        }
  }
@@@ -371,9 -372,10 +372,10 @@@ void count_alive_players(
   *
   * Gets called in StartFrame()
   */
+ float warntime;
  void Spawnqueue_Check()
  {
-       count_spawned_players();
+       count_players();
        if(g_ca || g_freezetag) // we want to perform this before the return block below (CA)...
        {
                count_alive_players();
                return;
  
        if(g_ca) {
-               required_ca_players = max(2, fabs(autocvar_bot_vs_human + 1));
-               if(ca_players < required_ca_players && (redspawned && bluespawned)) {
+               if(!ca_teams_ok && (red_players && blue_players)) {
                        reset_map(TRUE);
                }
-               else if(ca_players < required_ca_players) {
-                       FOR_EACH_PLAYER(self)
-                               centerprint(self, strcat("^1Need at least 1 player in each team to play CA", "^7\n"));
+               else if(!ca_teams_ok) {
+                       if (time > warntime)
+                       {
+                               FOR_EACH_PLAYER(self)
+                                       Send_CSQC_Centerprint_Generic(self, CPID_ROUND_STARTING, "^1Need at least 1 player in each team to play CA", 2, 0);
+                               warntime = time + 1;
+                       }
                        return;
                }
                else if(!next_round) {
-                       if((redspawned && !bluespawned) || (bluespawned && !redspawned)) {
+                       if((red_players && !blue_players) || (blue_players && !red_players)) {
                                next_round = time + 5;
-                               champion = find(world, classname, "player");
-                               if(champion_name)
-                                       strunzone(champion_name);
-                               champion_name = strzone(champion.netname);
                        }
-                       else if((!redspawned && !bluespawned) || time - warmup > autocvar_g_ca_round_timelimit) {
-                               FOR_EACH_CLIENT(self) centerprint(self, strcat("^7Round tied.", "^7\n"));
+                       else if((!red_players && !blue_players) || time - warmup > autocvar_g_ca_round_timelimit) {
+                               FOR_EACH_CLIENT(self) centerprint(self, "^7Round tied");
                                next_round = time + 5;
                        }
  
                        if(redalive && !bluealive)
                        {
                                play2all("ctf/red_capture.wav");
-                               FOR_EACH_CLIENT(self) centerprint(self, "^1 RED ^7team wins the round.\n");
+                               FOR_EACH_CLIENT(self) centerprint(self, "^1RED ^7team wins the round");
                                TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, +1);
                                stopalivecheck = TRUE;
                        }
                        else if(bluealive && !redalive)
                        {
                                play2all("ctf/blue_capture.wav");
-                               FOR_EACH_CLIENT(self) centerprint(self, "^4 BLUE ^7team wins the round.\n");
+                               FOR_EACH_CLIENT(self) centerprint(self, "^4BLUE ^7team wins the round");
                                TeamScore_AddToTeam(COLOR_TEAM2, ST_SCORE, +1);
                                stopalivecheck = TRUE;
                        }
index 13aa4bb2100977d71d7852373778c046d52b5222,e336efc538db42d1d1f737b96713047762903fe5..e5ab948f6027ac4a62d614702a56ed8a384132bd
@@@ -1,3 -1,5 +1,5 @@@
+ float autocvar__notarget;
+ float autocvar__independent_players;
  float autocvar__campaign_index;
  string autocvar__campaign_name;
  float autocvar__sv_init;
@@@ -59,7 -61,6 +61,6 @@@ float autocvar_bot_debug_goalstack
  float autocvar_bot_wander_enable;
  float autocvar_captureleadlimit_override;
  #define autocvar_capturelimit_override cvar("capturelimit_override")
- float autocvar_deathmatch_force_teamplay;
  #define autocvar_developer cvar("developer")
  float autocvar_developer_fteqccbugs;
  float autocvar_ekg;
@@@ -72,7 -73,6 +73,6 @@@ float autocvar_g_antilag_nudge
  float autocvar_g_arena_maxspawned;
  float autocvar_g_arena_point_leadlimit;
  float autocvar_g_arena_point_limit;
- float autocvar_g_arena_powerups;
  float autocvar_g_arena_roundbased;
  float autocvar_g_arena_warmup;
  float autocvar_g_assault;
@@@ -119,6 -119,12 +119,12 @@@ float autocvar_g_balance_rifle_secondar
  float autocvar_g_balance_rifle_reload_ammo;
  float autocvar_g_balance_rifle_reload_time;
  float autocvar_g_balance_cloaked_alpha;
+ float autocvar_g_balance_contents_damagerate;
+ float autocvar_g_balance_contents_drowndelay;
+ float autocvar_g_balance_contents_playerdamage_drowning;
+ float autocvar_g_balance_contents_playerdamage_lava;
+ float autocvar_g_balance_contents_playerdamage_slime;
+ float autocvar_g_balance_contents_projectiledamage;
  float autocvar_g_balance_crylink_primary_ammo;
  float autocvar_g_balance_crylink_primary_animtime;
  float autocvar_g_balance_crylink_primary_bouncedamagefactor;
@@@ -191,6 -197,7 +197,7 @@@ float autocvar_g_balance_electro_combo_
  float autocvar_g_balance_electro_combo_force;
  float autocvar_g_balance_electro_combo_radius;
  float autocvar_g_balance_electro_combo_speed;
+ float autocvar_g_balance_electro_combo_safeammocheck;
  float autocvar_g_balance_electro_lightning;
  float autocvar_g_balance_electro_primary_ammo;
  float autocvar_g_balance_electro_primary_animtime;
@@@ -214,6 -221,7 +221,7 @@@ float autocvar_g_balance_electro_second
  float autocvar_g_balance_electro_secondary_count;
  float autocvar_g_balance_electro_secondary_damage;
  float autocvar_g_balance_electro_secondary_damageforcescale;
+ float autocvar_g_balance_electro_secondary_damagedbycontents;
  float autocvar_g_balance_electro_secondary_edgedamage;
  float autocvar_g_balance_electro_secondary_force;
  float autocvar_g_balance_electro_secondary_health;
@@@ -228,7 -236,6 +236,6 @@@ float autocvar_g_balance_falldamage_dea
  float autocvar_g_balance_falldamage_factor;
  float autocvar_g_balance_falldamage_maxdamage;
  float autocvar_g_balance_falldamage_minspeed;
- float autocvar_g_balance_fireball_primary_ammo;
  float autocvar_g_balance_fireball_primary_animtime;
  float autocvar_g_balance_fireball_primary_bfgdamage;
  float autocvar_g_balance_fireball_primary_bfgforce;
@@@ -247,7 -254,6 +254,6 @@@ float autocvar_g_balance_fireball_prima
  float autocvar_g_balance_fireball_primary_refire;
  float autocvar_g_balance_fireball_primary_refire2;
  float autocvar_g_balance_fireball_primary_speed;
- float autocvar_g_balance_fireball_secondary_ammo;
  float autocvar_g_balance_fireball_secondary_animtime;
  float autocvar_g_balance_fireball_secondary_damage;
  float autocvar_g_balance_fireball_secondary_damageforcescale;
@@@ -260,8 -266,6 +266,6 @@@ float autocvar_g_balance_fireball_secon
  float autocvar_g_balance_fireball_secondary_refire;
  float autocvar_g_balance_fireball_secondary_speed;
  float autocvar_g_balance_fireball_secondary_speed_up;
- float autocvar_g_balance_fireball_reload_ammo;
- float autocvar_g_balance_fireball_reload_time;
  float autocvar_g_balance_firetransfer_damage;
  float autocvar_g_balance_firetransfer_time;
  float autocvar_g_balance_fuel_limit;
@@@ -279,6 -283,7 +283,7 @@@ float autocvar_g_balance_grapplehook_le
  float autocvar_g_balance_grapplehook_speed_fly;
  float autocvar_g_balance_grapplehook_speed_pull;
  float autocvar_g_balance_grapplehook_stretch;
+ float autocvar_g_balance_grapplehook_damagedbycontents;
  float autocvar_g_balance_grenadelauncher_bouncefactor;
  float autocvar_g_balance_grenadelauncher_bouncestop;
  float autocvar_g_balance_grenadelauncher_primary_ammo;
@@@ -317,6 -322,8 +322,8 @@@ float autocvar_g_balance_hagar_primary_
  float autocvar_g_balance_hagar_primary_damage;
  float autocvar_g_balance_hagar_primary_edgedamage;
  float autocvar_g_balance_hagar_primary_force;
+ float autocvar_g_balance_hagar_primary_health;
+ float autocvar_g_balance_hagar_primary_damageforcescale;
  float autocvar_g_balance_hagar_primary_lifetime;
  float autocvar_g_balance_hagar_primary_radius;
  float autocvar_g_balance_hagar_primary_refire;
@@@ -324,18 -331,26 +331,26 @@@ float autocvar_g_balance_hagar_primary_
  float autocvar_g_balance_hagar_secondary;
  float autocvar_g_balance_hagar_secondary_load;
  float autocvar_g_balance_hagar_secondary_load_speed;
+ float autocvar_g_balance_hagar_secondary_load_spread;
+ float autocvar_g_balance_hagar_secondary_load_spread_bias;
  float autocvar_g_balance_hagar_secondary_load_max;
  float autocvar_g_balance_hagar_secondary_load_hold;
  float autocvar_g_balance_hagar_secondary_load_releasedeath;
  float autocvar_g_balance_hagar_secondary_load_abort;
+ float autocvar_g_balance_hagar_secondary_load_linkexplode;
+ float autocvar_g_balance_hagar_secondary_load_animtime;
  float autocvar_g_balance_hagar_secondary_ammo;
  float autocvar_g_balance_hagar_secondary_damage;
  float autocvar_g_balance_hagar_secondary_edgedamage;
  float autocvar_g_balance_hagar_secondary_force;
+ float autocvar_g_balance_hagar_secondary_health;
+ float autocvar_g_balance_hagar_secondary_damageforcescale;
  float autocvar_g_balance_hagar_secondary_lifetime_min;
  float autocvar_g_balance_hagar_secondary_lifetime_rand;
  float autocvar_g_balance_hagar_secondary_radius;
  float autocvar_g_balance_hagar_secondary_refire;
+ float autocvar_g_balance_hagar_secondary_speed;
+ float autocvar_g_balance_hagar_secondary_spread;
  float autocvar_g_balance_hagar_reload_ammo;
  float autocvar_g_balance_hagar_reload_time;
  float autocvar_g_balance_health_limit;
@@@ -392,6 -407,8 +407,8 @@@ float autocvar_g_balance_hook_secondary
  float autocvar_g_balance_hook_secondary_radius;
  float autocvar_g_balance_hook_secondary_refire;
  float autocvar_g_balance_hook_secondary_speed;
+ float autocvar_g_balance_hook_secondary_health;
+ float autocvar_g_balance_hook_secondary_damageforcescale;
  float autocvar_g_balance_keyhunt_damageforcescale;
  float autocvar_g_balance_keyhunt_delay_collect;
  float autocvar_g_balance_keyhunt_delay_return;
@@@ -460,6 -477,8 +477,8 @@@ float autocvar_g_balance_minelayer_relo
  float autocvar_g_balance_minelayer_reload_time;
  float autocvar_g_balance_minstanex_ammo;
  float autocvar_g_balance_minstanex_laser_ammo;
+ float autocvar_g_balance_minstanex_laser_animtime;
+ float autocvar_g_balance_minstanex_laser_refire;
  float autocvar_g_balance_minstanex_animtime;
  float autocvar_g_balance_minstanex_refire;
  float autocvar_g_balance_minstanex_reload_ammo;
@@@ -538,6 -557,11 +557,11 @@@ float autocvar_g_balance_porto_primary_
  float autocvar_g_balance_porto_primary_lifetime;
  float autocvar_g_balance_porto_primary_refire;
  float autocvar_g_balance_porto_primary_speed;
+ float autocvar_g_balance_porto_secondary;
+ float autocvar_g_balance_porto_secondary_animtime;
+ float autocvar_g_balance_porto_secondary_lifetime;
+ float autocvar_g_balance_porto_secondary_refire;
+ float autocvar_g_balance_porto_secondary_speed;
  float autocvar_g_balance_powerup_invincible_takedamage;
  float autocvar_g_balance_powerup_invincible_time;
  float autocvar_g_balance_powerup_strength_damage;
@@@ -545,7 -569,7 +569,7 @@@ float autocvar_g_balance_powerup_streng
  float autocvar_g_balance_powerup_strength_selfdamage;
  float autocvar_g_balance_powerup_strength_selfforce;
  float autocvar_g_balance_powerup_strength_time;
- float autocvar_g_balance_powerup_timer;
+ float autocvar_g_balance_superweapons_time;
  float autocvar_g_balance_rocketlauncher_ammo;
  float autocvar_g_balance_rocketlauncher_animtime;
  float autocvar_g_balance_rocketlauncher_damage;
@@@ -650,9 -674,13 +674,13 @@@ float autocvar_g_balance_shotgun_second
  float autocvar_g_balance_shotgun_secondary_force;
  float autocvar_g_balance_shotgun_secondary_melee_delay;
  float autocvar_g_balance_shotgun_secondary_melee_range;
- float autocvar_g_balance_shotgun_secondary_melee_swing;
+ float autocvar_g_balance_shotgun_secondary_melee_swing_side;
+ float autocvar_g_balance_shotgun_secondary_melee_swing_up;
  float autocvar_g_balance_shotgun_secondary_melee_time;
+ float autocvar_g_balance_shotgun_secondary_melee_traces;
  float autocvar_g_balance_shotgun_secondary_melee_no_doubleslap;
+ float autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage;
+ float autocvar_g_balance_shotgun_secondary_melee_multihit;
  float autocvar_g_balance_shotgun_secondary_refire;
  float autocvar_g_balance_shotgun_reload_ammo;
  float autocvar_g_balance_shotgun_reload_time;
@@@ -676,6 -704,7 +704,7 @@@ float autocvar_g_balance_uzi_burst_spre
  float autocvar_g_balance_uzi_first;
  float autocvar_g_balance_uzi_first_ammo;
  float autocvar_g_balance_uzi_first_damage;
+ float autocvar_g_balance_uzi_first_headshotaddeddamage;
  float autocvar_g_balance_uzi_first_force;
  float autocvar_g_balance_uzi_first_refire;
  float autocvar_g_balance_uzi_first_spread;
@@@ -686,6 -715,7 +715,7 @@@ float autocvar_g_balance_uzi_spread_max
  float autocvar_g_balance_uzi_spread_min;
  float autocvar_g_balance_uzi_sustained_ammo;
  float autocvar_g_balance_uzi_sustained_damage;
+ float autocvar_g_balance_uzi_sustained_headshotaddeddamage;
  float autocvar_g_balance_uzi_sustained_force;
  float autocvar_g_balance_uzi_sustained_refire;
  float autocvar_g_balance_uzi_sustained_spread;
@@@ -712,9 -742,10 +742,10 @@@ float autocvar_g_ca_damage2score_multip
  float autocvar_g_ca_point_leadlimit;
  float autocvar_g_ca_point_limit;
  float autocvar_g_ca_round_timelimit;
+ float autocvar_g_ca_spectate_enemies;
  float autocvar_g_ca_warmup;
  float autocvar_g_campaign;
- float autocvar_g_campaign_forceteam;
+ #define autocvar_g_campaign_forceteam cvar("g_campaign_forceteam")
  float autocvar_g_campaign_skill;
  float autocvar_g_casings;
  float autocvar_g_changeteam_banned;
@@@ -730,17 -761,13 +761,15 @@@ float autocvar_g_chat_flood_spl_team
  float autocvar_g_chat_flood_spl_tell;
  float autocvar_g_chat_nospectators;
  float autocvar_g_chat_teamcolors;
 +float autocvar_g_ctf_allow_drop;
  float autocvar_g_ctf_captimerecord_always;
- float autocvar_g_ctf_capture_leadlimit;
- float autocvar_g_ctf_capture_limit;
  float autocvar_g_ctf_dynamiclights;
  string autocvar_g_ctf_flag_blue_model;
  float autocvar_g_ctf_flag_blue_skin;
  float autocvar_g_ctf_flag_capture_effects;
  float autocvar_g_ctf_flag_glowtrails;
  float autocvar_g_ctf_flag_pickup_effects;
 +float autocvar_g_ctf_flag_pickup_verbosename;
  string autocvar_g_ctf_flag_red_model;
  float autocvar_g_ctf_flag_red_skin;
  float autocvar_g_ctf_flag_returntime;
@@@ -753,7 -780,6 +782,6 @@@ float autocvar_g_ctf_shield_max_ratio
  float autocvar_g_ctf_shield_min_negscore;
  float autocvar_g_cts_finish_kill_delay;
  float autocvar_g_cts_selfdamage;
- float autocvar_g_deathglow;
  float autocvar_g_debug_bot_commands;
  float autocvar_g_domination_default_teams;
  float autocvar_g_domination_disable_frags;
@@@ -852,24 -878,7 +880,7 @@@ float autocvar_g_minstagib_extralives
  float autocvar_g_minstagib_speed_highspeed;
  #define autocvar_g_mirrordamage cvar("g_mirrordamage")
  #define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual")
- float autocvar_g_monster_zombie_attack_run_damage;
- float autocvar_g_monster_zombie_attack_run_delay;
- float autocvar_g_monster_zombie_attack_run_force;
- float autocvar_g_monster_zombie_attack_run_hitrange;
- float autocvar_g_monster_zombie_attack_run_range;
- float autocvar_g_monster_zombie_attack_stand_damage;
- float autocvar_g_monster_zombie_attack_stand_delay;
- float autocvar_g_monster_zombie_attack_stand_force;
- float autocvar_g_monster_zombie_attack_stand_range;
- float autocvar_g_monster_zombie_health;
- float autocvar_g_monster_zombie_idle_timer_max;
- float autocvar_g_monster_zombie_idle_timer_min;
- float autocvar_g_monster_zombie_movespeed;
- float autocvar_g_monster_zombie_respawntime;
- float autocvar_g_monster_zombie_stopspeed;
- float autocvar_g_monster_zombie_targetrange;
- float autocvar_g_monster_zombie_turnspeed;
- float autocvar_g_monsters;
  var float autocvar_g_movement_highspeed = 1;
  float autocvar_g_multijump;
  float autocvar_g_multijump_add;
@@@ -916,10 -925,8 +927,8 @@@ float autocvar_g_player_alpha
  float autocvar_g_player_brightness;
  float autocvar_g_playerclip_collisions;
  string autocvar_g_playerstats_uri;
- float autocvar_g_playerstats_debug;
- float autocvar_g_powerup_shield;
- float autocvar_g_powerup_strength;
- float autocvar_g_powerup_superhealth;
+ float autocvar_g_powerups;
+ float autocvar_g_projectiles_damage;
  float autocvar_g_projectiles_newton_style;
  float autocvar_g_projectiles_newton_style_2_maxfactor;
  float autocvar_g_projectiles_newton_style_2_minfactor;
@@@ -955,6 -962,7 +964,7 @@@ float autocvar_g_shootfromeye
  string autocvar_g_shootfromfixedorigin;
  float autocvar_g_showweaponspawns;
  float autocvar_g_spawn_furthest;
+ float autocvar_g_spawn_useallspawns;
  float autocvar_g_spawnpoints_auto_move_out_of_solid;
  #define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
  float autocvar_g_spawnsound;
@@@ -1018,10 -1026,8 +1028,8 @@@ float autocvar_g_turrets_unit_walker_tu
  float autocvar_g_turrets_unit_walker_turn_swim;
  float autocvar_g_use_ammunition;
  float autocvar_g_waypointeditor;
+ float autocvar_g_waypointeditor_auto;
  float autocvar_g_waypoints_for_items;
- float autocvar_g_waypointsprite_deadlifetime;
- float autocvar_g_waypointsprite_deployed_lifetime;
- float autocvar_g_waypointsprite_limitedrange;
  float autocvar_g_weapon_charge_colormod_blue_full;
  float autocvar_g_weapon_charge_colormod_blue_half;
  float autocvar_g_weapon_charge_colormod_green_full;
@@@ -1033,7 -1039,6 +1041,6 @@@ float autocvar_g_weapon_charge_colormod
  float autocvar_g_weapon_throwable;
  #define autocvar_g_weaponarena cvar_string("g_weaponarena")
  string autocvar_g_xonoticversion;
- float autocvar_gamecfg;
  float autocvar_gameversion;
  float autocvar_gameversion_min;
  float autocvar_gameversion_max;
@@@ -1051,12 -1056,12 +1058,12 @@@ float autocvar_quit_when_empty
  float autocvar_r_showbboxes;
  float autocvar_rescan_pending;
  float autocvar_samelevel;
+ string autocvar_sessionid;
  #define autocvar_skill cvar("skill")
  float autocvar_skill_auto;
  #define autocvar_slowmo cvar("slowmo")
  float autocvar_snd_soundradius;
  float autocvar_spawn_debug;
- float autocvar_spawn_debugview;
  float autocvar_speedmeter;
  float autocvar_sv_accelerate;
  var float autocvar_sv_accuracy_data_share = 1;
@@@ -1074,6 -1079,8 +1081,8 @@@ float autocvar_sv_airstrafeaccel_qw
  float autocvar_sv_airstrafeaccelerate;
  float autocvar_sv_autoscreenshot;
  float autocvar_sv_cheats;
+ float autocvar_sv_clientcommand_antispam_time;
+ float autocvar_sv_clientcommand_antispam_count;
  float autocvar_sv_curl_serverpackages_auto;
  float autocvar_sv_db_saveasdump;
  float autocvar_sv_defaultcharacter;
@@@ -1100,10 -1107,10 +1109,10 @@@ float autocvar_sv_eventlog_files_counte
  string autocvar_sv_eventlog_files_nameprefix;
  string autocvar_sv_eventlog_files_namesuffix;
  float autocvar_sv_eventlog_files_timestamps;
- float autocvar_sv_fragmessage_information_handicap;
- float autocvar_sv_fragmessage_information_ping;
- float autocvar_sv_fragmessage_information_stats;
- float autocvar_sv_fragmessage_information_typefrag;
+ float autocvar_sv_fraginfo;
+ float autocvar_sv_fraginfo_handicap;
+ float autocvar_sv_fraginfo_ping;
+ float autocvar_sv_fraginfo_stats;
  float autocvar_sv_friction;
  float autocvar_sv_friction_on_land;
  float autocvar_sv_gameplayfix_q2airaccelerate;
@@@ -1122,23 -1129,17 +1131,17 @@@ float autocvar_sv_maxairspeed
  float autocvar_sv_maxairstrafespeed;
  float autocvar_sv_maxspeed;
  string autocvar_sv_motd;
- string autocvar_sv_player_crouch_maxs;
- string autocvar_sv_player_crouch_mins;
- string autocvar_sv_player_crouch_viewoffset;
- string autocvar_sv_player_headsize;
- string autocvar_sv_player_maxs;
- string autocvar_sv_player_mins;
- string autocvar_sv_player_viewoffset;
+ float autocvar_sv_player_jumpanim_minfall;
  float autocvar_sv_precacheplayermodels;
  float autocvar_sv_precacheweapons;
  float autocvar_sv_q3acompat_machineshotgunswap;
- float autocvar_sv_qcweaponanimation;
  float autocvar_sv_ready_restart;
  float autocvar_sv_ready_restart_after_countdown;
  float autocvar_sv_ready_restart_repeatable;
  float autocvar_sv_servermodelsonly;
  float autocvar_sv_spectate;
  float autocvar_sv_spectator_speed_multiplier;
+ float autocvar_sv_status_privacy;
  float autocvar_sv_stepheight;
  float autocvar_sv_stopspeed;
  float autocvar_sv_strengthsound_antispam_refire_threshold;
@@@ -1153,13 -1154,16 +1156,16 @@@ float autocvar_sv_vote_call
  float autocvar_sv_vote_change;
  string autocvar_sv_vote_commands;
  float autocvar_sv_vote_majority_factor;
+ float autocvar_sv_vote_majority_factor_of_voted;
  float autocvar_sv_vote_master;
+ float autocvar_sv_vote_master_callable;
  string autocvar_sv_vote_master_commands;
  string autocvar_sv_vote_master_password;
+ float autocvar_sv_vote_master_playerlimit;
+ float autocvar_sv_vote_no_stops_vote;
  float autocvar_sv_vote_nospectators;
  string autocvar_sv_vote_only_commands;
  float autocvar_sv_vote_override_mostrecent;
- float autocvar_sv_vote_simple_majority_factor;
  float autocvar_sv_vote_singlecount;
  float autocvar_sv_vote_stop;
  float autocvar_sv_vote_timeout;
@@@ -1169,6 -1173,9 +1175,9 @@@ float autocvar_sv_warsowbunny_airforwar
  float autocvar_sv_warsowbunny_backtosideratio;
  float autocvar_sv_warsowbunny_topspeed;
  float autocvar_sv_warsowbunny_turnaccel;
+ float autocvar_sv_waypointsprite_deadlifetime;
+ float autocvar_sv_waypointsprite_deployed_lifetime;
+ float autocvar_sv_waypointsprite_limitedrange;
  string autocvar_sv_weaponstats_file;
  float autocvar_sv_gibhealth;
  float autocvar_sys_ticrate;
@@@ -1189,3 -1196,20 +1198,20 @@@ float autocvar_welcome_message_time
  float autocvar_sv_gameplayfix_gravityunaffectedbyticrate;
  float autocvar_g_trueaim_minrange;
  float autocvar_g_debug_defaultsounds;
+ float autocvar_g_loituma;
+ float autocvar_g_grab_range;
+ float autocvar_g_sandbox_info;
+ float autocvar_g_sandbox_readonly;
+ string autocvar_g_sandbox_storage_name;
+ float autocvar_g_sandbox_storage_autosave;
+ float autocvar_g_sandbox_storage_autoload;
+ float autocvar_g_sandbox_editor_flood;
+ float autocvar_g_sandbox_editor_maxobjects;
+ float autocvar_g_sandbox_editor_free;
+ float autocvar_g_sandbox_editor_distance_spawn;
+ float autocvar_g_sandbox_editor_distance_edit;
+ float autocvar_g_sandbox_object_scale_min;
+ float autocvar_g_sandbox_object_scale_max;
+ float autocvar_g_sandbox_object_material_velocity_min;
+ float autocvar_g_sandbox_object_material_velocity_factor;
+ float autocvar_g_max_info_autoscreenshot;
index cf6b6f7372a20f3c1ce59dc6eb486539c5bc5be7,74c611ea18ddf6bdfe8f54dc94c3de1c29c59c57..fe7b732acc7ec51bd3474a88da5ba26020ba2f0e
@@@ -23,7 -23,7 +23,7 @@@ void(float ratingscale, vector org, flo
  .float havocbot_cantfindflag;
  .float havocbot_role_timeout;
  .entity ctf_worldflagnext;
 -.entity basewaypoint;
 +.entity bot_basewaypoint;
  
  entity ctf_worldflaglist;
  vector havocbot_ctf_middlepoint;
@@@ -40,7 -40,7 +40,7 @@@ entity havocbot_ctf_find_flag(entity bo
                f = f.ctf_worldflagnext;
        }
        return world;
- };
+ }
  
  entity havocbot_ctf_find_enemy_flag(entity bot)
  {
@@@ -53,7 -53,7 +53,7 @@@
                f = f.ctf_worldflagnext;
        }
        return world;
- };
+ }
  
  float havocbot_ctf_teamcount(entity bot, vector org, float radius)
  {
        }
  
        return c;
- };
+ }
  
  void havocbot_goalrating_ctf_ourflag(float ratingscale)
  {
-       local entity head;
+       entity head;
        head = ctf_worldflaglist;
        while (head)
        {
        }
        if (head)
                navigation_routerating(head, ratingscale, 10000);
- };
+ }
  
  void havocbot_goalrating_ctf_ourbase(float ratingscale)
  {
-       local entity head;
+       entity head;
        head = ctf_worldflaglist;
        while (head)
        {
        if not(head)
                return;
  
 -      navigation_routerating(head.basewaypoint, ratingscale, 10000);
 +      navigation_routerating(head.bot_basewaypoint, ratingscale, 10000);
- };
+ }
  
  void havocbot_goalrating_ctf_enemyflag(float ratingscale)
  {
-       local entity head;
+       entity head;
        head = ctf_worldflaglist;
        while (head)
        {
        }
        if (head)
                navigation_routerating(head, ratingscale, 10000);
- };
+ }
  
  void havocbot_goalrating_ctf_enemybase(float ratingscale)
  {
                return;
        }
  
-       local entity head;
+       entity head;
  
        head = havocbot_ctf_find_enemy_flag(self);
  
        if not(head)
                return;
  
 -      navigation_routerating(head.basewaypoint, ratingscale, 10000);
 +      navigation_routerating(head.bot_basewaypoint, ratingscale, 10000);
- };
+ }
  
  void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
  {
-       local entity mf;
+       entity mf;
  
        mf = havocbot_ctf_find_flag(self);
  
        if(mf.cnt == FLAG_BASE)
                return;
  
-       navigation_routerating(mf, ratingscale, 10000);
- };
+       if(mf.tag_entity)
+               navigation_routerating(mf.tag_entity, ratingscale, 10000);
+ }
  
  void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius)
  {
-       local entity head;
+       entity head;
        head = ctf_worldflaglist;
        while (head)
        {
  
                head = head.ctf_worldflagnext;
        }
- };
+ }
  
  void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
  {
-       local entity head;
-       local float t;
+       entity head;
+       float t;
        head = findchainfloat(bot_pickup, TRUE);
        while (head)
        {
                }
                head = head.chain;
        }
- };
+ }
  
  void havocbot_role_ctf_setrole(entity bot, float role)
  {
                        bot.havocbot_role = havocbot_role_ctf_carrier;
                        bot.havocbot_role_timeout = 0;
                        bot.havocbot_cantfindflag = time + 10;
+                       bot.bot_strategytime = 0;
                        break;
                case HAVOCBOT_CTF_ROLE_DEFENSE:
                        dprint("defense");
                        bot.havocbot_previous_role = bot.havocbot_role;
                        bot.havocbot_role = havocbot_role_ctf_retriever;
                        bot.havocbot_role_timeout = time + 10;
+                       bot.bot_strategytime = 0;
                        break;
                case HAVOCBOT_CTF_ROLE_ESCORT:
                        dprint("escort");
                        bot.havocbot_previous_role = bot.havocbot_role;
                        bot.havocbot_role = havocbot_role_ctf_escort;
                        bot.havocbot_role_timeout = time + 30;
+                       bot.bot_strategytime = 0;
                        break;
        }
        dprint("\n");
- };
+ }
  
  void havocbot_role_ctf_carrier()
  {
                        return;
                }
        }
- };
+ }
  
  void havocbot_role_ctf_escort()
  {
-       local entity mf, ef;
+       entity mf, ef;
  
        if(self.deadflag != DEAD_NO)
        {
                havocbot_goalrating_items(10000, self.origin, 10000);
                navigation_goalrating_end();
        }
- };
+ }
  
  void havocbot_role_ctf_offense()
  {
-       local entity mf, ef;
-       local vector pos;
+       entity mf, ef;
+       vector pos;
  
        if(self.deadflag != DEAD_NO)
        {
                havocbot_goalrating_items(1000, self.origin, 10000);
                navigation_goalrating_end();
        }
- };
+ }
  
  // Retriever (temporary role):
  void havocbot_role_ctf_retriever()
  {
-       local entity mf;
+       entity mf;
  
        if(self.deadflag != DEAD_NO)
        {
  
        if (self.bot_strategytime < time)
        {
-               local float radius;
+               float radius;
                radius = 10000;
  
                self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                havocbot_goalrating_items(500, self.origin, radius);
                navigation_goalrating_end();
        }
- };
+ }
  
  void havocbot_role_ctf_middle()
  {
-       local entity mf;
+       entity mf;
  
        if(self.deadflag != DEAD_NO)
        {
  
        if (self.bot_strategytime < time)
        {
-               local vector org;
+               vector org;
  
                org = havocbot_ctf_middlepoint;
                org_z = self.origin_z;
                havocbot_goalrating_ctf_enemybase(2500);
                navigation_goalrating_end();
        }
- };
+ }
  
  void havocbot_role_ctf_defense()
  {
-       local entity mf;
+       entity mf;
  
        if(self.deadflag != DEAD_NO)
        {
        }
        if (self.bot_strategytime < time)
        {
-               local float radius;
-               local vector org;
+               float radius;
+               vector org;
  
                org = mf.dropped_origin;
                radius = havocbot_ctf_middlepoint_radius;
                navigation_goalrating_start();
  
                // if enemies are closer to our base, go there
-               local entity head, closestplayer;
-               local float distance, bestdistance;
+               entity head, closestplayer;
+               float distance, bestdistance;
                distance = 10000;
                FOR_EACH_PLAYER(head)
                {
                havocbot_goalrating_items(5000, self.origin, 10000);
                navigation_goalrating_end();
        }
- };
+ }
  
  void havocbot_calculate_middlepoint()
  {
        }
        havocbot_ctf_middlepoint = p1 + ((p2-p1) * 0.5);
        havocbot_ctf_middlepoint_radius  = vlen(p2-p1) * 0.5;
- };
+ }
  
  void havocbot_ctf_reset_role(entity bot)
  {
-       local float cdefense, cmiddle, coffense;
-       local entity mf, ef, head;
-       local float c;
+       float cdefense, cmiddle, coffense;
+       entity mf, ef, head;
+       float c;
  
        if(bot.deadflag != DEAD_NO)
                return;
                havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
        else
                havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
- };
+ }
  
  void havocbot_chooserole_ctf()
  {
        havocbot_ctf_reset_role(self);
- };
+ }
diff --combined qcsrc/server/cheats.qc
index 42f0395bc5ebd08d3863bfef4ecd5fd3bbb69ede,5ba860940a014344a3338f27551448ddbce0ad31..d52e33b0c3a62ffae0178a15a1abaf1f5cb8d534
@@@ -89,10 -89,37 +89,37 @@@ float CheatsAllowed(float i, float argc
        ADD_CHEATS(self,cheating); \
        return attempting
  #define IS_CHEAT(i,argc,fr) \
-       ++attempting; \
-       if(!CheatsAllowed(i,argc,fr)) \
+       if((++attempting, !CheatsAllowed(i,argc,fr))) \
                break
  
+ float num_autoscreenshot;
+ void info_autoscreenshot_findtarget()
+ {
+       entity e;
+       e = find(world, targetname, self.target);
+       if(!e)
+       {
+               objerror("Missing target. FAIL!");
+               return;
+       }
+       vector a = vectoangles(e.origin - self.origin);
+       a_x = -a_x; // don't ask
+       self.angles_x = a_x;
+       self.angles_y = a_y;
+       // we leave Rick Roll alone
+ }
+ void spawnfunc_info_autoscreenshot()
+ {
+       if(++num_autoscreenshot > autocvar_g_max_info_autoscreenshot)
+       {
+               objerror("Too many info_autoscreenshot entitites. FAIL!");
+               return;
+       }
+       if(self.target != "")
+               InitializeEntity(self, info_autoscreenshot_findtarget, INITPRIO_FINDTARGET);
+       // this one just has to exist
+ }
  float CheatImpulse(float i)
  {
        BEGIN_CHEAT_FUNCTION();
                                        if(self.flagcarried)
                                        {
                                                bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n");
 -                                              ReturnFlag(self.flagcarried);
 +                                              ctf_RespawnFlag(self); // FIXCTF
                                        }
                                }
                                if(g_ctf)
                        break;
                case CHIMPULSE_TELEPORT:
                        IS_CHEAT(i, 0, 0);
+                       if(self.movetype == MOVETYPE_NOCLIP)
+                       {
+                               e = find(world, classname, "info_autoscreenshot");
+                               if(e)
+                               {
+                                       sprint(self, "Emergency teleport used info_autoscreenshot location\n");
+                                       setorigin(self, e.origin);
+                                       self.angles = e.angles;
+                                       remove(e);
+                                       // should we? self.angles_x = -self.angles_x;
+                                       self.fixangle = TRUE;
+                                       self.velocity = '0 0 0';
+                                       DID_CHEAT();
+                                       break;
+                               }
+                       }
                        if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((gamestart_sv_cheats >= 2) ? 100000 : 100), 1024, 256))
                        {
+                               sprint(self, "Emergency teleport used random location\n");
                                self.angles_x = -self.angles_x;
                                self.fixangle = TRUE;
                                self.velocity = '0 0 0';
                        IS_CHEAT(i, 0, 0);
                        FOR_EACH_PLAYER(e)
                        {
-                               get_model_parameters(e.playermodel, e.skinindex);
+                               get_model_parameters(e.playermodel, e.skin);
                                if(get_model_parameters_sex == "Female")
                                {
                                        makevectors(e.angles);
@@@ -604,14 -648,6 +648,6 @@@ float CheatCommand(float argc
                                remove(e);
                        DID_CHEAT();
                        break;
-               case "warp":
-                       IS_CHEAT(0, argc, 0);
-                       if(argc == 2) if(autocvar_g_campaign)
-                       {
-                               CampaignLevelWarp(stof(argv(1)));
-                               DID_CHEAT();
-                       }
-                       break;
                case "god":
                        IS_CHEAT(0, argc, 0);
                        BITXOR_ASSIGN(self.flags, FL_GODMODE);
        END_CHEAT_FUNCTION();
  }
  
void crosshair_trace_plusvisibletriggers(entity pl);
float Drag(entity e, float grab, float ischeat);
  void Drag_Begin(entity dragger, entity draggee, vector touchpoint);
  void Drag_Finish(entity dragger);
  float Drag_IsDraggable(entity draggee);
@@@ -690,58 -726,53 +726,53 @@@ float CheatFrame(
  {
        BEGIN_CHEAT_FUNCTION();
  
-       if(Drag_IsDragging(self))
+       // Dragging can be used as either a cheat, or a function for some objects. If sv_cheats is active,
+       // the cheat dragging is used (unlimited pickup range and any entity can be carried). If sv_cheats
+       // is disabled, normal dragging is used (limited pickup range and only dragable objects can be carried),
+       // grabbing itself no longer being accounted as cheating.
+       switch(0)
        {
-               if(self.BUTTON_DRAG)
-               {
-                       if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18)
-                       {
-                               Drag_MoveForward(self);
-                               self.impulse = 0;
-                       }
-                       else if(self.impulse == 12 || self.impulse == 16 || self.impulse == 19)
-                       {
-                               Drag_MoveBackward(self);
-                               self.impulse = 0;
-                       }
-                       else if(self.impulse >= 1 && self.impulse <= 9)
+               default:
+                       if(self.maycheat || (gamestart_sv_cheats && autocvar_sv_cheats))
                        {
-                               Drag_SetSpeed(self, self.impulse - 1);
+                               // use cheat dragging if cheats are enabled
+                               crosshair_trace_plusvisibletriggers(self);
+                               Drag(trace_ent, TRUE, TRUE);
                        }
-                       else if(self.impulse == 14)
+                       else
                        {
-                               Drag_SetSpeed(self, 9);
-                       }
+                               // drag is TRUE if the object can be picked up. While an object is being carried, the Drag() function
+                               // must execute for it either way, otherwise it would cause bugs if it went out of the player's trace.
+                               // This also makes sure that an object can only pe picked up if in range, but does not get dropped if
+                               // it goes out of range while slinging it around.
  
-                       if(frametime)
-                               Drag_Update(self);
-               }
-               else
-               {
-                       Drag_Finish(self);
-               }
-       }
-       else
-       {
-               if(Drag_CanDrag(self))
-                       if(self.BUTTON_DRAG)
-                       {
+                               float drag;
                                crosshair_trace_plusvisibletriggers(self);
-                               if(trace_ent)
-                                       if(Drag_IsDraggable(trace_ent))
-                                               switch(0)
-                                               {
-                                                       default:
-                                                               IS_CHEAT(0, 0, CHRAME_DRAG);
-                                                               if(trace_ent.draggedby)
-                                                                       Drag_Finish(trace_ent.draggedby);
-                                                               if(trace_ent.tag_entity)
-                                                                       detach_sameorigin(trace_ent);
-                                                               Drag_Begin(self, trace_ent, trace_endpos);
-                                                               DID_CHEAT();
-                                                               break;
-                                               }
+                               if(vlen(self.origin - trace_ent.origin) <= autocvar_g_grab_range)
+                               {
+                                       switch(trace_ent.grab)
+                                       {
+                                               case 0: // can't grab
+                                                       break;
+                                               case 1: // owner can grab
+                                                       if(trace_ent.owner == self || trace_ent.realowner == self)
+                                                               drag = TRUE;
+                                                       break;
+                                               case 2: // owner and team mates can grab
+                                                       if(!IsDifferentTeam(trace_ent.owner, self) || !IsDifferentTeam(trace_ent.realowner, self) || trace_ent.team == self.team)
+                                                               drag = TRUE;
+                                                       break;
+                                               case 3: // anyone can grab
+                                                       drag = TRUE;
+                                                       break;
+                                               default:
+                                                       break;
+                                       }
+                               }
+                               Drag(trace_ent, drag, FALSE); // execute dragging
                        }
+                       break;
        }
  
        END_CHEAT_FUNCTION();
  
  // ENTITY DRAGGING
  
void crosshair_trace_plusvisibletriggers(entity pl)
float Drag(entity e, float pick, float ischeat)
  {
-       entity first;
-       entity e;
-       first = findchainfloat(solid, SOLID_TRIGGER);
+       BEGIN_CHEAT_FUNCTION();
  
-       for (e = first; e; e = e.chain)
-               if (e.model != "")
-                       e.solid = SOLID_BSP;
+       // returns TRUE when an entity has been picked up
+       // If pick is TRUE, the object can also be picked up if it's not being held already
+       // If pick is FALSE, only keep dragging the object if it's already being held
  
-       crosshair_trace(pl);
+       switch(0)
+       {
+               default:
+                       if(Drag_IsDragging(self))
+                       {
+                               if(self.BUTTON_DRAG)
+                               {
+                                       if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18)
+                                       {
+                                               Drag_MoveForward(self);
+                                               self.impulse = 0;
+                                       }
+                                       else if(self.impulse == 12 || self.impulse == 16 || self.impulse == 19)
+                                       {
+                                               Drag_MoveBackward(self);
+                                               self.impulse = 0;
+                                       }
+                                       else if(self.impulse >= 1 && self.impulse <= 9)
+                                       {
+                                               Drag_SetSpeed(self, self.impulse - 1);
+                                       }
+                                       else if(self.impulse == 14)
+                                       {
+                                               Drag_SetSpeed(self, 9);
+                                       }
  
-       for (e = first; e; e = e.chain)
-               e.solid = SOLID_TRIGGER;
+                                       if(frametime)
+                                               Drag_Update(self);
+                               }
+                               else
+                               {
+                                       Drag_Finish(self);
+                               }
+                       }
+                       else
+                       {
+                               if(Drag_CanDrag(self))
+                                       if(self.BUTTON_DRAG && pick)
+                                       {
+                                               if(e)
+                                                       if(Drag_IsDraggable(e))
+                                                       {
+                                                               if(ischeat)
+                                                                       IS_CHEAT(0, 0, CHRAME_DRAG);
+                                                               if(e.draggedby)
+                                                                       Drag_Finish(e.draggedby);
+                                                               if(e.tag_entity)
+                                                                       detach_sameorigin(e);
+                                                               Drag_Begin(self, e, trace_endpos);
+                                                               if(ischeat)
+                                                                       DID_CHEAT();
+                                                               return TRUE;
+                                                       }
+                                       }
+                       }
+                       break;
+       }
+       return FALSE;
  }
  
  // on dragger:
@@@ -837,6 -920,8 +920,8 @@@ void Drag_Finish(entity dragger
  float Drag_IsDraggable(entity draggee)
  {
        // TODO add more checks for bad stuff here
+       if(draggee == world)
+               return FALSE;
        if(draggee.classname == "func_bobbing")
                return FALSE;
        if(draggee.classname == "door") // FIXME find out why these must be excluded, or work around the problem (trying to drag these causes like 4 fps)
@@@ -945,11 -1030,6 +1030,6 @@@ void Drag_MoveDrag(entity from, entity 
        }
  }
  
  void DragBox_Think()
  {
        if(self.aiment && self.enemy)
index 81f3b094405903909979f77bea506a78f9ad1269,ba4249744eb90862ab299b96b151ebdad0cd41da..655eed0fa7151f3eff0cd8612e8a52b39cdf1e64
@@@ -118,12 -118,12 +118,12 @@@ void spawnpoint_use(
                self.team = activator.team;
                some_spawn_has_been_used = 1;
        }
- };
+ }
  
  // Returns:
  //   _x: prio (-1 if unusable)
  //   _y: weight
- vector Spawn_Score(entity spot, entity playerlist, float teamcheck, float anypoint)
+ vector Spawn_Score(entity spot, float mindist, float teamcheck)
  {
        float shortest, thisdist;
        float prio;
                        return '-1 0 0';
        }
  
+       shortest = vlen(world.maxs - world.mins);
+       FOR_EACH_PLAYER(player) if (player != self)
+       {
+               thisdist = vlen(player.origin - spot.origin);
+               if (thisdist < shortest)
+                       shortest = thisdist;
+       }
+       if(shortest > mindist)
+               prio += SPAWN_PRIO_GOOD_DISTANCE;
+       spawn_score = prio * '1 0 0' + shortest * '0 1 0';
+       spawn_spot = spot;
        // filter out spots for assault
        if(spot.target != "") {
-               local entity ent;
-               float good, found;
-               ent = find(world, targetname, spot.target);
+               entity ent;
+               float found;
  
-               while(ent) {
-                       if(ent.classname == "target_objective")
-                       {
-                               found = 1;
-                               if(ent.health < 0 || ent.health >= ASSAULT_VALUE_INACTIVE)
-                                       return '-1 0 0';
-                               good = 1;
-                       }
-                       else if(ent.classname == "trigger_race_checkpoint")
+               found = 0;
+               for(ent = world; (ent = find(ent, targetname, spot.target)); )
+               {
+                       ++found;
+                       if(ent.spawn_evalfunc)
                        {
-                               found = 1;
-                               if(!anypoint) // spectators may spawn everywhere
-                               {
-                                       if(g_race_qualifying)
-                                       {
-                                               // spawn at first
-                                               if(ent.race_checkpoint != 0)
-                                                       return '-1 0 0';
-                                               if(spot.race_place != race_lowest_place_spawn)
-                                                       return '-1 0 0';
-                                       }
-                                       else
-                                       {
-                                               if(ent.race_checkpoint != self.race_respawn_checkpoint)
-                                                       return '-1 0 0';
-                                               // try reusing the previous spawn
-                                               if(ent == self.race_respawn_spotref || spot == self.race_respawn_spotref)
-                                                       prio += 1;
-                                               if(ent.race_checkpoint == 0)
-                                               {
-                                                       float pl;
-                                                       pl = self.race_place;
-                                                       if(pl > race_highest_place_spawn)
-                                                               pl = 0;
-                                                       if(pl == 0 && !self.race_started)
-                                                               pl = race_highest_place_spawn; // use last place if he has not even touched finish yet
-                                                       if(spot.race_place != pl)
-                                                               return '-1 0 0';
-                                               }
-                                       }
-                               }
-                               good = 1;
+                               entity oldself = self;
+                               self = ent;
+                               spawn_score = ent.spawn_evalfunc(oldself, spot, spawn_score);
+                               self = oldself;
+                               if(spawn_score_x < 0)
+                                       return spawn_score;
                        }
-                       ent = find(ent, targetname, spot.target);
                }
  
-               if(found && !good)
+               if(!found)
+               {
+                       dprint("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n");
                        return '-1 0 0';
+               }
        }
  
-       player = playerlist;
-       shortest = vlen(world.maxs - world.mins);
-       for(player = playerlist; player; player = player.chain)
-               if (player != self)
-               {
-                       thisdist = vlen(player.origin - spot.origin);
-                       if (thisdist < shortest)
-                               shortest = thisdist;
-               }
-       return prio * '1 0 0' + shortest * '0 1 0';
+       MUTATOR_CALLHOOK(Spawn_Score);
+       return spawn_score;
  }
  
- float spawn_allbad;
- float spawn_allgood;
- entity Spawn_FilterOutBadSpots(entity firstspot, entity playerlist, float mindist, float teamcheck, float anypoint)
+ void Spawn_ScoreAll(entity firstspot, float mindist, float teamcheck)
  {
-       local entity spot, spotlist, spotlistend;
-       spawn_allgood = TRUE;
-       spawn_allbad = TRUE;
+       entity spot;
+       for(spot = firstspot; spot; spot = spot.chain)
+               spot.spawnpoint_score = Spawn_Score(spot, mindist, teamcheck);
+ }
+ entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck)
+ {
+       entity spot, spotlist, spotlistend;
  
        spotlist = world;
        spotlistend = world;
  
+       Spawn_ScoreAll(firstspot, mindist, teamcheck);
        for(spot = firstspot; spot; spot = spot.chain)
        {
-               spot.spawnpoint_score = Spawn_Score(spot, playerlist, teamcheck, anypoint);
-               if(autocvar_spawn_debugview)
-               {
-                       setmodel(spot, "models/runematch/rune.mdl");
-                       if(spot.spawnpoint_score_y < mindist)
-                       {
-                               spot.colormod = '1 0 0';
-                               spot.scale = 1;
-                       }
-                       else
-                       {
-                               spot.colormod = '0 1 0';
-                               spot.scale = spot.spawnpoint_score_y / mindist;
-                       }
-               }
                if(spot.spawnpoint_score_x >= 0) // spawning allowed here
                {
-                       if(spot.spawnpoint_score_y < mindist)
-                       {
-                               // too short distance
-                               spawn_allgood = FALSE;
-                       }
-                       else
-                       {
-                               // perfect
-                               spawn_allbad = FALSE;
-                               if(spotlistend)
-                                       spotlistend.chain = spot;
-                               spotlistend = spot;
-                               if(!spotlist)
-                                       spotlist = spot;
-                               /*
-                               if(teamcheck >= 0)
-                               if(spot.team != teamcheck)
-                                       error("invalid spawn added");
-                               print("added ", etos(spot), "\n");
-                               */
-                       }
+                       if(spotlistend)
+                               spotlistend.chain = spot;
+                       spotlistend = spot;
+                       if(!spotlist)
+                               spotlist = spot;
                }
        }
        if(spotlistend)
                spotlistend.chain = world;
  
-       /*
-               entity e;
-               if(teamcheck >= 0)
-                       for(e = spotlist; e; e = e.chain)
-                       {
-                               print("seen ", etos(e), "\n");
-                               if(e.team != teamcheck)
-                                       error("invalid spawn found");
-                       }
-       */
        return spotlist;
  }
  
@@@ -299,7 -232,7 +232,7 @@@ entity Spawn_WeightedPoint(entity first
  {
        // weight of a point: bound(lower, mindisttoplayer, upper)^exponent
        // multiplied by spot.cnt (useful if you distribute many spawnpoints in a small area)
-       local entity spot;
+       entity spot;
  
        RandomSelection_Init();
        for(spot = firstspot; spot; spot = spot.chain)
@@@ -317,15 -250,14 +250,14 @@@ Finds a point to respaw
  */
  entity SelectSpawnPoint (float anypoint)
  {
-       local float teamcheck;
-       local entity firstspot_new;
-       local entity spot, firstspot, playerlist;
+       float teamcheck;
+       entity spot, firstspot;
  
        spot = find (world, classname, "testplayerstart");
        if (spot)
                return spot;
  
-       if(anypoint)
+       if(anypoint || autocvar_g_spawn_useallspawns)
                teamcheck = -1;
        else if(have_team_spawns > 0)
        {
                // if we get here, we either require team spawns but have none, or we require non-team spawns and have none; use any spawn then
  
  
-       // get the list of players
-       playerlist = findchain(classname, "player");
        // get the entire list of spots
        firstspot = findchain(classname, "info_player_deathmatch");
        // filter out the bad ones
        }
        else
        {
-               firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, 100, teamcheck, anypoint);
-               if(!firstspot_new)
-                       firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, -1, teamcheck, anypoint);
-               firstspot = firstspot_new;
+               float mindist;
+               if (arena_roundbased && !g_ca)
+                       mindist = 800;
+               else
+                       mindist = 100;
+               firstspot = Spawn_FilterOutBadSpots(firstspot, mindist, teamcheck);
  
                // there is 50/50 chance of choosing a random spot or the furthest spot
                // (this means that roughly every other spawn will be furthest, so you
                // usually won't get fragged at spawn twice in a row)
-               if (arena_roundbased && !g_ca)
-               {
-                       firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, 800, teamcheck, anypoint);
-                       if(firstspot_new)
-                               firstspot = firstspot_new;
-                       spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
-               }
-               else if (random() > autocvar_g_spawn_furthest)
+               if (random() > autocvar_g_spawn_furthest)
                        spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
                else
                        spot = Spawn_WeightedPoint(firstspot, 1, 5000, 5); // chooses a far far away spawnpoint
        }
  
-       if(autocvar_spawn_debugview)
-       {
-               print("spot mindistance: ", vtos(spot.spawnpoint_score), "\n");
-               entity e;
-               if(teamcheck >= 0)
-                       for(e = firstspot; e; e = e.chain)
-                               if(e.team != teamcheck)
-                                       error("invalid spawn found");
-       }
        if (!spot)
        {
                if(autocvar_spawn_debug)
-                       GotoNextMap();
+                       GotoNextMap(0);
                else
                {
                        if(some_spawn_has_been_used)
@@@ -427,160 -341,36 +341,36 @@@ string CheckPlayerModel(string plyermod
                // to change a cvar default, we'll have a small leak here.
                FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
        }
-       if(strlen(plyermodel) < 4)
-               return FallbackPlayerModel;
+       // only in right path
        if( substring(plyermodel,0,14) != "models/player/")
                return FallbackPlayerModel;
-       else if(autocvar_sv_servermodelsonly)
+       // only good file extensions
+       if(substring(plyermodel,-4,4) != ".zym")
+       if(substring(plyermodel,-4,4) != ".dpm")
+       if(substring(plyermodel,-4,4) != ".iqm")
+       if(substring(plyermodel,-4,4) != ".md3")
+       if(substring(plyermodel,-4,4) != ".psk")
+               return FallbackPlayerModel;
+       // forbid the LOD models
+       if(substring(plyermodel, -9,5) == "_lod1")
+               return FallbackPlayerModel;
+       if(substring(plyermodel, -9,5) == "_lod2")
+               return FallbackPlayerModel;
+       if(plyermodel != strtolower(plyermodel))
+               return FallbackPlayerModel;
+       // also, restrict to server models
+       if(autocvar_sv_servermodelsonly)
        {
-               if(substring(plyermodel,-4,4) != ".zym")
-               if(substring(plyermodel,-4,4) != ".dpm")
-               if(substring(plyermodel,-4,4) != ".iqm")
-               if(substring(plyermodel,-4,4) != ".md3")
-               if(substring(plyermodel,-4,4) != ".psk")
-                       return FallbackPlayerModel;
-               // forbid the LOD models
-               if(substring(plyermodel, -9,5) == "_lod1")
-                       return FallbackPlayerModel;
-               if(substring(plyermodel, -9,5) == "_lod2")
-                       return FallbackPlayerModel;
-               if(plyermodel != strtolower(plyermodel))
-                       return FallbackPlayerModel;
                if(!fexists(plyermodel))
                        return FallbackPlayerModel;
        }
        return plyermodel;
  }
  
- /*
- =============
- Client_customizeentityforclient
- LOD reduction
- =============
- */
- void Client_uncustomizeentityforclient()
+ void setplayermodel(entity e, string modelname)
  {
-       if(self.modelindex == 0) // no need to uncustomize then
-               return;
-       self.modelindex = self.modelindex_lod0;
-       self.skin = self.skinindex;
- }
- float Client_customizeentityforclient()
- {
-       entity modelsource;
-       if(self.modelindex == 0)
-               return TRUE;
-       // forcemodel stuff
- #ifdef PROFILING
-       float t0;
-       t0 = gettime(GETTIME_HIRES); // reference
- #endif
-       modelsource = self;
- #ifdef ALLOW_FORCEMODELS
-       if(other.cvar_cl_forceplayermodelsfromxonotic)
-               if not(self.modelindex_lod0_from_xonotic)
-                       modelsource = other;
-       if(other.cvar_cl_forceplayermodels && sv_clforceplayermodels)
-               modelsource = other;
- #endif
-       self.skin = modelsource.skinindex;
- #if 0
-       if(modelsource == self)
-               self.skin = modelsource.skinindex;
-       else
-               self.skin = mod(modelsource.skinindex, 3); // forbid the fbskins as forced skins
- #endif
-       // self: me
-       // other: the player viewing me
-       float distance;
-       float f;
-       if(other.cvar_cl_playerdetailreduction <= 0)
-       {
-               if(other.cvar_cl_playerdetailreduction <= -2)
-                       self.modelindex = modelsource.modelindex_lod2;
-               else if(other.cvar_cl_playerdetailreduction <= -1)
-                       self.modelindex = modelsource.modelindex_lod1;
-               else
-                       self.modelindex = modelsource.modelindex_lod0;
-       }
-       else
-       {
-               distance = vlen(self.origin - other.origin);
-               f = (distance + 100.0) * other.cvar_cl_playerdetailreduction;
-               if(f > sv_loddistance2)
-                       self.modelindex = modelsource.modelindex_lod2;
-               else if(f > sv_loddistance1)
-                       self.modelindex = modelsource.modelindex_lod1;
-               else
-                       self.modelindex = modelsource.modelindex_lod0;
-       }
- #ifdef PROFILING
-       float t1;
-       t1 = gettime(GETTIME_HIRES); // reference
-       client_cefc_accumulator += (t1 - t0);
- #endif
-       return TRUE;
- }
- void setmodel_lod(entity e, string modelname)
- {
-       string s;
-       if(sv_loddistance1)
-       {
-               // FIXME: this only supports 3-letter extensions
-               s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod1", substring(modelname, -4, 4));
-               if(fexists(s))
-               {
-                       setmodel(e, s); // players have high precision
-                       self.modelindex_lod1 = self.modelindex;
-               }
-               else
-                       self.modelindex_lod1 = -1;
-               s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod2", substring(modelname, -4, 4));
-               if(fexists(s))
-               {
-                       setmodel(e, s); // players have high precision
-                       self.modelindex_lod2 = self.modelindex;
-               }
-               else
-                       self.modelindex_lod2 = -1;
-               precache_model(modelname);
-               setmodel(e, modelname); // players have high precision
-               self.modelindex_lod0 = self.modelindex;
-               if(self.modelindex_lod1 < 0)
-                       self.modelindex_lod1 = self.modelindex;
-               if(self.modelindex_lod2 < 0)
-                       self.modelindex_lod2 = self.modelindex;
-       }
-       else
-       {
-               precache_model(modelname);
-               setmodel(e, modelname); // players have high precision
-               self.modelindex_lod0 = self.modelindex;
-                       // save it for possible player model forcing
-       }
-       s = whichpack(self.model);
-       self.modelindex_lod0_from_xonotic = ((s == "") || (substring(s, 0, 4) == "data"));
+       precache_model(modelname);
+       setmodel(e, modelname);
        player_setupanimsformodel();
        UpdatePlayerSounds();
  }
@@@ -614,6 -404,9 +404,9 @@@ void PutObserverInServer (void
        DropAllRunes(self);
        MUTATOR_CALLHOOK(MakePlayerObserver);
  
+       if (g_minstagib)
+               minstagib_stop_countdown();
        Portal_ClearAll(self);
  
        if(self.alivetime)
            vehicles_exit(VHEF_RELESE);
  
        if(self.flagcarried)
 -              DropFlag(self.flagcarried, world, world);
 +              ctf_Handle_Drop(self); // FIXCTF
  
        if(self.ballcarried && g_nexball)
                DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
        accuracy_resend(self);
  
        self.spectatortime = time;
+       
        self.classname = "observer";
        self.iscreature = FALSE;
+       self.damagedbycontents = FALSE;
        self.health = -666;
        self.takedamage = DAMAGE_NO;
        self.solid = SOLID_NOT;
-       self.movetype = MOVETYPE_NOCLIP;
+       self.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink
        self.flags = FL_CLIENT | FL_NOTARGET;
        self.armorvalue = 666;
        self.effects = 0;
        self.pauseregen_finished = 0;
        self.damageforcescale = 0;
        self.death_time = 0;
-       self.dead_frame = 0;
+       self.respawn_time = 0;
        self.alpha = 0;
        self.scale = 0;
        self.fade_time = 0;
        self.pain_finished = 0;
        self.strength_finished = 0;
        self.invincible_finished = 0;
+       self.superweapons_finished = 0;
        self.pushltime = 0;
        self.think = SUB_Null;
        self.nextthink = 0;
        self.fixangle = TRUE;
        self.crouch = FALSE;
  
-       self.view_ofs = PL_VIEW_OFS;
-       setorigin (self, spot.origin);
-       setsize (self, '0 0 0', '0 0 0');
+       setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
        self.prevorigin = self.origin;
        self.items = 0;
        self.weapons = 0;
        self.model = "";
        FixPlayermodel();
-       self.model = "";
-       self.modelindex = 0;
+       setmodel(self, "null");
+       self.drawonlytoclient = self;
+       setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
+       self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
        self.weapon = 0;
+       self.weaponname = "";
+       self.switchingweapon = 0;
        self.weaponmodel = "";
        self.weaponentity = world;
        self.exteriorweaponentity = world;
        self.oldvelocity = self.velocity;
        self.fire_endtime = -1;
  
-       if(sv_loddistance1)
-               SetCustomizer(self, Client_customizeentityforclient, Client_uncustomizeentityforclient);
        if(g_arena)
        {
                if(self.version_mismatch)
                else
                        self.frags = FRAGS_LMS_LOSER;
        }
+       else if(g_ca)
+       {
+               if(self.caplayer)
+                       self.frags = FRAGS_LMS_LOSER;
+               else
+                       self.frags = FRAGS_SPECTATOR;
+       }
        else
                self.frags = FRAGS_SPECTATOR;
  }
  
+ .float model_randomizer;
  void FixPlayermodel()
  {
-       local string defaultmodel;
-       local float defaultskin, chmdl, oldskin;
-       local vector m1, m2;
+       string defaultmodel;
+       float defaultskin, chmdl, oldskin, n, i;
+       vector m1, m2;
  
        defaultmodel = "";
  
-       if(autocvar_sv_defaultcharacter == 1) {
+       if(autocvar_sv_defaultcharacter == 1)
+       {
                defaultskin = 0;
  
                if(teamplay)
                        defaultmodel = autocvar_sv_defaultplayermodel;
                        defaultskin = autocvar_sv_defaultplayerskin;
                }
-       }
  
-       if(self.modelindex == 0 && self.deadflag == DEAD_NO)
-       {
-               if(self.model != "")
-                       bprint("\{1}^1Player ", self.netname, "^1 has a zero modelindex, trying to fix...\n");
-               self.model = ""; // force the != checks to return true
+               n = tokenize_console(defaultmodel);
+               if(n > 0)
+                       defaultmodel = argv(floor(n * self.model_randomizer));
+               i = strstrofs(defaultmodel, ":", 0);
+               if(i >= 0)
+               {
+                       defaultskin = stof(substring(defaultmodel, i+1, -1));
+                       defaultmodel = substring(defaultmodel, 0, i);
+               }
        }
  
        if(defaultmodel != "")
                {
                        m1 = self.mins;
                        m2 = self.maxs;
-                       setmodel_lod (self, defaultmodel);
+                       setplayermodel (self, defaultmodel);
                        setsize (self, m1, m2);
                        chmdl = TRUE;
                }
  
-               oldskin = self.skinindex;
-               self.skinindex = defaultskin;
+               oldskin = self.skin;
+               self.skin = defaultskin;
        } else {
                if (self.playermodel != self.model || self.playermodel == "")
                {
                        self.playermodel = CheckPlayerModel(self.playermodel); // this is never "", so no endless loop
                        m1 = self.mins;
                        m2 = self.maxs;
-                       setmodel_lod (self, self.playermodel);
+                       setplayermodel (self, self.playermodel);
                        setsize (self, m1, m2);
                        chmdl = TRUE;
                }
  
-               oldskin = self.skinindex;
-               self.skinindex = stof(self.playerskin);
+               oldskin = self.skin;
+               self.skin = stof(self.playerskin);
        }
  
-       if(chmdl || oldskin != self.skinindex)
+       if(chmdl || oldskin != self.skin)
                self.species = player_getspecies(); // model or skin has changed
  
        if(!teamplay)
@@@ -848,6 -657,9 +657,9 @@@ void PutClientInServer (void
                WriteByte(MSG_ONE, SVC_SETVIEW);
                WriteEntity(MSG_ONE, self);
        }
+       
+       // reset player keys
+       self.itemkeys = 0;
  
        // player is dead and becomes observer
        // FIXME fix LMS scoring for new system
                self.classname = "player";
                self.wasplayer = TRUE;
                self.iscreature = TRUE;
+               self.damagedbycontents = TRUE;
                self.movetype = MOVETYPE_WALK;
                self.solid = SOLID_SLIDEBOX;
                self.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
                if(clienttype(self) == CLIENTTYPE_BOT && autocvar_g_botclip_collisions)
                        self.dphitcontentsmask |= DPCONTENTS_BOTCLIP;
                self.frags = FRAGS_PLAYER;
-               if(independent_players)
+               if(INDEPENDENT_PLAYERS)
                        MAKE_INDEPENDENT_PLAYER(self);
                self.flags = FL_CLIENT;
+               if(autocvar__notarget)
+                       self.flags |= FL_NOTARGET;
                self.takedamage = DAMAGE_AIM;
                if(g_minstagib)
                        self.effects = EF_FULLBRIGHT;
                else
                        self.effects = 0;
+               self.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
                self.air_finished = time + 12;
                self.dmg = 2;
                if(autocvar_g_balance_nex_charge)
                        self.weapons = start_weapons;
                }
  
+               if(self.weapons & WEPBIT_SUPERWEAPONS) // exception for minstagib, as minstanex is a superweapon
+                       self.superweapons_finished = time + autocvar_g_balance_superweapons_time;
+               else
+                       self.superweapons_finished = 0;
                if(g_weaponarena_random)
                {
                        if(g_weaponarena_random_with_laser)
                }
  
                self.items = start_items;
-               self.jump_interval = time;
  
                self.spawnshieldtime = time + autocvar_g_spawnshieldtime;
                self.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
                }
                self.damageforcescale = 2;
                self.death_time = 0;
-               self.dead_frame = 0;
-               self.alpha = 0;
+               self.respawn_time = 0;
                self.scale = 0;
                self.fade_time = 0;
                self.pain_frame = 0;
                        WriteByte(MSG_ONE, TE_CSQC_SPAWN);
                });
  
-               if(sv_loddistance1)
-                       SetCustomizer(self, Client_customizeentityforclient, Client_uncustomizeentityforclient);
                self.model = "";
                FixPlayermodel();
+               self.drawonlytoclient = world;
  
                self.crouch = FALSE;
                self.view_ofs = PL_VIEW_OFS;
                self.prevorigin = self.origin;
                self.lastrocket = world; // stop rocket guiding, no revenge from the grave!
                self.lastteleporttime = time; // prevent insane speeds due to changing origin
+         self.hud = HUD_NORMAL;
+         
                if(g_arena)
                {
                        Spawnqueue_Remove(self);
                        self.killcount = 0;
                }
  
-               self.cnt = WEP_LASER;
                CL_SpawnWeaponentity();
                self.alpha = default_player_alpha;
                self.colormod = '1 1 1' * autocvar_g_player_brightness;
  
                race_PostSpawn(spot);
  
-               if(autocvar_spawn_debug)
-               {
-                       sprint(self, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
-                       remove(spot);   // usefull for checking if there are spawnpoints, that let drop through the floor
-               }
                //stuffcmd(self, "chase_active 0");
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
  
                        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]) = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));
                }
-               self.weapon_forbidchange = FALSE;
  
                oldself = self;
                self = spot;
                        activator = world;
                self = oldself;
  
+               spawn_spot = spot;
                MUTATOR_CALLHOOK(PlayerSpawn);
  
+               if(autocvar_spawn_debug)
+               {
+                       sprint(self, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
+                       remove(spot);   // usefull for checking if there are spawnpoints, that let drop through the floor
+               }
                self.switchweapon = w_getbestweapon(self);
-               self.cnt = self.switchweapon;
+               self.cnt = -1; // W_LastWeapon will not complain
                self.weapon = 0;
+               self.weaponname = "";
+               self.switchingweapon = 0;
  
                if(!self.alivetime)
                        self.alivetime = time;
+               antilag_clear(self);
        } else if(self.classname == "observer" || (g_ca && !allowed_to_spawn)) {
                PutObserverInServer ();
        }
@@@ -1147,6 -967,7 +967,7 @@@ float ClientInit_SendEntity(entity to, 
        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
        WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange);
+       WriteByte(MSG_ENTITY, autocvar_g_balance_porto_secondary);
        return TRUE;
  }
  
@@@ -1300,7 -1121,7 +1121,7 @@@ void KillIndicator_Think(
                return;
        }
  
-       if (!self.owner.modelindex)
+       if (self.owner.alpha < 0)
        {
                self.owner.killindicator = world;
                remove(self);
                {
                        if(self.cnt <= 10)
                                AnnounceTo(self.owner, strcat(ftos(self.cnt), ""));
-                       if(self.owner.killindicator_teamchange)
-                       {
-                               if(self.owner.killindicator_teamchange == -1)
-                                       centerprint(self.owner, strcat("Changing team in ", ftos(self.cnt), " seconds"));
-                               else if(self.owner.killindicator_teamchange == -2)
-                                       centerprint(self.owner, strcat("Spectating in ", ftos(self.cnt), " seconds"));
-                               else
-                                       centerprint(self.owner, strcat("Changing to ", ColoredTeamName(self.owner.killindicator_teamchange), " in ", ftos(self.cnt), " seconds"));
-                       }
-                       else
-                               centerprint(self.owner, strcat("^1Suicide in ", ftos(self.cnt), " seconds"));
                }
                self.nextthink = time + 1;
                self.cnt -= 1;
        }
  }
  
+ float clientkilltime;
  void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2 = spec
  {
        float killtime;
+       float starttime;
        entity e;
  
        if (gameover)
  
      if(!self.killindicator)
        {
-               if(self.modelindex && self.deadflag == DEAD_NO)
+               if(self.deadflag == DEAD_NO)
                {
                        killtime = max(killtime, self.clientkill_nexttime - time);
                        self.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam;
                }
  
-               if(killtime <= 0 || !self.modelindex || self.deadflag != DEAD_NO)
+               if(killtime <= 0 || self.classname != "player" || self.deadflag != DEAD_NO)
                {
                        ClientKill_Now();
                }
                else
                {
+                       starttime = max(time, clientkilltime);
                        self.killindicator = spawn();
                        self.killindicator.owner = self;
                        self.killindicator.scale = 0.5;
                        setattachment(self.killindicator, self, "");
                        setorigin(self.killindicator, '0 0 52');
                        self.killindicator.think = KillIndicator_Think;
-                       self.killindicator.nextthink = time + (self.lip) * 0.05;
+                       self.killindicator.nextthink = starttime + (self.lip) * 0.05;
+                       clientkilltime = max(clientkilltime, self.killindicator.nextthink + 0.05);
                        self.killindicator.cnt = ceil(killtime);
                        self.killindicator.count = bound(0, ceil(killtime), 10);
                        //sprint(self, strcat("^1You'll be dead in ", ftos(self.killindicator.cnt), " seconds\n"));
                                setattachment(e.killindicator, e, "");
                                setorigin(e.killindicator, '0 0 52');
                                e.killindicator.think = KillIndicator_Think;
-                               e.killindicator.nextthink = time + (e.lip) * 0.05;
+                               e.killindicator.nextthink = starttime + (e.lip) * 0.05;
+                               clientkilltime = max(clientkilltime, e.killindicator.nextthink + 0.05);
                                e.killindicator.cnt = ceil(killtime);
                        }
                        self.lip = 0;
        if(self.killindicator)
        {
                if(targetteam == 0) // just die
+               {
                        self.killindicator.colormod = '0 0 0';
+                       if(clienttype(self) == CLIENTTYPE_REAL)
+                       if(self.killindicator.cnt > 0)
+                               Send_CSQC_Centerprint_Generic(self, CPID_TEAMCHANGE, "^1Suicide in %d seconds", 1, self.killindicator.cnt);
+               }
                else if(targetteam == -1) // auto
+               {
                        self.killindicator.colormod = '0 1 0';
+                       if(clienttype(self) == CLIENTTYPE_REAL)
+                       if(self.killindicator.cnt > 0)
+                               Send_CSQC_Centerprint_Generic(self, CPID_TEAMCHANGE, "Changing team in %d seconds", 1, self.killindicator.cnt);
+               }
                else if(targetteam == -2) // spectate
+               {
                        self.killindicator.colormod = '0.5 0.5 0.5';
+                       if(clienttype(self) == CLIENTTYPE_REAL)
+                       if(self.killindicator.cnt > 0)
+                               Send_CSQC_Centerprint_Generic(self, CPID_TEAMCHANGE, "Spectating in %d seconds", 1, self.killindicator.cnt);
+               }
                else
+               {
                        self.killindicator.colormod = TeamColor(targetteam);
+                       if(clienttype(self) == CLIENTTYPE_REAL)
+                       if(self.killindicator.cnt > 0)
+                               Send_CSQC_Centerprint_Generic(self, CPID_TEAMCHANGE, strcat("Changing to ", ColoredTeamName(targetteam), " in %d seconds"), 1, self.killindicator.cnt);
+               }
        }
  }
  
  void ClientKill (void)
@@@ -1624,6 -1461,8 +1461,8 @@@ void ClientConnect (void
  
        self.playerid = (playerid_last = playerid_last + 1);
  
+       PlayerStats_AddEvent(sprintf("kills-%d", self.playerid));
      if(clienttype(self) == CLIENTTYPE_BOT)
          PlayerStats_AddPlayer(self);
  
  
        bprint("\n");
  
-       self.welcomemessage_time = 0;
        stuffcmd(self, strcat(clientstuff, "\n"));
-       stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
-       stuffcmd(self, "cl_particles_reloadeffects\n");
+       stuffcmd(self, "cl_particles_reloadeffects\n"); // TODO do we still need this?
  
        FixClientCvars(self);
  
        // Wazat's grappling hook
        SetGrappleHookBindings();
  
-       // get autoswitch state from player when he toggles it
-       stuffcmd(self, "alias autoswitch \"set cl_autoswitch $1 ; cmd autoswitch $1\"\n"); // default.cfg-ed in 2.4.1
        // get version info from player
        stuffcmd(self, "cmd clientversion $gameversion\n");
  
        else
                stuffcmd(self, "set _teams_available 0\n");
  
-       stuffcmd(self, strcat("set gametype ", ftos(game), "\n"));
        if(g_arena || g_ca)
        {
                self.classname = "observer";
        }
  
        self.jointime = time;
-       self.allowedTimeouts = autocvar_sv_timeout_number;
+       self.allowed_timeouts = autocvar_sv_timeout_number;
  
        if(clienttype(self) == CLIENTTYPE_REAL)
        {
                set_dom_state(self);
  
        CheatInitClient();
+       if(!autocvar_g_campaign)
+               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, getwelcomemessage(), autocvar_welcome_message_time, 0);
+       CSQCMODEL_AUTOINIT();
+       self.model_randomizer = random();
  }
  
  /*
@@@ -1812,8 -1650,9 +1650,9 @@@ void ClientDisconnect (void
  
        Portal_ClearAll(self);
  
+       RemoveGrapplingHook(self);
        if(self.flagcarried)
 -              DropFlag(self.flagcarried, world, world);
 +              ctf_Handle_Drop(self); // FIXCTF
        if(self.ballcarried && g_nexball)
                DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
  
  void ChatBubbleThink()
  {
        self.nextthink = time;
-       if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
+       if ((self.owner.alpha < 0) || self.owner.chatbubbleentity != self)
        {
                if(self.owner) // but why can that ever be world?
                        self.owner.chatbubbleentity = world;
                self.model = self.mdl;
        else
                self.model = "";
- };
+ }
  
  void UpdateChatBubble()
  {
-       if (!self.modelindex)
+       if (self.alpha < 0)
                return;
        // spawn a chatbubble entity if needed
        if (!self.chatbubbleentity)
  // added to the model skins
  /*void UpdateColorModHack()
  {
-       local float c;
+       float c;
        c = self.clientcolors & 15;
        // LordHavoc: only bothering to support white, green, red, yellow, blue
             if (!teamplay) self.colormod = '0 0 0';
        else if (c == 12) self.colormod = '1.22 1.22 0.10';
        else if (c == 13) self.colormod = '0.10 0.10 1.73';
        else self.colormod = '1 1 1';
- };*/
+ }*/
  
- .float oldcolormap;
  void respawn(void)
  {
-       if(self.modelindex != 0 && autocvar_g_respawn_ghosts)
+       if(self.alpha >= 0 && autocvar_g_respawn_ghosts)
        {
                self.solid = SOLID_NOT;
                self.takedamage = DAMAGE_NO;
                self.movetype = MOVETYPE_FLY;
                self.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
                self.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
-               self.effects |= EF_ADDITIVE;
-               self.oldcolormap = self.colormap;
-               self.colormap = 512;
+               self.effects |= CSQCMODEL_EF_RESPAWNGHOST;
                pointparticles(particleeffectnum("respawn_ghost"), self.origin, '0 0 0', 1);
                if(autocvar_g_respawn_ghosts_maxtime)
                        SUB_SetFade (self, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5);
        }
  
        CopyBody(1);
        self.effects |= EF_NODRAW; // prevent another CopyBody
-       if(self.oldcolormap)
-       {
-               self.colormap = self.oldcolormap;
-               self.oldcolormap = 0;
-       }
        PutClientInServer();
  }
  
@@@ -1956,47 -1788,6 +1788,6 @@@ void play_countdown(float finished, str
                                sound (self, CH_INFO, samp, VOL_BASE, ATTN_NORM);
  }
  
- /**
-  * When sv_timeout is used this function returs strings like
-  * "Timeout begins in 2 seconds!\n" or "Timeout ends in 23 seconds!\n".
-  * Called by centerprint functions
-  * @param addOneSecond boolean, set to 1 if the welcome-message centerprint asks for the text
-  */
- string getTimeoutText(float addOneSecond) {
-       if (!autocvar_sv_timeout || !timeoutStatus)
-               return "";
-       local string retStr;
-       if (timeoutStatus == 1) {
-               if (addOneSecond == 1) {
-                       retStr = strcat("Timeout begins in ", ftos(remainingLeadTime + 1), " seconds!\n");
-               }
-               else {
-                       retStr = strcat("Timeout begins in ", ftos(remainingLeadTime), " seconds!\n");
-               }
-               return retStr;
-       }
-       else if (timeoutStatus == 2) {
-               if (addOneSecond) {
-                       retStr = strcat("Timeout ends in ", ftos(remainingTimeoutTime + 1), " seconds!\n");
-                       //don't show messages like "Timeout ends in 0 seconds"...
-                       if ((remainingTimeoutTime + 1) > 0)
-                               return retStr;
-                       else
-                               return "";
-               }
-               else {
-                       retStr = strcat("Timeout ends in ", ftos(remainingTimeoutTime), " seconds!\n");
-                       //don't show messages like "Timeout ends in 0 seconds"...
-                       if (remainingTimeoutTime > 0)
-                               return retStr;
-                       else
-                               return "";
-               }
-       }
-       else return "";
- }
  void player_powerups (void)
  {
        // add a way to see what the items were BEFORE all of these checks for the mutator hook
                self.modelflags &~= MF_ROCKET;
        }
  
-       self.effects &~= (EF_DIMLIGHT | EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
+       self.effects &~= (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
  
-       if(!self.modelindex || self.deadflag) // don't apply the flags if the player is gibbed
+       if(self.alpha < 0 || self.deadflag) // don't apply the flags if the player is gibbed
                return;
  
        Fire_ApplyDamage(self);
                if (self.items & IT_INVINCIBLE)
                {
                        play_countdown(self.invincible_finished, "misc/poweroff.wav");
-                       if (time > self.invincible_finished && autocvar_g_balance_powerup_timer)
+                       if (time > self.invincible_finished)
                        {
                                self.items = self.items - (self.items & IT_INVINCIBLE);
                                sprint(self, "^3Speed has worn off\n");
                {
                        play_countdown(self.strength_finished, "misc/poweroff.wav");
                        self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > self.strength_finished && autocvar_g_balance_powerup_timer)
+                       if (time > self.strength_finished)
                        {
                                self.items = self.items - (self.items & IT_STRENGTH);
                                sprint(self, "^3Strength has worn off\n");
                {
                        play_countdown(self.invincible_finished, "misc/poweroff.wav");
                        self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > self.invincible_finished && autocvar_g_balance_powerup_timer)
+                       if (time > self.invincible_finished)
                        {
                                self.items = self.items - (self.items & IT_INVINCIBLE);
                                sprint(self, "^3Shield has worn off\n");
                                sprint(self, "^3Shield surrounds you\n");
                        }
                }
+               if (self.items & IT_SUPERWEAPON)
+               {
+                       //if(W_WeaponBit(self.weapon) & WEPBIT_SUPERWEAPONS)
+                       //      self.effects = self.effects | EF_RED;
+                       if (!(self.weapons & WEPBIT_SUPERWEAPONS))
+                       {
+                               self.superweapons_finished = 0;
+                               self.items = self.items - (self.items & IT_SUPERWEAPON);
+                               sprint(self, "^3Superweapons have been lost\n");
+                       }
+                       else if (self.items & IT_UNLIMITED_SUPERWEAPONS)
+                       {
+                               // don't let them run out
+                       }
+                       else
+                       {
+                               play_countdown(self.superweapons_finished, "misc/poweroff.wav");
+                               if (time > self.superweapons_finished)
+                               {
+                                       self.items = self.items - (self.items & IT_SUPERWEAPON);
+                                       self.weapons &~= WEPBIT_SUPERWEAPONS;
+                                       sprint(self, "^3Superweapons have broken down\n");
+                               }
+                       }
+               }
+               else if(self.weapons & WEPBIT_SUPERWEAPONS)
+               {
+                       if (time < self.superweapons_finished || (self.items & IT_UNLIMITED_SUPERWEAPONS))
+                       {
+                               self.items = self.items | IT_SUPERWEAPON;
+                               sprint(self, "^3You now have a superweapon\n");
+                       }
+                       else
+                       {
+                               self.superweapons_finished = 0;
+                               self.weapons &~= WEPBIT_SUPERWEAPONS; // just in case
+                       }
+               }
+               else
+               {
+                       self.superweapons_finished = 0;
+               }
+       }
+       
+       if(autocvar_g_nodepthtestplayers)
+               self.effects = self.effects | EF_NODEPTHTEST;
  
-               if(autocvar_g_nodepthtestplayers)
-                       self.effects = self.effects | EF_NODEPTHTEST;
-               if(autocvar_g_fullbrightplayers)
-                       self.effects = self.effects | EF_FULLBRIGHT;
+       if(autocvar_g_fullbrightplayers)
+               self.effects = self.effects | EF_FULLBRIGHT;
  
-               // midair gamemode: damage only while in the air
-               // if in midair mode, being on ground grants temporary invulnerability
-               // (this is so that multishot weapon don't clear the ground flag on the
-               // first damage in the frame, leaving the player vulnerable to the
-               // remaining hits in the same frame)
-               if (self.flags & FL_ONGROUND)
-               if (g_midair)
-                       self.spawnshieldtime = max(self.spawnshieldtime, time + autocvar_g_midair_shieldtime);
+       // midair gamemode: damage only while in the air
+       // if in midair mode, being on ground grants temporary invulnerability
+       // (this is so that multishot weapon don't clear the ground flag on the
+       // first damage in the frame, leaving the player vulnerable to the
+       // remaining hits in the same frame)
+       if (self.flags & FL_ONGROUND)
+       if (g_midair)
+               self.spawnshieldtime = max(self.spawnshieldtime, time + autocvar_g_midair_shieldtime);
  
-               if (time >= game_starttime)
-               if (time < self.spawnshieldtime)
-                       self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
-       }
+       if (time >= game_starttime)
+       if (time < self.spawnshieldtime)
+               self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
  
        MUTATOR_CALLHOOK(PlayerPowerups);
  }
@@@ -2325,6 -2158,7 +2158,7 @@@ void SpectateCopy(entity spectatee) 
        self.pressedkeys = spectatee.pressedkeys;
        self.weapons = spectatee.weapons;
        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.dmg_save = spectatee.dmg_save;
        self.dmg_inflictor = spectatee.dmg_inflictor;
        self.angles = spectatee.v_angle;
-       self.fixangle = TRUE;
+       if(!self.BUTTON_USE)
+               self.fixangle = TRUE;
        setorigin(self, spectatee.origin);
        setsize(self, spectatee.mins, spectatee.maxs);
        SetZoomState(spectatee.zoomstate);
@@@ -2384,12 -2219,41 +2219,41 @@@ float SpectateUpdate() 
        return 1;
  }
  
- float SpectateNext() {
-       other = find(self.enemy, classname, "player");
  
-       if (!other)
+ // Returns next available player to spectate if g_ca_spectate_enemies == 0
+ entity CA_SpectateNext(entity start) {
+       if (start.team == self.team) {
+               return start;
+       }
+       
+       other = start;
+       // continue from current player
+       while(other && other.team != self.team) {
                other = find(other, classname, "player");
+       }
+       
+       if (!other) {
+               // restart from begining
+               other = find(other, classname, "player");
+               while(other && other.team != self.team) {
+                       other = find(other, classname, "player");
+               }
+       }
+       
+       return other;
+ }
  
+ float SpectateNext() {
+       other = find(self.enemy, classname, "player");
+       if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) {
+               // CA and ca players when spectating enemies is forbidden
+               other = CA_SpectateNext(other);
+       } else {
+               // other modes and ca spectators or spectating enemies is allowed
+               if (!other)
+                       other = find(other, classname, "player");
+       }
+       
        if (other)
                self.enemy = other;
  
@@@ -2435,18 -2299,19 +2299,19 @@@ void ShowRespawnCountdown(
                return;
        else
        {
-               number = ceil(self.death_time - time);
+               number = ceil(self.respawn_time - time);
                if(number <= 0)
                        return;
                if(number <= self.respawn_countdown)
                {
                        self.respawn_countdown = number - 1;
-                       if(ceil(self.death_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds
+                       if(ceil(self.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds
                                AnnounceTo(self, strcat(ftos(number), ""));
                }
        }
  }
  
+ .float prevent_join_msgtime;
  void LeaveSpectatorMode()
  {
        if(nJoinAllowed(1)) {
                                bprint ("^4", self.netname, "^4 is playing now\n");
  
                        if(!autocvar_g_campaign)
-                               centerprint(self,""); // clear MOTD
+                       if (time < self.jointime + autocvar_welcome_message_time)
+                               Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD); // clear MOTD
+                       if (self.prevent_join_msgtime)
+                       {
+                               Send_CSQC_Centerprint_Generic_Expire(self, CPID_PREVENT_JOIN);
+                               self.prevent_join_msgtime = 0;
+                       }
  
                        return;
                } else {
        }
        else {
                //player may not join because of g_maxplayers is set
-               centerprint_atprio(self, CENTERPRIO_MAPVOTE, PREVENT_JOIN_TEXT);
+               if (time - self.prevent_join_msgtime > 2)
+               {
+                       Send_CSQC_Centerprint_Generic(self, CPID_PREVENT_JOIN, PREVENT_JOIN_TEXT, 0, 0);
+                       self.prevent_join_msgtime = time;
+               }
        }
  }
  
@@@ -2493,16 -2369,16 +2369,16 @@@ float nJoinAllowed(float includeMe) 
                return FALSE; // forced spectators can never join
  
        // TODO simplify this
-       local entity e;
+       entity e;
  
-       local float totalClients;
+       float totalClients;
        FOR_EACH_CLIENT(e)
                totalClients += 1;
  
        if (!autocvar_g_maxplayers)
                return maxclients - totalClients + includeMe;
  
-       local float currentlyPlaying;
+       float currentlyPlaying;
        FOR_EACH_REALPLAYER(e)
                currentlyPlaying += 1;
  
@@@ -2525,19 -2401,58 +2401,58 @@@ void checkSpectatorBlock() 
        }
  }
  
+ .float motd_actived_time; // used for both motd and campaign_message
+ void PrintWelcomeMessage()
+ {
+       if (self.motd_actived_time == 0) { // is there already a message showing?
+               if (autocvar_g_campaign) {
+                       if ((self.classname == "player" && self.BUTTON_INFO) || (self.classname != "player")) {
+                               self.motd_actived_time = time;
+                               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, campaign_message, -1, 0);
+                       }
+               } else {
+                       if ((time - self.jointime > autocvar_welcome_message_time) && self.BUTTON_INFO) {
+                               self.motd_actived_time = time;
+                               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, getwelcomemessage(), -1, 0);
+                       }
+               }
+       } else { // showing MOTD or campaign message
+               if (autocvar_g_campaign) {
+                       if (self.BUTTON_INFO)
+                               self.motd_actived_time = time;
+                       else if ((time - self.motd_actived_time > 2) && self.classname == "player") { // hide it some seconds after BUTTON_INFO has been released
+                               self.motd_actived_time = 0;
+                               Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD);
+                       }
+               } else {
+                       if ((time - self.jointime) > autocvar_welcome_message_time) {
+                               if (self.BUTTON_INFO)
+                                       self.motd_actived_time = time;
+                               else if (time - self.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
+                                       self.motd_actived_time = 0;
+                                       Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD);
+                               }
+                       }
+               }
+       }
+ }
  void ObserverThink()
  {
+       float prefered_movetype;
        if (self.flags & FL_JUMPRELEASED) {
                if (self.BUTTON_JUMP && !self.version_mismatch) {
-                       self.welcomemessage_time = 0;
                        self.flags &~= FL_JUMPRELEASED;
                        self.flags |= FL_SPAWNING;
                } else if(self.BUTTON_ATCK && !self.version_mismatch) {
-                       self.welcomemessage_time = 0;
                        self.flags &~= FL_JUMPRELEASED;
                        if(SpectateNext() == 1) {
                                self.classname = "spectator";
                        }
+               } else {
+                       prefered_movetype = ((!self.BUTTON_USE ? self.cvar_cl_clippedspectating : !self.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+                       if (self.movetype != prefered_movetype)
+                               self.movetype = prefered_movetype;
                }
        } else {
                if (!(self.BUTTON_ATCK || self.BUTTON_JUMP)) {
                        }
                }
        }
-       PrintWelcomeMessage(self);
+       PrintWelcomeMessage();
  }
  
  void SpectatorThink()
  {
        if (self.flags & FL_JUMPRELEASED) {
                if (self.BUTTON_JUMP && !self.version_mismatch) {
-                       self.welcomemessage_time = 0;
                        self.flags &~= FL_JUMPRELEASED;
                        self.flags |= FL_SPAWNING;
                } else if(self.BUTTON_ATCK) {
-                       self.welcomemessage_time = 0;
                        self.flags &~= FL_JUMPRELEASED;
                        if(SpectateNext() == 1) {
                                self.classname = "spectator";
                                PutClientInServer();
                        }
                } else if (self.BUTTON_ATCK2) {
-                       self.welcomemessage_time = 0;
                        self.flags &~= FL_JUMPRELEASED;
                        self.classname = "observer";
                        PutClientInServer();
                        PutObserverInServer();
        }
  
-       PrintWelcomeMessage(self);
+       PrintWelcomeMessage();
        self.flags |= FL_CLIENT | FL_NOTARGET;
  }
  
 -float ctf_usekey();
  void PlayerUseKey()
  {
        if(self.classname != "player")
        }
        
        // a use key was pressed; call handlers
 -      if(ctf_usekey())
 -              return;
 -
        MUTATOR_CALLHOOK(PlayerUseKey);
  }
  
@@@ -2621,7 -2538,7 +2534,7 @@@ Called every frame for each client befo
  =============
  */
  .float usekeypressed;
 -void() ctf_setstatus;
 +//void() ctf_setstatus;
  void() nexball_setstatus;
  .float items_added;
  void PlayerPreThink (void)
                if(self.cvar_g_xonoticversion)
                        if(time > self.version_nagtime)
                        {
-                               if(strstr(self.cvar_g_xonoticversion, "git", 0) < 0)
+                               // don't notify git users
+                               if(strstr(self.cvar_g_xonoticversion, "git", 0) < 0 && strstr(self.cvar_g_xonoticversion, "autobuild", 0) < 0)
                                {
-                                       if(strstr(autocvar_g_xonoticversion, "git", 0) >= 0)
+                                       if(strstr(autocvar_g_xonoticversion, "git", 0) >= 0 || strstr(autocvar_g_xonoticversion, "autobuild", 0) >= 0)
                                        {
+                                               // notify release users if connecting to git
                                                dprint("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, " (beta)^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
                                                sprint(self, strcat("\{1}^1NOTE: ^7the server is running ^3Xonotic ", autocvar_g_xonoticversion, " (beta)^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n"));
                                        }
                                                r = vercmp(self.cvar_g_xonoticversion, autocvar_g_xonoticversion);
                                                if(r < 0)
                                                {
-                                                       dprint("^1NOTE^7 to ", self.netname, "^7 - ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.com/^1!\n");
-                                                       sprint(self, strcat("\{1}^1NOTE: ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.com/^1!\n"));
+                                                       // give users new version
+                                                       dprint("^1NOTE^7 to ", self.netname, "^7 - ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.org/^1!\n");
+                                                       sprint(self, strcat("\{1}^1NOTE: ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.org/^1!\n"));
                                                }
                                                else if(r > 0)
                                                {
-                                                       dprint("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, "^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
+                                                       // notify users about old server version
+                                                       print("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, "^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
                                                        sprint(self, strcat("\{1}^1NOTE: ^7the server is running ^3Xonotic ", autocvar_g_xonoticversion, "^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n"));
                                                }
                                        }
  
        MUTATOR_CALLHOOK(PlayerPreThink);
  
-       if(self.BUTTON_USE && !self.usekeypressed)
-               PlayerUseKey();
-       self.usekeypressed = self.BUTTON_USE;
+       if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
+       {
+               if(self.BUTTON_USE && !self.usekeypressed)
+                       PlayerUseKey();
+               self.usekeypressed = self.BUTTON_USE;
+       }
+       PrintWelcomeMessage();
  
        if(self.classname == "player") {
  //            if(self.netname == "Wazat")
  
                CheckRules_Player();
  
-               PrintWelcomeMessage(self);
                if (intermission_running)
                {
                        IntermissionThink ();   // otherwise a button could be missed between
                }
  
                //don't allow the player to turn around while game is paused!
-               if(timeoutStatus == 2) {
+               if(timeout_status == TIMEOUT_ACTIVE) {
                        // FIXME turn this into CSQC stuff
                        self.v_angle = self.lastV_angle;
                        self.angles = self.lastV_angle;
  
                if(frametime)
                {
-                       if(self.health <= 0 && autocvar_g_deathglow)
-                       {
-                               if(self.glowmod_x > 0)
-                                       self.glowmod_x -= autocvar_g_deathglow * frametime;
-                               else
-                                       self.glowmod_x = -1;
-                               if(self.glowmod_y > 0)
-                                       self.glowmod_y -= autocvar_g_deathglow * frametime;
-                               else
-                                       self.glowmod_y = -1;
-                               if(self.glowmod_z > 0)
-                                       self.glowmod_z -= autocvar_g_deathglow * frametime;
-                               else
-                                       self.glowmod_z = -1;
-                       }
-                       else
+ #ifndef NO_LEGACY_NETWORKING
+                       self.glowmod = colormapPaletteColor(self.clientcolors & 0x0F, TRUE) * 2;
+ #endif
+                       if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
                        {
-                               // set weapon and player glowmod
-                               self.glowmod = colormapPaletteColor(self.clientcolors & 0x0F, TRUE) * 2;
+                               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);
  
-                               if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
+                               if(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.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);
-                                       if(self.nex_charge > 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.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.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);
                                }
-                               else
-                                       self.weaponentity_glowmod = self.glowmod;
                        }
+                       else
+                               self.weaponentity_glowmod = colormapPaletteColor(self.clientcolors & 0x0F, TRUE) * 2;
                        player_powerups();
                }
  
+               if (g_minstagib)
+                       minstagib_ammocheck();
                if (self.deadflag != DEAD_NO)
                {
                        float button_pressed, force_respawn;
                        if(self.personal && g_race_qualifying)
                        {
-                               if(time > self.death_time)
+                               if(time > self.respawn_time)
                                {
-                                       self.death_time = time + 1; // only retry once a second
+                                       self.respawn_time = time + 1; // only retry once a second
                                        respawn();
                                        self.impulse = 141;
                                }
                                }
                                else if (self.deadflag == DEAD_RESPAWNING)
                                {
-                                       if(time > self.death_time)
+                                       if(time > self.respawn_time)
                                        {
-                                               self.death_time = time + 1; // only retry once a second
+                                               self.respawn_time = time + 1; // only retry once a second
                                                respawn();
                                        }
                                }
                        }
                        return;
                }
+               // FIXME from now on self.deadflag is always 0 (and self.health is never < 1)
+               // so (self.deadflag == DEAD_NO) is always true in the code below
  
                if(g_touchexplode)
                if(time > self.touchexplode_time)
  
                self.prevorigin = self.origin;
  
-               if ((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss)
+               if (((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss) && self.animstate_startframe != self.anim_melee_x) // prevent crouching if using melee attack
                {
                        if (!self.crouch)
                        {
                                self.crouch = TRUE;
                                self.view_ofs = PL_CROUCH_VIEW_OFS;
                                setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
-                               setanim(self, self.anim_duck, FALSE, TRUE, TRUE);
+                               // setanim(self, self.anim_duck, FALSE, TRUE, TRUE); // this anim is BROKEN anyway
                        }
                }
                else
                if(frametime)
                        player_anim();
  
-               if (g_minstagib)
-                       minstagib_ammocheck();
 -              if(g_ctf)
 -                      ctf_setstatus();
 +              //if(g_ctf)
 +              //      ctf_setstatus();
  
                if(g_nexball)
                        nexball_setstatus();
+               
+               // secret status
+               secrets_setstatus();
+               
                self.dmg_team = max(0, self.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
  
                //self.angles_y=self.v_angle_y + 90;   // temp
@@@ -3048,42 -2961,52 +2957,52 @@@ void PlayerPostThink (void
                stuffcmd(self, strcat("name ", self.netname, substring(ftos(random()), 2, -1), "\n"));
        }
  
-       if(sv_maxidle && frametime)
+       if(sv_maxidle && frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
        {
-               // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
-               float timeleft;
-               timeleft = ceil(sv_maxidle - (time - self.parm_idlesince));
-               if(timeleft <= 0)
+               if (time - self.parm_idlesince < 1) // instead of (time == self.parm_idlesince) to support sv_maxidle <= 10
                {
-                       bprint("^3", self.netname, "^3 was kicked for idling.\n");
-                       AnnounceTo(self, "terminated");
-                       dropclient(self);
-                       return;
-               }
-               else if(timeleft <= 10)
-               {
-                       if(timeleft != self.idlekick_lasttimeleft)
+                       if(self.idlekick_lasttimeleft)
                        {
-                               centerprint_atprio(self, CENTERPRIO_IDLEKICK, strcat("^3Stop idling!\n^3Disconnecting in ", ftos(timeleft), "..."));
-                               AnnounceTo(self, strcat(ftos(timeleft), ""));
+                               Send_CSQC_Centerprint_Generic_Expire(self, CPID_DISCONNECT_IDLING);
+                               self.idlekick_lasttimeleft = 0;
                        }
                }
                else
                {
-                       centerprint_expire(self, CENTERPRIO_IDLEKICK);
+                       float timeleft;
+                       timeleft = ceil(sv_maxidle - (time - self.parm_idlesince));
+                       if(timeleft == min(10, sv_maxidle - 1)) // - 1 to support sv_maxidle <= 10
+                       {
+                               if(!self.idlekick_lasttimeleft)
+                                       Send_CSQC_Centerprint_Generic(self, CPID_DISCONNECT_IDLING, "^3Stop idling!\n^3Disconnecting in %d seconds...", 1, timeleft);
+                       }
+                       if(timeleft <= 0)
+                       {
+                               bprint("^3", self.netname, "^3 was kicked for idling.\n");
+                               AnnounceTo(self, "terminated");
+                               dropclient(self);
+                               return;
+                       }
+                       else if(timeleft <= 10)
+                       {
+                               if(timeleft != self.idlekick_lasttimeleft)
+                                       AnnounceTo(self, ftos(timeleft));
+                               self.idlekick_lasttimeleft = timeleft;
+                       }
                }
-               self.idlekick_lasttimeleft = timeleft;
        }
  
  #ifdef TETRIS
        if(self.impulse == 100)
                ImpulseCommands();
-       if (TetrisPostFrame())
-               return;
+       if (!TetrisPostFrame())
+       {
  #endif
  
        CheatFrame();
  
+       //CheckPlayerJump();
        if(self.classname == "player") {
                CheckRules_Player();
                UpdateChatBubble();
                //do nothing
        }
        
+ #ifdef TETRIS
+       }
+ #endif
        /*
        float i;
        for(i = 0; i < 1000; ++i)
                        self.stored_netname = strzone(uid2name(self.crypto_idfp));
                if(self.stored_netname != self.netname)
                {
-                       db_put(ServerProgsDB, strcat("uid2name", self.crypto_idfp), self.netname);
+                       db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname);
                        strunzone(self.stored_netname);
                        self.stored_netname = strzone(self.netname);
                }
        if(g_race)
                dprint(sprintf("%f %.6f\n", time, race_GetFractionalLapCount(self)));
        */
+       CSQCMODEL_AUTOUPDATE();
  }
index 376e0ec8c17e3dad397b2ab68d015d5efbcf0a93,49b52555f6ebb0734b8ee371a3b33f08b5cf8466..cd7188b0a571aca501fdff6dfa7c5bac1729ed41
@@@ -13,23 -13,26 +13,26 @@@ void WeaponStats_Init(
  
  #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_Shutdown()
+ void WeaponStats_ready(entity fh, entity pass, float status)
  {
-       float i, j, ibot, jbot, idx;
-       float fh;
+       float i, j, n, ibot, jbot, idx;
        vector v;
-       string prefix;
-       if(weaponstats_buffer < 0)
-               return;
-       prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t");
-       if(autocvar_sv_weaponstats_file != "")
+       string prefix, s;
+       switch(status)
        {
-               fh = fopen(autocvar_sv_weaponstats_file, FILE_APPEND);
-               if(fh >= 0)
-               {
-                       fputs(fh, "#begin statsfile\n");
-                       fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"));
-                       fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_changes)), "\n"));
+               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)
                                {
                                        if(v != '0 0 0')
                                        {
                                                //vector is: kills hits damage
-                                               fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot));
-                                               fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z));
+                                               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));
                                        }
                                }
-                       fputs(fh, "#end\n\n");
-                       fclose(fh);
+                       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;
        }
-       buf_del(weaponstats_buffer);
-       weaponstats_buffer = -1;
  }
  
  void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item)
@@@ -88,9 -124,25 +124,25 @@@ void WeaponStats_LogKill(float awep, fl
  .entity pusher;
  .float pushltime;
  
+ .float CopyBody_nextthink;
+ .void(void) CopyBody_think;
+ void CopyBody_Think(void)
+ {
+       if(self.CopyBody_nextthink && time > self.CopyBody_nextthink)
+       {
+               self.CopyBody_think();
+               if(wasfreed(self))
+                       return;
+               self.CopyBody_nextthink = self.nextthink;
+               self.CopyBody_think = self.think;
+               self.think = CopyBody_Think;
+       }
+       CSQCMODEL_AUTOUPDATE();
+       self.nextthink = time;
+ }
  void CopyBody(float keepvelocity)
  {
-       local entity oldself;
+       entity oldself;
        if (self.effects & EF_NODRAW)
                return;
        oldself = self;
        self.enemy = oldself;
        self.lip = oldself.lip;
        self.colormap = oldself.colormap;
-       self.glowmod = oldself.glowmod;
        self.iscreature = oldself.iscreature;
+       self.damagedbycontents = oldself.damagedbycontents;
        self.angles = oldself.angles;
        self.avelocity = oldself.avelocity;
        self.classname = "body";
        self.damageforcescale = oldself.damageforcescale;
        self.effects = oldself.effects;
+       self.glowmod = oldself.glowmod;
        self.event_damage = oldself.event_damage;
        self.animstate_startframe = oldself.animstate_startframe;
        self.animstate_numframes = oldself.animstate_numframes;
        self.animstate_endtime = oldself.animstate_endtime;
        self.animstate_override = oldself.animstate_override;
        self.animstate_looping = oldself.animstate_looping;
+       self.dphitcontentsmask = oldself.dphitcontentsmask;
+       self.death_time = oldself.death_time;
        self.frame = oldself.frame;
-       self.dead_frame = oldself.dead_frame;
        self.pain_finished = oldself.pain_finished;
        self.health = oldself.health;
        self.armorvalue = oldself.armorvalue;
        self.armortype = oldself.armortype;
        self.model = oldself.model;
        self.modelindex = oldself.modelindex;
-       self.modelindex_lod0 = oldself.modelindex_lod0;
-       self.modelindex_lod0_from_xonotic = oldself.modelindex_lod0_from_xonotic;
-       self.modelindex_lod1 = oldself.modelindex_lod1;
-       self.modelindex_lod2 = oldself.modelindex_lod2;
-       self.skinindex = oldself.skinindex;
+       self.skin = oldself.skin;
        self.species = oldself.species;
        self.movetype = oldself.movetype;
-       self.nextthink = oldself.nextthink;
        self.solid = oldself.solid;
        self.ballistics_density = oldself.ballistics_density;
        self.takedamage = oldself.takedamage;
-       self.think = oldself.think;
        self.customizeentityforclient = oldself.customizeentityforclient;
        self.uncustomizeentityforclient = oldself.uncustomizeentityforclient;
        self.uncustomizeentityforclient_set = oldself.uncustomizeentityforclient_set;
        if (keepvelocity == 1)
                self.velocity = oldself.velocity;
        self.oldvelocity = self.velocity;
+       self.alpha = oldself.alpha;
        self.fade_time = oldself.fade_time;
        self.fade_rate = oldself.fade_rate;
        //self.weapon = oldself.weapon;
  
        Drag_MoveDrag(oldself, self);
  
+       self.owner = oldself;
+       if(self.colormap <= maxclients && self.colormap > 0)
+               self.colormap = 1024 + oldself.clientcolors;
+       CSQCMODEL_AUTOINIT();
+       self.CopyBody_nextthink = oldself.nextthink;
+       self.CopyBody_think = oldself.think;
+       self.nextthink = time;
+       self.think = CopyBody_Think;
        self = oldself;
  }
  
  float player_getspecies()
  {
        float s;
-       get_model_parameters(self.model, self.skinindex);
+       get_model_parameters(self.model, self.skin);
        s = get_model_parameters_species;
        get_model_parameters(string_null, 0);
        if(s < 0)
  
  void player_setupanimsformodel()
  {
-       local string animfilename;
-       local float animfile;
        // defaults for legacy .zym models without animinfo files
-       self.anim_die1 = '0 1 0.5'; // 2 seconds
-       self.anim_die2 = '1 1 0.5'; // 2 seconds
-       self.anim_draw = '2 1 3'; // TODO: analyze models and set framerate
-       self.anim_duck = '3 1 100'; // this anim seems bogus in most models, so make it play VERY briefly!
-       self.anim_duckwalk = '4 1 1';
-       self.anim_duckjump = '5 1 100'; // zym anims keep playing until changed, so this only has to start the anim, landing will end it
-       self.anim_duckidle = '6 1 1';
-       self.anim_idle = '7 1 1';
-       self.anim_jump = '8 1 100'; // zym anims keep playing until changed, so this only has to start the anim, landing will end it
-       self.anim_pain1 = '9 1 2'; // 0.5 seconds
-       self.anim_pain2 = '10 1 2'; // 0.5 seconds
-       self.anim_shoot = '11 1 5'; // TODO: analyze models and set framerate
-       self.anim_taunt = '12 1 0.33'; // FIXME?  there is no code using this anim
-       self.anim_run = '13 1 1';
-       self.anim_runbackwards = '14 1 1';
-       self.anim_strafeleft = '15 1 1';
-       self.anim_straferight = '16 1 1';
-       self.anim_dead1 = '17 1 1';
-       self.anim_dead2 = '18 1 1';
-       self.anim_forwardright = '19 1 1';
-       self.anim_forwardleft = '20 1 1';
-       self.anim_backright = '21 1 1';
-       self.anim_backleft  = '22 1 1';
-       self.anim_melee = '23 1 1';
-       animparseerror = FALSE;
-       animfilename = strcat(self.model, ".animinfo");
-       animfile = fopen(animfilename, FILE_READ);
-       if (animfile >= 0)
-       {
-               self.anim_die1         = animparseline(animfile);
-               self.anim_die2         = animparseline(animfile);
-               self.anim_draw         = animparseline(animfile);
-               self.anim_duck         = animparseline(animfile);
-               self.anim_duckwalk     = animparseline(animfile);
-               self.anim_duckjump     = animparseline(animfile);
-               self.anim_duckidle     = animparseline(animfile);
-               self.anim_idle         = animparseline(animfile);
-               self.anim_jump         = animparseline(animfile);
-               self.anim_pain1        = animparseline(animfile);
-               self.anim_pain2        = animparseline(animfile);
-               self.anim_shoot        = animparseline(animfile);
-               self.anim_taunt        = animparseline(animfile);
-               self.anim_run          = animparseline(animfile);
-               self.anim_runbackwards = animparseline(animfile);
-               self.anim_strafeleft   = animparseline(animfile);
-               self.anim_straferight  = animparseline(animfile);
-               self.anim_forwardright = animparseline(animfile);
-               self.anim_forwardleft  = animparseline(animfile);
-               self.anim_backright    = animparseline(animfile);
-               self.anim_backleft     = animparseline(animfile);
-               self.anim_melee        = animparseline(animfile);
-               fclose(animfile);
-               // derived anims
-               self.anim_dead1 = '0 1 1' + '1 0 0' * (self.anim_die1_x + self.anim_die1_y - 1);
-               self.anim_dead2 = '0 1 1' + '1 0 0' * (self.anim_die2_x + self.anim_die2_y - 1);
-               if (animparseerror)
-                       print("Parse error in ", animfilename, ", some player animations are broken\n");
-       }
-       else
-               dprint("File ", animfilename, " not found, assuming legacy .zym model animation timings\n");
+       self.anim_die1 = animfixfps(self, '0 1 0.5'); // 2 seconds
+       self.anim_die2 = animfixfps(self, '1 1 0.5'); // 2 seconds
+       self.anim_draw = animfixfps(self, '2 1 3');
+       // self.anim_duck = '3 1 100'; // This anim is broken, use slot 3 as a new free slot in the future ;)
+       self.anim_duckwalk = animfixfps(self, '4 1 1');
+       self.anim_duckjump = '5 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
+       self.anim_duckidle = animfixfps(self, '6 1 1');
+       self.anim_idle = animfixfps(self, '7 1 1');
+       self.anim_jump = '8 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
+       self.anim_pain1 = animfixfps(self, '9 1 2'); // 0.5 seconds
+       self.anim_pain2 = animfixfps(self, '10 1 2'); // 0.5 seconds
+       self.anim_shoot = animfixfps(self, '11 1 5'); // analyze models and set framerate
+       self.anim_taunt = animfixfps(self, '12 1 0.33');
+       self.anim_run = animfixfps(self, '13 1 1');
+       self.anim_runbackwards = animfixfps(self, '14 1 1');
+       self.anim_strafeleft = animfixfps(self, '15 1 1');
+       self.anim_straferight = animfixfps(self, '16 1 1');
+       //self.anim_dead1 = animfixfps(self, '17 1 1');
+       //self.anim_dead2 = animfixfps(self, '18 1 1');
+       self.anim_forwardright = animfixfps(self, '19 1 1');
+       self.anim_forwardleft = animfixfps(self, '20 1 1');
+       self.anim_backright = animfixfps(self, '21 1 1');
+       self.anim_backleft  = animfixfps(self, '22 1 1');
+       self.anim_melee = animfixfps(self, '23 1 1');
+       self.anim_duckwalkbackwards = animfixfps(self, '24 1 1');
+       self.anim_duckwalkstrafeleft = animfixfps(self, '25 1 1');
+       self.anim_duckwalkstraferight = animfixfps(self, '26 1 1');
+       self.anim_duckwalkforwardright = animfixfps(self, '27 1 1');
+       self.anim_duckwalkforwardleft = animfixfps(self, '28 1 1');
+       self.anim_duckwalkbackright = animfixfps(self, '29 1 1');
+       self.anim_duckwalkbackleft  = animfixfps(self, '30 1 1');
+       // TODO introspect models for finding right "fps" value (1/duration)
        // reset animstate now
        setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
- };
+ }
  
  void player_anim (void)
  {
                updateanim(self.weaponentity);
  
        if (self.deadflag != DEAD_NO)
-       {
-               if (time > self.animstate_endtime)
-               {
-                       if (self.maxs_z > 5)
-                       {
-                               self.maxs_z = 5;
-                               setsize(self, self.mins, self.maxs);
-                       }
-                       self.frame = self.dead_frame;
-               }
                return;
-       }
  
        if (!self.animstate_override)
        {
-               if (!(self.flags & FL_ONGROUND))
+               if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP)
                {
                        if (self.crouch)
-                               setanim(self, self.anim_duckjump, FALSE, TRUE, self.restart_jump);
+                       {
+                               if (self.animstate_startframe != self.anim_duckjump_x) // don't perform another trace if already playing the crouch jump anim
+                               {
+                                       traceline(self.origin + '0 0 1' * PL_CROUCH_MIN_z, self.origin + '0 0 1' * (PL_CROUCH_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
+                                       if(!trace_startsolid && trace_fraction == 1 || !(self.animstate_startframe == self.anim_duckwalk_x || self.animstate_startframe == self.anim_duckidle_x)) // don't get stuck on non-crouch anims
+                                       {
+                                               setanim(self, self.anim_duckjump, FALSE, TRUE, self.restart_jump);
+                                               self.restart_jump = FALSE;
+                                       }
+                               }
+                       }
                        else
-                               setanim(self, self.anim_jump, FALSE, TRUE, self.restart_jump);
-                       self.restart_jump = FALSE;
+                       {
+                 if (self.animstate_startframe != self.anim_jump_x) // don't perform another trace if already playing the jump anim
+                 {
+                     traceline(self.origin + '0 0 1' * PL_MIN_z, self.origin + '0 0 1' * (PL_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
+                     if(!trace_startsolid && trace_fraction == 1 || self.animstate_startframe == self.anim_idle_x || (self.animstate_startframe == self.anim_melee_x && time - self.animstate_starttime >= 21/20)) // don't get stuck on idle animation in midair, nor melee after it finished
+                     {
+                         setanim(self, self.anim_jump, FALSE, TRUE, self.restart_jump);
+                         self.restart_jump = FALSE;
+                     }
+                 }
+                       }
                }
                else if (self.crouch)
                {
-                       if (self.movement_x * self.movement_x + self.movement_y * self.movement_y > 20)
+                       if (self.movement_x > 0 && self.movement_y == 0)
                                setanim(self, self.anim_duckwalk, TRUE, FALSE, FALSE);
+                       else if (self.movement_x < 0 && self.movement_y == 0)
+                               setanim(self, self.anim_duckwalkbackwards, TRUE, FALSE, FALSE);
+                       else if (self.movement_x == 0 && self.movement_y > 0)
+                               setanim(self, self.anim_duckwalkstraferight, TRUE, FALSE, FALSE);
+                       else if (self.movement_x == 0 && self.movement_y < 0)
+                               setanim(self, self.anim_duckwalkstrafeleft, TRUE, FALSE, FALSE);
+                       else if (self.movement_x > 0 && self.movement_y > 0)
+                               setanim(self, self.anim_duckwalkforwardright, TRUE, FALSE, FALSE);
+                       else if (self.movement_x > 0 && self.movement_y < 0)
+                               setanim(self, self.anim_duckwalkforwardleft, TRUE, FALSE, FALSE);
+                       else if (self.movement_x < 0 && self.movement_y > 0)
+                               setanim(self, self.anim_duckwalkbackright, TRUE, FALSE, FALSE);
+                       else if (self.movement_x < 0 && self.movement_y < 0)
+                               setanim(self, self.anim_duckwalkbackleft, TRUE, FALSE, FALSE);
                        else
                                setanim(self, self.anim_duckidle, TRUE, FALSE, FALSE);
                }
@@@ -326,7 -376,7 +376,7 @@@ void SpawnThrownWeapon (vector org, flo
  
  void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
-       local float take, save;
+       float take, save;
        vector v;
        Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
  
        self.dmg_take = self.dmg_take + take;//max(take - 10, 0);
        self.dmg_inflictor = inflictor;
  
-       if (self.health <= -autocvar_sv_gibhealth && self.modelindex != 0)
+       if (self.health <= -autocvar_sv_gibhealth && self.alpha >= 0)
        {
                // don't use any animations as a gib
                self.frame = 0;
-               self.dead_frame = 0;
                // view just above the floor
                self.view_ofs = '0 0 4';
  
                Violence_GibSplash(self, 1, 1, attacker);
-               self.modelindex = 0; // restore later
+               self.alpha = -1;
                self.solid = SOLID_NOT; // restore later
+               self.takedamage = DAMAGE_NO; // restore later
        }
  }
  
@@@ -382,12 -432,12 +432,12 @@@ void freezetag_CheckWinner()
  
  void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
-       local float take, save, waves, sdelay, dh, da, j;
+       float take, save, waves, sdelay, dh, da, j;
        vector v;
        float valid_damage_for_weaponstats;
        float excess;
  
-       if((g_arena && numspawned < 2) || (g_ca && ca_players < required_ca_players) && !inWarmupStage)
+       if((g_arena && numspawned < 2) || (g_ca && !ca_teams_ok) && !inWarmupStage)
                return;
  
        dh = max(self.health, 0);
                        self.armorvalue = self.armorvalue - save;
                        self.health = self.health - take;
                        // pause regeneration for 5 seconds
-                       self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
+                       if(take)
+                 self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
  
                        if (time > self.pain_finished)          //Don't switch pain sequences like crazy
                        {
                                if(sv_gentle < 1) {
                                        if(self.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models
                                        {
-                                               if (random() > 0.5)
-                                                       setanim(self, self.anim_pain1, FALSE, TRUE, TRUE);
-                                               else
-                                                       setanim(self, self.anim_pain2, FALSE, TRUE, TRUE);
+                                               if (!self.animstate_override)
+                                               {
+                                                       if (random() > 0.5)
+                                                               setanim(self, self.anim_pain1, FALSE, TRUE, TRUE);
+                                                       else
+                                                               setanim(self, self.anim_pain2, FALSE, TRUE, TRUE);
+                                               }
                                        }
  
                                        if(sound_allowed(MSG_BROADCAST, attacker))
                                }
  
                                // throw off bot aim temporarily
-                               local float shake;
+                               float shake;
                                shake = damage * 5 / (bound(0,skill,100) + 1);
                                self.v_angle_x = self.v_angle_x + (random() * 2 - 1) * shake;
                                self.v_angle_y = self.v_angle_y + (random() * 2 - 1) * shake;
                if(!g_freezetag)
                {
                        // become fully visible
-                       self.alpha = 1;
+                       self.alpha = default_player_alpha;
                        // throw a weapon
                        SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
                }
          float w;
          w = DEATH_WEAPONOF(deathtype);
          if(WEP_VALID(w))
-         if(self.classname == "player")
-         if(self != attacker)
+       if(accuracy_isgooddamage(attacker, self))
          attacker.accuracy.(accuracy_frags[w-1]) += 1;
  
                if(deathtype == DEATH_HURTTRIGGER && g_freezetag)
                MUTATOR_CALLHOOK(PlayerDies);
                weapon_action(self.weapon, WR_PLAYERDEATH);
  
+               RemoveGrapplingHook(self);
                if(self.flagcarried)
                {
 +                      // FIXCTF
                        if(attacker.classname != "player")
 -                              DropFlag(self.flagcarried, self, attacker); // penalty for flag loss by suicide
 +                              ctf_Handle_Drop(self); // penalty for flag loss by suicide
                        else if(attacker.team == self.team)
 -                              DropFlag(self.flagcarried, attacker, attacker); // penalty for flag loss by suicide/teamkill
 +                              ctf_Handle_Drop(self); // penalty for flag loss by suicide/teamkill
                        else
 -                              DropFlag(self.flagcarried, world, attacker);
 +                              ctf_Handle_Drop(self);
                }
                if(self.ballcarried && g_nexball)
                        DropBall(self.ballcarried, self.origin, self.velocity);
                if(!waves)
                        waves = autocvar_g_respawn_waves;
                if(waves)
-                       self.death_time = ceil((time + sdelay) / waves) * waves;
+                       self.respawn_time = ceil((time + sdelay) / waves) * waves;
                else
-                       self.death_time = time + sdelay;
-               if((sdelay + waves >= 5.0) && (self.death_time - time > 1.75))
+                       self.respawn_time = time + sdelay;
+               if((sdelay + waves >= 5.0) && (self.respawn_time - time > 1.75))
                        self.respawn_countdown = 10; // first number to count down from is 10
                else
                        self.respawn_countdown = -1; // do not count down
+               self.death_time = time;
                if (random() < 0.5)
-               {
                        setanim(self, self.anim_die1, FALSE, TRUE, TRUE);
-                       self.dead_frame = self.anim_dead1_x;
-               }
                else
-               {
                        setanim(self, self.anim_die2, FALSE, TRUE, TRUE);
-                       self.dead_frame = self.anim_dead2_x;
+               if (self.maxs_z > 5)
+               {
+                       self.maxs_z = 5;
+                       setsize(self, self.mins, self.maxs);
                }
                // set damage function to corpse damage
                self.event_damage = PlayerCorpseDamage;
@@@ -901,7 -955,7 +956,7 @@@ float Say(entity source, float teamsay
                                flood = 1;
                }
  
-               if (timeoutStatus == 2) //when game is paused, no flood protection
+               if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection
                        source.flood_field = flood = 0;
        }
  
        if(!privatesay)
        if(source.classname != "player")
        {
-               if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !inWarmupStage))
-                       teamsay = -1; // spectators
+               if not(intermission_running)
+                       if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !inWarmupStage))
+                               teamsay = -1; // spectators
        }
  
        if(flood)
  
        if(sourcemsgstr != "" && ret != 0)
        {
-               if(ret < 0) // fake
+               if(ret < 0) // faked message, because the player is muted
                {
                        sprint(source, sourcemsgstr);
                        if(sourcecmsgstr != "" && !privatesay)
                                centerprint(source, sourcecmsgstr);
                }
-               else if(privatesay)
+               else if(privatesay) // private message, between 2 people only, not sent to server console
                {
                        sprint(source, sourcemsgstr);
                        sprint(privatesay, msgstr);
                        if(cmsgstr != "")
                                centerprint(privatesay, cmsgstr);
                }
-               else if(teamsay > 0)
+               else if(teamsay > 0) // team message, only sent to team mates
                {
                        sprint(source, sourcemsgstr);
+                       //print(msgstr); // send to server console too
                        if(sourcecmsgstr != "")
                                centerprint(source, sourcecmsgstr);
                        FOR_EACH_REALPLAYER(head) if(head.team == source.team)
                                                centerprint(head, cmsgstr);
                                }
                }
-               else if(teamsay < 0)
+               else if(teamsay < 0) // spectator message, only sent to spectators
                {
                        sprint(source, sourcemsgstr);
+                       //print(msgstr); // send to server console too
                        FOR_EACH_REALCLIENT(head) if(head.classname != "player")
                                if(head != source)
                                        sprint(head, msgstr);
                }
-               else if(sourcemsgstr != msgstr)
+               else if(sourcemsgstr != msgstr) // trimmed/server fixed message, sent to all players
                {
                        sprint(source, sourcemsgstr);
+                       //print(msgstr); // send to server console too
                        FOR_EACH_REALCLIENT(head)
                                if(head != source)
                                        sprint(head, msgstr);
                }
                else
-                       bprint(msgstr);
+                       bprint(msgstr); // entirely normal message, sent to all players -- bprint sends to server console too.
        }
  
        return ret;
@@@ -1124,18 -1182,18 +1183,18 @@@ float LoadPlayerSounds(string f, float 
  }
  
  .float modelindex_for_playersound;
- .float skinindex_for_playersound;
+ .float skin_for_playersound;
  void UpdatePlayerSounds()
  {
        if(self.modelindex == self.modelindex_for_playersound)
-       if(self.skinindex == self.skinindex_for_playersound)
+       if(self.skin == self.skin_for_playersound)
                return;
        self.modelindex_for_playersound = self.modelindex;
-       self.skinindex_for_playersound = self.skinindex;
+       self.skin_for_playersound = self.skin;
        ClearPlayerSounds();
        LoadPlayerSounds("sound/player/default.sounds", 1);
        if(!autocvar_g_debug_defaultsounds)
-               if(!LoadPlayerSounds(get_model_datafilename(self.model, self.skinindex, "sounds"), 0))
+               if(!LoadPlayerSounds(get_model_datafilename(self.model, self.skin, "sounds"), 0))
                        LoadPlayerSounds(get_model_datafilename(self.model, 0, "sounds"), 0);
  }
  
diff --combined qcsrc/server/defs.qh
index 6d322acc5a4c7bf687bab4b37d1aa03258bc45ca,22d9e54d6c71d4c8ea4a007a157354eb23b7e6d4..4a0b5ad8cc253c7440c60faf006d65b76e179452
@@@ -1,6 -1,6 +1,6 @@@
  #define INDEPENDENT_ATTACK_FINISHED
  
- float require_spawnfunc_prefix; // if this float exists, only functions with spawnfunc_ name prefix qualify as spawn functions
noref float require_spawnfunc_prefix; // if this float exists, only functions with spawnfunc_ name prefix qualify as spawn functions
  
  #define BUTTON_ATCK       button0
  #define BUTTON_JUMP       button2
  
  // Globals
  
 -float ctf_score_value(string parameter);
 +float ctf_ReadScore(string parameter); // SOON WON'T BE NEEDED. // FIXCTF
  
- float g_dm, g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_ca, g_lms, g_runematch, g_race, g_nexball, g_cts, g_freezetag, g_keepaway;
  float g_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, g_midair, g_minstagib, g_pinata, g_norecoil, g_minstagib_invis_alpha, g_bloodloss;
  float g_warmup_limit;
  float g_warmup_allguns;
  float g_warmup_allow_timeout;
- float g_ctf_win_mode;
  float g_ctf_ignore_frags;
  float g_ctf_reverse;
  float g_race_qualifying;
  float inWarmupStage;
  float g_pickup_respawntime_weapon;
+ float g_pickup_respawntime_superweapon;
  float g_pickup_respawntime_ammo;
  float g_pickup_respawntime_short;
  float g_pickup_respawntime_medium;
  float g_pickup_respawntime_long;
  float g_pickup_respawntime_powerup;
  float g_pickup_respawntimejitter_weapon;
+ float g_pickup_respawntimejitter_superweapon;
  float g_pickup_respawntimejitter_ammo;
  float g_pickup_respawntimejitter_short;
  float g_pickup_respawntimejitter_medium;
@@@ -91,9 -91,8 +91,8 @@@ float maxclients
  .float  crouch;       // Crouching or not?
  
  .float        strength_finished;
- //.float      speed_finished;
  .float        invincible_finished;
//.float      slowmo_finished;
.float        superweapons_finished;
  
  .vector               finaldest, finalangle;          //plat.qc stuff
  .void()               think1;
  //.float cnt2;
  
  .float play_time;
+ .float respawn_time;
  .float death_time;
- .float dead_frame;
  .float fade_time;
  .float fade_rate;
  
  .vector anim_die1; // player dies
  .vector anim_die2; // player dies differently
  .vector anim_draw; // player pulls out a weapon
- .vector anim_duck; // player crouches (from idle to duckidle)
// .vector anim_duck; // player crouches (from idle to duckidle)
  .vector anim_duckwalk; // player walking while crouching
  .vector anim_duckjump; // player jumping from a crouch
  .vector anim_duckidle; // player idling while crouching
  .vector anim_runbackwards; // player running backward
  .vector anim_strafeleft; // player shuffling left quickly
  .vector anim_straferight; // player shuffling right quickly
- .vector anim_dead1; // player dead (must be identical to last frame of die1)
- .vector anim_dead2; // player dead (must be identical to last frame of die2)
//.vector anim_dead1; // player dead (must be identical to last frame of die1)
//.vector anim_dead2; // player dead (must be identical to last frame of die2)
  .vector anim_forwardright; // player running forward and right
  .vector anim_forwardleft; // player running forward and left
  .vector anim_backright; // player running backward and right
  .vector anim_backleft; // player running back and left
  .vector anim_melee; // player doing the melee action
+ .vector anim_duck; // player doing the melee action
+ .vector anim_duckwalkbackwards;
+ .vector anim_duckwalkstrafeleft;
+ .vector anim_duckwalkstraferight;
+ .vector anim_duckwalkforwardright;
+ .vector anim_duckwalkforwardleft;
+ .vector anim_duckwalkbackright;
+ .vector anim_duckwalkbackleft;
  
  // weapon animation vectors:
  .vector anim_fire1;
@@@ -171,6 -178,9 +178,9 @@@ void setanim(entity e, vector anim, flo
  //.float      chasecam;
  
  .float        damageforcescale;
+ #define MIN_DAMAGEEXTRARADIUS 2
+ #define MAX_DAMAGEEXTRARADIUS 16
+ .float damageextraradius;
  
  //.float          gravity;
  
  
  .float watersound_finished;
  .float iscreature;
+ .float damagedbycontents;
  .vector oldvelocity;
  
  .float pauseregen_finished;
  .entity weaponentity;
  .entity exteriorweaponentity;
  .vector weaponentity_glowmod;
- .float switchweapon;
+ //.float weapon; // current weapon
+ .float switchweapon; // weapon requested to switch to
+ .float switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible)
+ .string weaponname; // name of .weapon
  .float autoswitch;
  float weapon_action(float wpn, float wrequest);
  float client_hasweapon(entity cl, float wpn, float andammo, float complain);
@@@ -210,7 -226,6 +226,6 @@@ void w_clear()
  void w_ready();
  // VorteX: standalone think for weapons, so normal think on weaponentity can be reserved by weaponflashes (which needs update even player dies)
  .float weapon_nextthink;
- .float weapon_forbidchange;
  .void() weapon_think;
  
  //float       PLAYER_WEAPONSELECTION_DELAY = );
@@@ -231,7 -246,6 +246,6 @@@ void weapon_defaultspawnfunc(float wpn)
  
  string w_deathtypestring;
  
- void(entity client, string s) centerprint_builtin = #73;
  .vector dest1, dest2;
  
  float gameover;
@@@ -242,12 -256,14 +256,14 @@@ float alreadychangedlevel
  
  .float runes;
  
+ // Keys player is holding
+ .float itemkeys;
+ // message delay for func_door locked by keys and key locks
+ // this field is used on player entities
+ .float key_door_messagetime;
  
- .float welcomemessage_time;
- .float version;
  
- // minstagib vars
- .float jump_interval;    // laser refire
+ .float version;
  
  //swamp
  .float in_swamp;              // bool
  // footstep interval
  .float nextstep;
  
- .float ready;
- #define RESTART_COUNTDOWN 10
- float restart_mapalreadyrestarted; //bool, indicates whether reset_map() was already executed
- entity restartTimer;
- void restartTimer_Think();
  float blockSpectators; //if set, new or existing spectators or observers will be removed unless they become a player within g_maxplayers_spectator_blocktime seconds
  .float spectatortime; //point in time since the client is spectating or observing
  void checkSpectatorBlock();
  float nJoinAllowed(float includeMe);
  #define PREVENT_JOIN_TEXT "^1You may not join the game at this time.\n\nThe player limit reached maximum capacity."
  
- //sv_timeout: pauses the game by setting the gamespeed to a really low value (see TIMEOUT_SLOWMO_VALUE)
- #define TIMEOUT_SLOWMO_VALUE 0.0001
- float sys_frametime; // gets initialised in worlspawn, saves the value from autocvar_sys_ticrate
- float remainingTimeoutTime; // contains the time in seconds that the active timeout has left
- float remainingLeadTime; // contains the number of seconds left of the leadtime (before the timeout starts)
- float timeoutStatus; // (values: 0, 1, 2) contains whether a timeout is not active (0), was called but still at leadtime (1) or is active (2)
- .float allowedTimeouts; // contains the number of allowed timeouts for each player
- entity timeoutInitiator; // contains the entity of the player who started the last timeout
- float orig_slowmo; // contains the value of autocvar_slowmo so that, after timeout finished, it isn't set to slowmo 1 necessarily
- .vector lastV_angle; //used when pausing the game in order to force the player to keep his old view angle fixed
- entity timeoutHandler; //responsible for centerprinting the timeout countdowns and playing sounds
- void timeoutHandler_Think();
- void evaluateTimeout();
- void evaluateTimein();
- string getTimeoutText(float addOneSecond);
  .float spawnshieldtime;
  
  .float lms_nextcheck;
@@@ -311,8 -306,11 +306,11 @@@ float default_weapon_alpha
  
  .float() customizeentityforclient;
  .float cvar_cl_handicap;
- .float cvar_cl_playerdetailreduction;
- .float cvar_scr_centertime;
+ .float cvar_cl_clippedspectating;
+ .float cvar_cl_autoscreenshot;
+ .float cvar_cl_movement_track_canjump;
+ .float cvar_cl_newusekeysupported;
  .string cvar_g_xonoticversion;
  .string cvar_cl_weaponpriority;
  .string cvar_cl_weaponpriorities[10];
  .float cvar_cl_forceplayermodelsfromxonotic;
  float sv_clforceplayermodels;
  #endif
- float sv_loddistance1;
- float sv_loddistance2;
  .float cvar_cl_gunalign;
  .float cvar_cl_noantilag;
  
@@@ -337,12 -333,6 +333,6 @@@ void AnnounceTo(entity e, string snd)
  
  .float version_nagtime;
  
- .float modelindex_lod0;
- .float modelindex_lod0_from_xonotic;
- .float skinindex;
- .float modelindex_lod1;
- .float modelindex_lod2;
  #define NUM_JUMPPADSUSED 3
  .float jumppadcount;
  .entity jumppadsused[NUM_JUMPPADSUSED];
@@@ -351,7 -341,7 +341,7 @@@ string gamemode_name
  
  float startitem_failed;
  
 -void DropFlag(entity flag, entity penalty_receiver, entity attacker);
 +void ctf_Handle_Drop(entity player); // FIXCTF
  void DropBall(entity ball, vector org, vector vel);
  void DropAllRunes(entity pl);
  
@@@ -368,10 -358,6 +358,6 @@@ void FixClientCvars(entity e)
  
  float weaponsInMap;
  
- void centerprint_atprio(entity e, float prio, string s);
- void centerprint_expire(entity e, float prio);
- void centerprint(entity e, string s);
  .float respawn_countdown; // next number to count
  
  float bot_waypoints_for_items;
@@@ -391,9 -377,6 +377,6 @@@ float assault_attacker_team
  // speedrun: when 1, player auto teleports back when capture timeout happens
  .float speedrunning;
  
- // Q3 support
- float q3acompat_machineshotgunswap;
  // database
  float ServerProgsDB;
  float TemporaryDB;
@@@ -416,7 -399,6 +399,6 @@@ float lockteams
  float sv_maxidle;
  float sv_maxidle_spectatorsareidle;
  
- float sv_pogostick;
  float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end);
  
  float next_pingtime;
@@@ -502,6 -484,7 +484,7 @@@ float GetPlayerSoundSampleField_notFoun
  .float version_mismatch;
  
  float independent_players;
+ #define INDEPENDENT_PLAYERS (autocvar__independent_players ? (autocvar__independent_players > 0) : independent_players)
  #define IS_INDEPENDENT_PLAYER(e) ((e).solid == SOLID_TRIGGER)
  #define MAKE_INDEPENDENT_PLAYER(e) (((e).solid = SOLID_TRIGGER) + ((e).frags = FRAGS_PLAYER_NONSOLID))
  // we're using + here instead of , because fteqcc sucks
@@@ -612,6 -595,8 +595,8 @@@ float client_cefc_accumulatortime
  .float clip_size;
  .float minelayer_mines;
  
+ .float grab; // 0 = can't grab, 1 = owner can grab, 2 = owner and team mates can grab, 3 = anyone can grab
  #define PROJECTILE_MAKETRIGGER(e) (e).solid = SOLID_CORPSE; (e).dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE
  // when doing this, hagar can go through clones
  // #define PROJECTILE_MAKETRIGGER(e) (e).solid = SOLID_BBOX
@@@ -664,3 -649,8 +649,8 @@@ float serverflags
  .float misc_bulletcounter;    // replaces uzi & hlac bullet counter.
  
  void PlayerUseKey();
+ typedef vector(entity player, entity spot, vector current) spawn_evalfunc_t;
+ .spawn_evalfunc_t spawn_evalfunc;
+ .entity conveyor;
diff --combined qcsrc/server/g_damage.qc
index 285493b2a1bfae72edc7d9d46fa8a0034f1aeecd,bdaeef0a53d19d3ed48ae81cf3468b560350ffb4..db23f49db20fcc7cabee39fa95d7dcb0868557b7
@@@ -14,10 -14,11 +14,11 @@@ float Damage_DamageInfo_SendEntity(enti
        WriteByte(MSG_ENTITY, bound(0, self.dmg_radius, 255));
        WriteByte(MSG_ENTITY, bound(1, self.dmg_edge, 255));
        WriteShort(MSG_ENTITY, self.oldorigin_x);
+       WriteByte(MSG_ENTITY, self.species);
        return TRUE;
  }
  
- void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, float deathtype, entity dmgowner)
+ void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, float deathtype, float bloodtype, entity dmgowner)
  {
        // TODO maybe call this from non-edgedamage too?
        // TODO maybe make the client do the particle effects for the weapons and the impact sounds using this info?
        e.dmg_radius = rad;
        e.dmg_force = vlen(force);
        e.velocity = force;
        e.oldorigin_x = compressShortVector(e.velocity);
+       e.species = bloodtype;
  
        Net_LinkEntity(e, FALSE, 0.2, Damage_DamageInfo_SendEntity);
  }
  
- #define DAMAGE_CENTERPRINT_SPACER NEWLINES
  float checkrules_firstblood;
  
  float yoda;
@@@ -126,6 -125,8 +125,8 @@@ void GiveFrags (entity attacker, entit
        {
                // regular frag
                PlayerScore_Add(attacker, SP_KILLS, 1);
+               if(targ.playerid)
+                       PlayerStats_Event(attacker, sprintf("kills-%d", targ.playerid), 1);
        }
  
        PlayerScore_Add(targ, SP_DEATHS, 1);
                        }
                        f = 0;
                }
 -              else if(g_ctf)
 +              else if(g_ctf) // FIXCTF
                {
                        if(g_ctf_ignore_frags)
                                f = 0;
                UpdateFrags(attacker, f);
  }
  
+ string Obituary_ExtraFragInfo(entity player) // Extra fragmessage information
+ {
+       string health_output;
+       string ping_output;
+       string handicap_output;
+       string output;
+       if(autocvar_sv_fraginfo && ((autocvar_sv_fraginfo == 2) || inWarmupStage))
+       {
+               // health/armor of attacker (person who killed you)
+               if(autocvar_sv_fraginfo_stats && (player.health >= 1))
+                       health_output = strcat("^7(Health ^1", ftos(rint(player.health)), "^7 / Armor ^2", ftos(rint(player.armorvalue)), "^7)");
+               
+               // ping display
+               if(autocvar_sv_fraginfo_ping)
+                       ping_output = ((clienttype(player) == CLIENTTYPE_BOT) ? "^2Bot" : strcat("Ping ", ((player.ping >= 150) ? "^1" : "^2"), ftos(rint(player.ping)), "ms"));
+                       
+               // handicap display 
+               if(autocvar_sv_fraginfo_handicap) 
+               {
+                       if(autocvar_sv_fraginfo_handicap == 2)  
+                               handicap_output = strcat(output, strcat("Handicap ^2", ((player.cvar_cl_handicap <= 1) ? "Off" : ftos(rint(player.cvar_cl_handicap)))));
+                       else if(player.cvar_cl_handicap) // with _handicap 1, only show this if there actually is a handicap enabled.   
+                               handicap_output = strcat("Handicap ^2", ftos(rint(player.cvar_cl_handicap)));
+               }
+               
+               // format the string
+               output = strcat(health_output, (health_output ? ((ping_output || handicap_output) ? " ^7(" : "") : ((ping_output || handicap_output) ? "^7(" : "")), 
+                       ping_output, (handicap_output ? "^7 / " : ""), 
+                       handicap_output, ((ping_output || handicap_output) ? "^7)" : ""));
+               
+               // add new line to the beginning if there is a message
+               if(output) { output = strcat("\n", output); }
+       }
+       
+       return output;
+ }
  string AppendItemcodes(string s, entity player)
  {
        float w;
@@@ -267,8 -306,7 +306,7 @@@ void LogDeath(string mode, float deatht
  void Send_KillNotification (string s1, string s2, string s3, float msg, float type)
  {
        WriteByte(MSG_ALL, SVC_TEMPENTITY);
-       WriteByte(MSG_ALL, TE_CSQC_NOTIFY);
-       WriteByte(MSG_ALL, CSQC_KILLNOTIFY);
+       WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
        WriteString(MSG_ALL, s1);
        WriteString(MSG_ALL, s2);
        WriteString(MSG_ALL, s3);
  }
  
  // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
- void Send_CSQC_Centerprint(entity e, string s1, string s2, float msg, float type)
+ void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
  {
        if (clienttype(e) == CLIENTTYPE_REAL)
        {
                msg_entity = e;
                WRITESPECTATABLE_MSG_ONE({
                        WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_NOTIFY);
-                       WriteByte(MSG_ONE, CSQC_CENTERPRINT);
+                       WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
                        WriteString(MSG_ONE, s1);
                        WriteString(MSG_ONE, s2);
                        WriteShort(MSG_ONE, msg);
@@@ -309,7 -346,7 +346,7 @@@ void Obituary (entity attacker, entity 
                        if (deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE)
                                msg = ColoredTeamName(targ.team); // TODO: check if needed?
              if(!g_cts) // no "killed your own dumb self" message in CTS
-                 Send_CSQC_Centerprint(targ, msg, "", deathtype, MSG_SUICIDE);
+                 Send_CSQC_KillCenterprint(targ, msg, "", deathtype, MSG_SUICIDE);
  
                        if(deathtype != DEATH_TEAMCHANGE && deathtype != DEATH_QUIET)
                        {
                }
                else if (attacker.classname == "player")
                {
-                       if(teamplay && attacker.team == targ.team)
+                       if(!IsDifferentTeam(attacker, targ))
                        {
                                if(attacker.team == COLOR_TEAM1)
                                        type = KILL_TEAM_RED;
  
                                GiveFrags(attacker, targ, -1, deathtype);
  
-                               Send_CSQC_Centerprint(attacker, s, "", type, MSG_KILL);
+                               Send_CSQC_KillCenterprint(attacker, s, "", type, MSG_KILL);
  
                                if (targ.killcount > 2) {
                                        msg = ftos(targ.killcount);
                                        checkrules_firstblood = TRUE;
                                        Send_KillNotification(a, "", "", KILL_FIRST_BLOOD, MSG_KILL);
                                        // TODO: make these print a newline if they dont
-                                       Send_CSQC_Centerprint(attacker, "", "", KILL_FIRST_BLOOD, MSG_KILL);
-                                       Send_CSQC_Centerprint(targ, "", "", KILL_FIRST_VICTIM, MSG_KILL);
+                                       Send_CSQC_KillCenterprint(attacker, "", "", KILL_FIRST_BLOOD, MSG_KILL);
+                                       Send_CSQC_KillCenterprint(targ, "", "", KILL_FIRST_VICTIM, MSG_KILL);
                                        PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
                                        PlayerStats_Event(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
                                }
  
-                               if((autocvar_sv_fragmessage_information_typefrag) && (targ.BUTTON_CHAT)) {
-                                       Send_CSQC_Centerprint(attacker, s, GetAdvancedDeathReports(targ), KILL_TYPEFRAG, MSG_KILL);
-                                       Send_CSQC_Centerprint(targ, a, GetAdvancedDeathReports(attacker), KILL_TYPEFRAGGED, MSG_KILL);
+                               if(targ.BUTTON_CHAT) {
+                                       Send_CSQC_KillCenterprint(attacker, s, Obituary_ExtraFragInfo(targ), KILL_TYPEFRAG, MSG_KILL);
+                                       Send_CSQC_KillCenterprint(targ, a, Obituary_ExtraFragInfo(attacker), KILL_TYPEFRAGGED, MSG_KILL);
                                } else {
-                                       Send_CSQC_Centerprint(attacker, s, GetAdvancedDeathReports(targ), KILL_FRAG, MSG_KILL);
-                                       Send_CSQC_Centerprint(targ, a, GetAdvancedDeathReports(attacker), KILL_FRAGGED, MSG_KILL);
+                                       Send_CSQC_KillCenterprint(attacker, s, Obituary_ExtraFragInfo(targ), KILL_FRAG, MSG_KILL);
+                                       Send_CSQC_KillCenterprint(targ, a, Obituary_ExtraFragInfo(attacker), KILL_FRAGGED, MSG_KILL);
                                }
  
                                attacker.taunt_soundtime = time + 1;
  
                                if(g_ctf && targ.flagcarried)
                                {
 -                                      UpdateFrags(attacker, ctf_score_value("score_kill"));
 +                                      UpdateFrags(attacker, ctf_ReadScore("score_kill")); // FIXCTF
                                        PlayerScore_Add(attacker, SP_CTF_FCKILLS, 1);
                                        GiveFrags(attacker, targ, 0, deathtype); // for logging
                                }
                }
                else
                {
-                       Send_CSQC_Centerprint(targ, "", "", deathtype, MSG_KILL_ACTION);
+                       Send_CSQC_KillCenterprint(targ, "", "", deathtype, MSG_KILL_ACTION);
                        if (deathtype == DEATH_HURTTRIGGER && inflictor.message != "")
                                msg = inflictor.message;
                        else if (deathtype == DEATH_CUSTOM)
@@@ -503,7 -540,7 +540,7 @@@ void Damage (entity targ, entity inflic
        if (gameover || targ.killcount == -666)
                return;
  
-       local entity oldself;
+       entity oldself;
        oldself = self;
        self = targ;
          damage_targ = targ;
        }
        else
        {
+               /*
+               skill based bot damage? gtfo. (tZork)
                if (targ.classname == "player")
                if (attacker.classname == "player")
                if (!targ.isbot)
                if (attacker.isbot)
                        damage = damage * bound(0.1, (skill + 5) * 0.1, 1);
+         */
+         
                // nullify damage if teamplay is on
                if(deathtype != DEATH_TELEFRAG)
                if(attacker.classname == "player")
                                damage = 0;
                                force = '0 0 0';
                        }
-                       else if(teamplay && attacker.team == targ.team)
+                       else if(!IsDifferentTeam(attacker, targ))
                        {
                                if(autocvar_teamplay_mode == 1)
                                        damage = 0;
                                                        {
                                                                vector v;
                                                                v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage);
+                                                               v_z = 0; // fteqcc sucks
                                                                attacker.dmg_take += v_x;
                                                                attacker.dmg_save += v_y;
                                                                attacker.dmg_inflictor = inflictor;
                                                        {
                                                                vector v;
                                                                v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, damage);
+                                                               v_z = 0; // fteqcc sucks
                                                                targ.dmg_take += v_x;
                                                                targ.dmg_save += v_y;
                                                                targ.dmg_inflictor = inflictor;
                                                                damage = 0;
-                                 if(!autocvar_g_friendlyfire_virtual_force)
-                                     force = '0 0 0';
+                                                               if(!autocvar_g_friendlyfire_virtual_force)
+                                                                       force = '0 0 0';
                                                        }
                                                }
                                                else
                        if (targ.armorvalue && (deathtype == WEP_MINSTANEX) && damage)
                        {
                                targ.armorvalue -= 1;
-                               centerprint(targ, strcat(DAMAGE_CENTERPRINT_SPACER, "^3Remaining extra lives: ",ftos(targ.armorvalue)));
+                               centerprint(targ, strcat("^3Remaining extra lives: ",ftos(targ.armorvalue)));
                                damage = 0;
                                targ.hitsound += 1;
                                attacker.hitsound += 1; // TODO change this to a future specific hitsound for armor hit
                                if (targ != attacker)
                                {
                                        if ((targ.health >= 1) && (targ.classname == "player"))
-                                               centerprint(attacker, strcat(DAMAGE_CENTERPRINT_SPACER, "Secondary fire inflicts no damage!"));
+                                               centerprint(attacker, "Secondary fire inflicts no damage!");
                                        force = '0 0 0';
                                        // keep mirrorforce
                                        attacker = targ;
                                damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
                }
  
 -              // CTF: reduce damage/force
 -              if(g_ctf)
 -              if(targ == attacker)
 -              if(targ.flagcarried)
 -              {
 -                      damage = damage * autocvar_g_ctf_flagcarrier_selfdamage;
 -                      force = force * autocvar_g_ctf_flagcarrier_selfforce;
 -              }
 -
                if(g_runematch)
                {
                        // apply strength rune
                if(targ.takedamage == DAMAGE_AIM)
                if(targ != attacker)
                {
-                       if(targ.classname == "player")
+                       if(damage_headshotbonus > 0)
                        {
-                               // HEAD SHOT:
-                               // find height of hit on player axis
-                               // if above view_ofs and below maxs, and also in the middle half of the bbox, it is head shot
-                               vector headmins, headmaxs, org;
-                               org = antilag_takebackorigin(targ, time - ANTILAG_LATENCY(attacker));
-                               headmins = org + GetHeadshotMins(targ);
-                               headmaxs = org + GetHeadshotMaxs(targ);
-                               if(trace_hits_box(railgun_start, railgun_end, headmins, headmaxs))
+                               if(targ.classname == "player")
+                               {
+                                       // HEAD SHOT:
+                                       // find height of hit on player axis
+                                       // if above view_ofs and below maxs, and also in the middle half of the bbox, it is head shot
+                                       vector headmins, headmaxs, org;
+                                       org = antilag_takebackorigin(targ, time - ANTILAG_LATENCY(attacker));
+                                       headmins = org + GetHeadshotMins(targ);
+                                       headmaxs = org + GetHeadshotMaxs(targ);
+                                       if(trace_hits_box(railgun_start, railgun_end, headmins, headmaxs))
+                                       {
+                                               deathtype |= HITTYPE_HEADSHOT;
+                                       }
+                               }
+                               else if(targ.classname == "turret_head")
                                {
                                        deathtype |= HITTYPE_HEADSHOT;
                                }
+                               if(deathtype & HITTYPE_HEADSHOT)
+                                       damage *= 1 + damage_headshotbonus;
                        }
-                       else if(targ.classname == "turret_head")
-                       {
-                               deathtype |= HITTYPE_HEADSHOT;
-                       }
-                       if(deathtype & HITTYPE_HEADSHOT)
-                               damage *= 1 + damage_headshotbonus;
  
                        entity victim;
                        if((targ.vehicle_flags & VHF_ISVEHICLE) && targ.owner)
                        if(attacker.armorvalue > 0)
                        {
                                attacker.armorvalue = attacker.armorvalue - 1;
-                               centerprint(attacker, strcat(DAMAGE_CENTERPRINT_SPACER, "^3Remaining extra lives: ",ftos(attacker.armorvalue)));
+                               centerprint(attacker, strcat("^3Remaining extra lives: ",ftos(attacker.armorvalue)));
                                attacker.hitsound += 1;
                        }
                        mirrordamage = 0;
@@@ -943,14 -997,14 +988,14 @@@ float RadiusDamage (entity inflictor, e
                else
                        force = normalize(force);
                if(forceintensity >= 0)
-                       Damage_DamageInfo(blastorigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, attacker);
+                       Damage_DamageInfo(blastorigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
                else
-                       Damage_DamageInfo(blastorigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, attacker);
+                       Damage_DamageInfo(blastorigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
        }
  
        stat_damagedone = 0;
  
-       targ = WarpZone_FindRadius (blastorigin, rad, FALSE);
+       targ = WarpZone_FindRadius (blastorigin, rad + MAX_DAMAGEEXTRARADIUS, FALSE);
        while (targ)
        {
                next = targ.chain;
                                diff = targ.WarpZone_findradius_dist;
                                // round up a little on the damage to ensure full damage on impacts
                                // and turn the distance into a fraction of the radius
-                               power = 1 - ((vlen (diff) - 2) / rad);
+                               power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
                                //bprint(" ");
                                //bprint(ftos(power));
                                //if (targ == attacker)
                                        finaldmg = coredamage * power + edgedamage * (1 - power);
                                        if (finaldmg > 0)
                                        {
-                                               local float a;
-                                               local float c;
-                                               local float hits;
-                                               local float total;
-                                               local float hitratio;
-                                               local vector hitloc;
-                                               local vector myblastorigin;
+                                               float a;
+                                               float c;
+                                               float hits;
+                                               float total;
+                                               float hitratio;
+                                               vector hitloc;
+                                               vector myblastorigin;
                                                myblastorigin = WarpZone_TransformOrigin(targ, blastorigin);
                                                center = targ.origin + (targ.mins + targ.maxs) * 0.5;
                                                // if it's a player, use the view origin as reference
index e50431938e2b5f889e8ed972acff675eeee0d2f3,0000000000000000000000000000000000000000..3140822103815ec783fea3f9d3874ee00cebcce9
mode 100644,000000..100644
--- /dev/null
@@@ -1,881 -1,0 +1,881 @@@
-       if(g_ctf_win_mode != 2)
 +// ================================================================
 +//  Official capture the flag game mode coding, reworked by Samual
 +//  Last updated: March 28th, 2011
 +// ================================================================
 +
 +// Flag constants 
 +#define FLAG_MIN (PL_MIN + '0 0 -13')
 +#define FLAG_MAX (PL_MAX + '0 0 -13')
 +#define FLAG_CARRY_POS '-15 0 7'
 +
 +.entity bot_basewaypoint; // flag waypointsprite
 +.entity wps_flagbase; 
 +.entity wps_flagcarrier;
 +.entity wps_flagdropped;
 +
 +entity ctf_worldflaglist; // CTF flags in the map
 +.entity ctf_worldflagnext;
 +
 +.vector ctf_spawnorigin; // stored vector for where the flag is placed on the map itself. 
 +
 +float ctf_captimerecord; // record time for capturing the flag
 +.float ctf_pickuptime;
 +.float ctf_pickupid;
 +.float ctf_dropperid; // don't allow spam of dropping the flag
 +.float ctf_droptime;
 +.float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
 +
 +
 +.float next_take_time; // Delay between when the person can pick up a flag // is this obsolete from the stuff above?
 +
 +// CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag.
 +.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
 +float ctf_captureshield_min_negscore; // punish at -20 points
 +float ctf_captureshield_max_ratio; // punish at most 30% of each team
 +float ctf_captureshield_force; // push force of the shield
 +
 +// after game mode is finished, these will be changed to use #define with other entities so as to not create many more.
 +
 +// ==================
 +// Misc CTF functions
 +// ==================
 +
 +float ctf_ReadScore(string parameter) // make this obsolete
 +{
-       else
-               return cvar(strcat("g_ctf_flag", parameter));
++      //if(g_ctf_win_mode != 2)
 +              return cvar(strcat("g_ctf_personal", parameter));
-                       centerprint_atprio(player, CENTERPRIO_SHIELDING, "^3You are now ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Make some defensive scores before trying again.");
++      //else
++      //      return cvar(strcat("g_ctf_flag", parameter));
 +}
 +
 +void ctf_FakeTimeLimit(entity e, float t)
 +{
 +      msg_entity = e;
 +      WriteByte(MSG_ONE, 3); // svc_updatestat
 +      WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
 +      if(t < 0)
 +              WriteCoord(MSG_ONE, autocvar_timelimit);
 +      else
 +              WriteCoord(MSG_ONE, (t + 1) / 60);
 +}
 +
 +void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later
 +{
 +      if(autocvar_sv_eventlog)
 +              GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
 +}
 +
 +
 +// =======================
 +// CaptureShield Functions 
 +// =======================
 +
 +float ctf_CaptureShield_CheckStatus(entity p) 
 +{
 +      float s, se;
 +      entity e;
 +      float players_worseeq, players_total;
 +
 +      if(ctf_captureshield_max_ratio <= 0)
 +              return FALSE;
 +
 +      s = PlayerScore_Add(p, SP_SCORE, 0);
 +      if(s >= -ctf_captureshield_min_negscore)
 +              return FALSE;
 +
 +      players_total = players_worseeq = 0;
 +      FOR_EACH_PLAYER(e)
 +      {
 +              if(e.team != p.team)
 +                      continue;
 +              se = PlayerScore_Add(e, SP_SCORE, 0);
 +              if(se <= s)
 +                      ++players_worseeq;
 +              ++players_total;
 +      }
 +
 +      // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
 +      // use this rule here
 +      
 +      if(players_worseeq >= players_total * ctf_captureshield_max_ratio)
 +              return FALSE;
 +
 +      return TRUE;
 +}
 +
 +void ctf_CaptureShield_Update(entity player, float wanted_status)
 +{
 +      float updated_status = ctf_CaptureShield_CheckStatus(player);
 +      if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only
 +      {
 +              if(updated_status) // TODO csqc notifier for this // Samual: How?
-                       centerprint_atprio(player, CENTERPRIO_SHIELDING, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.");
++                      Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Make some defensive scores before trying again.", 5, 0);
 +              else
-       centerprint_atprio(other, CENTERPRIO_SHIELDING, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.");
++                      Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0);
 +                      
 +              player.ctf_captureshielded = updated_status;
 +      }
 +}
 +
 +float ctf_CaptureShield_Customize()
 +{
 +      if not(other.ctf_captureshielded)
 +              return FALSE;
 +      if(self.team == other.team)
 +              return FALSE;
 +      return TRUE;
 +}
 +
 +void ctf_CaptureShield_Touch()
 +{
 +      if not(other.ctf_captureshielded)
 +              return;
 +      if(self.team == other.team)
 +              return;
 +      vector mymid;
 +      vector othermid;
 +      mymid = (self.absmin + self.absmax) * 0.5;
 +      othermid = (other.absmin + other.absmax) * 0.5;
 +      Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
-       g_ctf_win_mode = cvar("g_ctf_win_mode");
++      Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
 +}
 +
 +void ctf_CaptureShield_Spawn(entity flag)
 +{
 +      entity e;
 +      e = spawn();
 +      e.enemy = self;
 +      e.team = self.team;
 +      e.touch = ctf_CaptureShield_Touch;
 +      e.customizeentityforclient = ctf_CaptureShield_Customize;
 +      e.classname = "ctf_captureshield";
 +      e.effects = EF_ADDITIVE;
 +      e.movetype = MOVETYPE_NOCLIP;
 +      e.solid = SOLID_TRIGGER;
 +      e.avelocity = '7 0 11';
 +      setorigin(e, self.origin);
 +      setmodel(e, "models/ctf/shield.md3");
 +      e.scale = 0.5;
 +      setsize(e, e.scale * e.mins, e.scale * e.maxs);
 +}
 +
 +
 +// ==============
 +// Event Handlers
 +// ==============
 +
 +void ctf_Handle_Drop(entity player)
 +{
 +      entity flag = player.flagcarried;
 +
 +      if(!flag) { return; }
 +      if(flag.speedrunning) { ctf_RespawnFlag(flag); return; }
 +      
 +      // reset the flag
 +      setattachment(flag, world, "");
 +      setorigin(flag, player.origin - '0 0 24' + '0 0 37');
 +      flag.owner.flagcarried = world;
 +      flag.owner = world;
 +      flag.movetype = MOVETYPE_TOSS;
 +      flag.solid = SOLID_TRIGGER;
 +      flag.takedamage = DAMAGE_YES;
 +      flag.velocity = ('0 0 200' + ('0 100 0' * crandom()) + ('100 0 0' * crandom()));
 +      flag.pain_finished = time + autocvar_g_ctf_flag_returntime; // replace this later
 +      
 +      flag.ctf_droptime = time;
 +      flag.ctf_dropperid = player.playerid;
 +      flag.ctf_status = FLAG_DROPPED;
 +
 +      // messages and sounds
 +      Send_KillNotification(player.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO);
 +      sound(flag, CH_TRIGGER, flag.noise4, VOL_BASE, ATTN_NONE);
 +      ctf_EventLog("dropped", player.team, player);
 +      
 +      // scoring
 +      PlayerTeamScore_AddScore(player, -ctf_ReadScore("penalty_drop"));       
 +      PlayerScore_Add(player, SP_CTF_DROPS, 1);
 +
 +      // waypoints
 +      WaypointSprite_Spawn("flagdropped", 0, 0, flag, '0 0 64', world, player.team, flag, wps_flagdropped, FALSE, RADARICON_FLAG, '0 1 1'); // (COLOR_TEAM1 + COLOR_TEAM2 - flag.team)
 +      WaypointSprite_Ping(player.wps_flagcarrier);
 +      WaypointSprite_Kill(player.wps_flagcarrier);
 +
 +      // captureshield
 +      ctf_CaptureShield_Update(player, 0); // shield only
 +
 +      // check if the flag will fall off the map
 +      trace_startsolid = FALSE;
 +      tracebox(flag.origin, flag.mins, flag.maxs, flag.origin, TRUE, flag);
 +      if(trace_startsolid)
 +              dprint("FLAG FALLTHROUGH will happen SOON\n");
 +}
 +
 +void ctf_Handle_Capture(entity flag, entity player)
 +{
 +      // declarations
 +      float cap_time, cap_record, success;
 +      string cap_message, refername;
 +
 +      // records
 +      if((autocvar_g_ctf_captimerecord_always) || (player_count - currentbots)) {
 +              cap_record = ctf_captimerecord;
 +              cap_time = (time - player.flagcarried.ctf_pickuptime);
 +
 +              refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
 +              refername = ((refername == player.netname) ? "their" : strcat(refername, "^7's"));
 +
 +              if(!ctf_captimerecord) 
 +                      { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds"); success = TRUE; }
 +              else if(cap_time < cap_record) 
 +                      { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, breaking ", refername, " previous record of ", ftos_decimals(cap_record, 2), " seconds"); success = TRUE; }
 +              else
 +                      { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, failing to break ", refername, " record of ", ftos_decimals(cap_record, 2), " seconds"); success = FALSE; }
 +
 +              if(success) {
 +                      ctf_captimerecord = cap_time;
 +                      db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
 +                      db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
 +                      write_recordmarker(player, (time - cap_time), cap_time); } }
 +      
 +      // messages and sounds
 +      Send_KillNotification(player.netname, player.flagcarried.netname, cap_message, INFO_CAPTUREFLAG, MSG_INFO);
 +      sound(player, CH_TRIGGER, flag.noise2, VOL_BASE, ATTN_NONE); // "ctf/*_capture.wav"
 +      ctf_EventLog("capture", player.flagcarried.team, player);
 +      
 +      // scoring
 +      PlayerTeamScore_AddScore(player, ctf_ReadScore("score_capture"));
 +      PlayerTeamScore_Add(player, SP_CTF_CAPS, ST_CTF_CAPS, 1);
 +
 +      // effects
 +      if (autocvar_g_ctf_flag_capture_effects) 
 +      {
 +              pointparticles(particleeffectnum((player.team == COLOR_TEAM1) ? "red_ground_quake" : "blue_ground_quake"), flag.origin, '0 0 0', 1);
 +              //shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1);
 +      }
 +
 +      // waypointsprites
 +      WaypointSprite_Kill(player.wps_flagcarrier);
 +
 +      // reset the flag
 +      if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); }
 +      
 +      ctf_RespawnFlag(player.flagcarried);
 +}
 +
 +void ctf_Handle_Return(entity flag, entity player)
 +{
 +      // messages and sounds
 +      Send_KillNotification (player.netname, flag.netname, "", INFO_RETURNFLAG, MSG_INFO);
 +      sound(player, CH_TRIGGER, flag.noise1, VOL_BASE, ATTN_NONE);
 +      ctf_EventLog("return", flag.team, player);
 +
 +      // scoring
 +      PlayerTeamScore_AddScore(player, ctf_ReadScore(strcat("score_return", ((player.playerid == flag.playerid) ? "_by_killer" : "")))); // reward for return
 +      PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
 +
 +      TeamScore_AddToTeam(((flag.team == COLOR_TEAM1) ? COLOR_TEAM2 : COLOR_TEAM1), ST_SCORE, -ctf_ReadScore("penalty_returned")); // punish the team who was last carrying it
 +      FOR_EACH_PLAYER(player) if(player.playerid == flag.ctf_dropperid) // punish the player who dropped the flag
 +      {
 +              PlayerScore_Add(player, SP_SCORE, -ctf_ReadScore("penalty_returned"));
 +              ctf_CaptureShield_Update(player, 0); // shield only
 +      }
 +
 +      // waypointsprites 
 +      WaypointSprite_Kill(flag.wps_flagdropped);
 +      
 +      // reset the flag
 +      ctf_RespawnFlag(flag);
 +}
 +
 +void ctf_Handle_Pickup_Base(entity flag, entity player)
 +{
 +      entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
 +      string verbosename; // holds the name of the player OR no name at all for printing in the centerprints
 +
 +      // attach the flag to the player
 +      flag.owner = player;
 +      player.flagcarried = flag;
 +      setattachment(flag, player, "");
 +      setorigin(flag, FLAG_CARRY_POS);
 +      
 +      // set up the flag
 +      flag.movetype = MOVETYPE_NONE;
 +      flag.takedamage = DAMAGE_NO;
 +      flag.solid = SOLID_NOT;
 +      flag.angles = '0 0 0';
 +      flag.ctf_pickuptime = time; // used for timing runs
 +      flag.ctf_pickupid = player.playerid;
 +      flag.ctf_status = FLAG_CARRY;
 +      
 +      // messages and sounds
 +      Send_KillNotification (player.netname, flag.netname, "", INFO_GOTFLAG, MSG_INFO);
 +      sound(player, CH_TRIGGER, flag.noise, VOL_BASE, ATTN_NONE);
 +      ctf_EventLog("steal", flag.team, player);
 +      verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat("(", player.netname, ")") : ""); // replace TRUE with an autocvar for it.
 +      FOR_EACH_PLAYER(tmp_player)
 +              if(tmp_player.team == flag.team)
 +                      centerprint(tmp_player, strcat("The enemy ", verbosename, "got your flag! Retrieve it!"));
 +              else if((tmp_player.team == player.team) && (tmp_player != player))
 +                      centerprint(tmp_player, strcat("Your team mate ", verbosename, "got the flag! Protect them!"));
 +      
 +      // scoring
 +      PlayerTeamScore_AddScore(player, ctf_ReadScore("score_pickup_base"));
 +      PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
 +      
 +      // speedrunning
 +      flag.speedrunning = player.speedrunning; // if speedrunning, flag will flag-return and teleport the owner back after the record
 +      if((player.speedrunning) && (ctf_captimerecord))
 +              ctf_FakeTimeLimit(player, time + ctf_captimerecord);
 +              
 +      // effects
 +      if (autocvar_g_ctf_flag_pickup_effects)
 +      {
 +              pointparticles(particleeffectnum("smoke_ring"), 0.5 * (flag.absmin + flag.absmax), '0 0 0', 1);
 +      }
 +      
 +      // waypoints 
 +      WaypointSprite_Spawn("flagcarrier", 0, 0, player, '0 0 64', world, player.team, player, wps_flagcarrier, FALSE, RADARICON_FLAG, '1 1 0'); // (COLOR_TEAM1 + COLOR_TEAM2 - flag.team)
 +      WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent) * 2);
 +      WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent));
 +      WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, '1 1 0');
 +      WaypointSprite_Ping(player.wps_flagcarrier);
 +}
 + 
 +void ctf_Handle_Pickup_Dropped(entity flag, entity player) // make sure this works
 +{
 +      // declarations
 +      float returnscore = bound(0, (flag.pain_finished - time) / autocvar_g_ctf_flag_returntime, 1); // can this be division by zero?
 +      entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
 +      string verbosename; // holds the name of the player OR no name at all for printing in the centerprints
 +
 +      // attach the flag to the player
 +      flag.owner = player;
 +      player.flagcarried = flag;
 +      setattachment(flag, player, "");
 +      setorigin(flag, FLAG_CARRY_POS);
 +      
 +      // set up the flag
 +      flag.movetype = MOVETYPE_NONE;
 +      flag.takedamage = DAMAGE_NO;
 +      flag.solid = SOLID_NOT;
 +      flag.angles = '0 0 0';
 +      //flag.ctf_pickuptime = time; // don't update pickuptime since this isn't a real steal. 
 +      flag.ctf_pickupid = player.playerid;
 +      flag.ctf_status = FLAG_CARRY;
 +
 +      // messages and sounds
 +      Send_KillNotification (player.netname, flag.netname, "", INFO_PICKUPFLAG, MSG_INFO);
 +      sound (player, CH_TRIGGER, flag.noise, VOL_BASE, ATTN_NONE);
 +      ctf_EventLog("pickup", flag.team, player);
 +      verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat("(", player.netname, ")") : "");
 +      FOR_EACH_PLAYER(tmp_player)
 +              if(tmp_player.team == flag.team)
 +                      centerprint(tmp_player, strcat("The enemy ", verbosename, "got your flag! Retrieve it!"));
 +              else if((tmp_player.team == player.team) && (tmp_player != player))
 +                      centerprint(tmp_player, strcat("Your team mate ", verbosename, "got the flag! Protect them!"));
 +
 +      // scoring
 +      returnscore = floor((ctf_ReadScore("score_pickup_dropped_late") * (1-returnscore) + ctf_ReadScore("score_pickup_dropped_early") * returnscore) + 0.5);
 +      print("score is ", ftos(returnscore), "\n");
 +      PlayerTeamScore_AddScore(player, returnscore);
 +      PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
 +
 +      // effects
 +      if (autocvar_g_ctf_flag_pickup_effects) // field pickup effect
 +      {
 +              pointparticles(particleeffectnum("smoke_ring"), 0.5 * (flag.absmin + flag.absmax), '0 0 0', 1); 
 +      }
 +
 +      // waypoints
 +      WaypointSprite_Kill(flag.wps_flagdropped);
 +      WaypointSprite_Spawn("flagcarrier", 0, 0, player, '0 0 64', world, player.team, player, wps_flagcarrier, FALSE, RADARICON_FLAG, '1 1 0'); // (COLOR_TEAM1 + COLOR_TEAM2 - flag.team)
 +      WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent) * 2);
 +      WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent));
 +      WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, '1 1 0');
 +      WaypointSprite_Ping(player.wps_flagcarrier);
 +}
 +
 +
 +// ===================
 +// Main Flag Functions
 +// ===================
 +
 +void ctf_FlagThink()
 +{
 +      // declarations
 +      entity tmp_entity;
 +
 +      self.nextthink = time + 0.1; // only 10 fps, more is unnecessary.
 +
 +      // captureshield
 +      if(self == ctf_worldflaglist) // only for the first flag
 +              FOR_EACH_CLIENT(tmp_entity)
 +                      ctf_CaptureShield_Update(tmp_entity, 1); // release shield only
 +
 +      // sanity checks
 +      if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) { // reset the flag boundaries in case it got squished
 +              dprint("wtf the flag got squished?\n");
 +              tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
 +              if(!trace_startsolid) // can we resize it without getting stuck?
 +                      setsize(self, FLAG_MIN, FLAG_MAX); }
 +
 +      if(self.owner.classname != "player" || (self.owner.deadflag) || (self.owner.flagcarried != self)) {
 +              dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n");
 +              ctf_Handle_Drop(self.owner);
 +              return; }
 +
 +      // main think method
 +      switch(self.ctf_status) 
 +      {       
 +              case FLAG_BASE: // nothing to do here
 +                      return;
 +              
 +              case FLAG_DROPPED:
 +                      // flag fallthrough? FIXME remove this if bug is really fixed now
 +                      if(self.origin_z < -131072)
 +                      {
 +                              dprint("FLAG FALLTHROUGH just happened\n");
 +                              self.pain_finished = 0;
 +                      }
 +                      setattachment(self, world, "");
 +                      if(time > self.pain_finished)
 +                      {
 +                              bprint("The ", self.netname, " has returned to base\n");
 +                              sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
 +                              ctf_EventLog("returned", self.team, world);
 +                              ctf_RespawnFlag(self);
 +                      }
 +                      return;
 +                              
 +              case FLAG_CARRY:
 +                      if((self.owner) && (self.speedrunning) && (ctf_captimerecord) && (time >= self.ctf_pickuptime + ctf_captimerecord)) 
 +                      {
 +                              bprint("The ", self.netname, " became impatient after ", ftos_decimals(ctf_captimerecord, 2), " seconds and returned itself\n");
 +                              sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
 +
 +                              self.owner.impulse = 141; // returning!
 +
 +                              tmp_entity = self;
 +                              self = self.owner;
 +                              ctf_RespawnFlag(tmp_entity);
 +                              ImpulseCommands();
 +                              self = tmp_entity;
 +                      }
 +                      return;
 +
 +              default: // this should never happen
 +                      dprint("Think: Flag exists with no status?\n");
 +                      return;
 +      }
 +}
 +
 +void ctf_FlagTouch()
 +{
 +      if(gameover) { return; }
 +      if(!self) { return; }
 +      if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
 +      { // The flag fell off the map, respawn it since players can't get to it
 +              //ctf_RespawnFlag(self);
 +              return;
 +      }
 +      if(other.deadflag != DEAD_NO) { return; }
 +      if(other.classname != "player") 
 +      {  // The flag just touched an object, most likely the world
 +              pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
 +              sound(self, CH_TRIGGER, "keepaway/touch.wav", VOL_BASE, ATTN_NORM);
 +              return; 
 +      }
 +      else if(self.wait > time) { return; }
 +
 +      switch(self.ctf_status) 
 +      {       
 +              case FLAG_BASE:
 +                      if((other.team == self.team) && (other.flagcarried) && (other.flagcarried.team != self.team))
 +                              ctf_Handle_Capture(self, other); // other just captured the enemies flag to his base
 +                      else if((other.team != self.team) && (!other.flagcarried) && (!other.ctf_captureshielded))
 +                              ctf_Handle_Pickup_Base(self, other); // other just stole the enemies flag
 +                      break;
 +              
 +              case FLAG_DROPPED:
 +                      if(other.team == self.team)
 +                              ctf_Handle_Return(self, other); // other just returned his own flag
 +                      else if((!other.flagcarried) && ((other.playerid != self.ctf_dropperid) || (time > self.ctf_droptime + autocvar_g_balance_ctf_delay_collect)))
 +                              ctf_Handle_Pickup_Dropped(self, other); // other just picked up a dropped enemy flag
 +                      break;
 +                              
 +              case FLAG_CARRY:
 +                      dprint("Someone touched a flag even though it was being carried?\n");
 +                      break;
 +
 +              default: // this should never happen
 +                      dprint("Touch: Flag exists with no status?\n");
 +                      break;
 +      }
 +}
 +
 +void ctf_RespawnFlag(entity flag)
 +{
 +      // reset the player (if there is one)
 +      if((flag.owner) && (flag.owner.flagcarried == flag))
 +      {
 +              WaypointSprite_Kill(flag.wps_flagcarrier);
 +              flag.owner.flagcarried = world;
 +
 +              if(flag.speedrunning)
 +                      ctf_FakeTimeLimit(flag.owner, -1);
 +      }
 +
 +      // reset the flag
 +      setattachment(flag, world, "");
 +      setorigin(flag, flag.ctf_spawnorigin); // replace with flag.ctf_spawnorigin
 +      flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS);
 +      flag.takedamage = DAMAGE_NO;
 +      flag.solid = SOLID_TRIGGER;
 +      flag.velocity = '0 0 0';
 +      flag.angles = flag.mangle;
 +      flag.ctf_status = FLAG_BASE;
 +      flag.flags = FL_ITEM | FL_NOTARGET;
 +      flag.owner = world;
 +}
 +
 +void ctf_Reset()
 +{
 +      if(self.owner)
 +              if(self.owner.classname == "player")
 +                      ctf_Handle_Drop(self.owner);
 +                      
 +      ctf_RespawnFlag(self);
 +}
 +
 +void ctf_SetupFlag(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc 
 +{
 +      // declarations
 +      teamnumber = fabs(teamnumber - bound(0, g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1. 
 +      
 +      // main setup
 +      flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist // todo: find out if this can be simplified
 +      ctf_worldflaglist = flag;
 +
 +      setattachment(flag, world, ""); 
 +
 +      flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
 +      flag.team = ((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2); // COLOR_TEAM1: color 4 team (red) - COLOR_TEAM2: color 13 team (blue)
 +      flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
 +      flag.classname = "item_flag_team";
 +      flag.target = "###item###"; // wut?
 +      flag.flags = FL_ITEM | FL_NOTARGET;
 +      flag.solid = SOLID_TRIGGER;
 +      flag.velocity = '0 0 0';
 +      flag.ctf_status = FLAG_BASE;
 +      flag.ctf_spawnorigin = flag.origin; 
 +      flag.mangle = flag.angles;
 +      flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
 +      
 +      if(flag.spawnflags & 1) // I don't understand what all this is about.
 +      {       
 +              flag.noalign = TRUE;
 +              flag.movetype = MOVETYPE_NONE;
 +              print("This map was loaded with flags using MOVETYPE_NONE\n");
 +      }
 +      else 
 +      { 
 +              flag.noalign = FALSE;
 +              flag.movetype = MOVETYPE_TOSS; 
 +              print("This map was loaded with flags using MOVETYPE_TOSS\n");
 +      }       
 +      
 +      flag.reset = ctf_Reset;
 +      flag.touch = ctf_FlagTouch;
 +
 +      // appearence
 +      if(!flag.model) { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); }
 +      setmodel (flag, flag.model); // precision set below
 +      setsize(flag, FLAG_MIN, FLAG_MAX);
 +      setorigin(flag, flag.origin);
 +      if(!flag.scale) { flag.scale = 0.6; }
 +      
 +      flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin);
 +      
 +      if(autocvar_g_ctf_flag_glowtrails)
 +      {
 +              flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
 +              flag.glow_size = 25;
 +              flag.glow_trail = 1;
 +      }
 +      
 +      flag.effects |= EF_LOWPRECISION;
 +      if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; }
 +      if(autocvar_g_ctf_dynamiclights)   { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); }
 +      
 +      // sound 
 +      if(!flag.noise)  { flag.noise  = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); }
 +      if(!flag.noise1) { flag.noise1 = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); }
 +      if(!flag.noise2) { flag.noise2 = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag
 +      if(!flag.noise3) { flag.noise3 = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
 +      if(!flag.noise4) { flag.noise4 = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); }
 +      
 +      // precache
 +      precache_sound(flag.noise);
 +      precache_sound(flag.noise1);
 +      precache_sound(flag.noise2);
 +      precache_sound(flag.noise3);
 +      precache_sound(flag.noise4);
 +      precache_model(flag.model);
 +      precache_model("models/ctf/shield.md3");
 +      precache_model("models/ctf/shockwavetransring.md3");
 +
 +      // bot waypoints
 +      waypoint_spawnforitem_force(flag, flag.origin);
 +      flag.nearestwaypointtimeout = 0; // activate waypointing again
 +      flag.bot_basewaypoint = flag.nearestwaypoint;
 +
 +      // waypointsprites
 +      WaypointSprite_SpawnFixed(((teamnumber) ? "redbase" : "bluebase"), flag.origin + '0 0 64', flag, wps_flagbase, RADARICON_FLAG, colormapPaletteColor(((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2) - 1, FALSE));
 +      WaypointSprite_UpdateTeamRadar(flag.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2) - 1, FALSE));
 +
 +      // captureshield setup
 +      ctf_CaptureShield_Spawn(flag);
 +}
 +
 +
 +// ==============
 +// Hook Functions
 +// ==============
 +
 +// g_ctf_ignore_frags
 +
 +MUTATOR_HOOKFUNCTION(ctf_RemovePlayer)
 +{
 +      if(self.flagcarried) { ctf_Handle_Drop(self); }
 +      return 0;
 +}
 +
 +MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
 +{
 +      entity flag;
 +      
 +      // initially clear items so they can be set as necessary later.
 +      self.items &~= (IT_RED_FLAG_CARRYING | IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST 
 +              | IT_BLUE_FLAG_CARRYING | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED);
 +
 +      // item for stopping players from capturing the flag too often
 +      if(self.ctf_captureshielded)
 +              self.items |= IT_CTF_SHIELDED;
 +
 +      // scan through all the flags and notify the client about them 
 +      for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
 +      {
 +              if(flag.ctf_status == FLAG_CARRY)
 +                      if(flag.owner == self)
 +                              self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_CARRYING : IT_BLUE_FLAG_CARRYING); // carrying: self is currently carrying the flag
 +                      else 
 +                              self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_TAKEN : IT_BLUE_FLAG_TAKEN); // taken: someone on self's team is carrying the flag
 +              else if(flag.ctf_status == FLAG_DROPPED) 
 +                      self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_LOST : IT_BLUE_FLAG_LOST); // lost: the flag is dropped somewhere on the map
 +      }
 +      
 +      return 0;
 +}
 +
 +MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
 +{     /*
 +      if(frag_attacker.flagcarried) // if the attacker is a flagcarrier
 +      {
 +              if(frag_target == frag_attacker) // damage done to yourself
 +              {
 +                      frag_damage *= autocvar_g_ctf_flagcarrier_selfdamagefactor;
 +                      frag_force *= autocvar_g_ctf_flagcarrier_selfforcefactor;
 +              }
 +              else // damage done to noncarriers
 +              {
 +                      frag_damage *= autocvar_g_ctf_flagcarrier_damagefactor;
 +                      frag_force *= autocvar_g_ctf_flagcarrier_forcefactor;
 +              }
 +      }*/
 +      return 0;
 +}
 +
 +MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKill)
 +{
 +      frag_score = 0; // no frags counted in keepaway
 +      return (g_ctf_ignore_frags); // you deceptive little bugger ;3 This needs to be true in order for this function to even count. 
 +}
 +
 +MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
 +{
 +      if(autocvar_g_ctf_allow_drop)
 +              ctf_Handle_Drop(self);
 +              
 +      return 0;
 +}
 +
 +// ==========
 +// Spawnfuncs
 +// ==========
 +
 +/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
 +CTF Starting point for a player in team one (Red).
 +Keys: "angle" viewing angle when spawning. */
 +void spawnfunc_info_player_team1()
 +{
 +      if(g_assault) { remove(self); return; }
 +      
 +      self.team = COLOR_TEAM1; // red
 +      spawnfunc_info_player_deathmatch();
 +}
 +
 +
 +/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)
 +CTF Starting point for a player in team two (Blue).
 +Keys: "angle" viewing angle when spawning. */
 +void spawnfunc_info_player_team2()
 +{
 +      if(g_assault) { remove(self); return; }
 +      
 +      self.team = COLOR_TEAM2; // blue
 +      spawnfunc_info_player_deathmatch();
 +}
 +
 +/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)
 +CTF Starting point for a player in team three (Yellow).
 +Keys: "angle" viewing angle when spawning. */
 +void spawnfunc_info_player_team3()
 +{
 +      if(g_assault) { remove(self); return; }
 +      
 +      self.team = COLOR_TEAM3; // yellow
 +      spawnfunc_info_player_deathmatch();
 +}
 +
 +
 +/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)
 +CTF Starting point for a player in team four (Purple).
 +Keys: "angle" viewing angle when spawning. */
 +void spawnfunc_info_player_team4()
 +{
 +      if(g_assault) { remove(self); return; }
 +      
 +      self.team = COLOR_TEAM4; // purple
 +      spawnfunc_info_player_deathmatch();
 +}
 +
 +/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
 +CTF flag for team one (Red). Multiple flags are allowed.
 +Keys: 
 +"angle" Angle the flag will point (minus 90 degrees)... 
 +"model" model to use, note this needs red and blue as skins 0 and 1 (default models/ctf/flag.md3)...
 +"noise" sound played when flag is picked up (default ctf/take.wav)...
 +"noise1" sound played when flag is returned by a teammate (default ctf/return.wav)...
 +"noise2" sound played when flag is captured (default ctf/redcapture.wav)...
 +"noise3" sound played when flag is lost in the field and respawns itself (default ctf/respawn.wav)... */
 +void spawnfunc_item_flag_team1()
 +{
 +      if(!g_ctf) { remove(self); return; }
 +
 +      ctf_SetupFlag(1, self); // 1 = red
 +}
 +
 +/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
 +CTF flag for team two (Blue). Multiple flags are allowed.
 +Keys: 
 +"angle" Angle the flag will point (minus 90 degrees)... 
 +"model" model to use, note this needs red and blue as skins 0 and 1 (default models/ctf/flag.md3)...
 +"noise" sound played when flag is picked up (default ctf/take.wav)...
 +"noise1" sound played when flag is returned by a teammate (default ctf/return.wav)...
 +"noise2" sound played when flag is captured (default ctf/redcapture.wav)...
 +"noise3" sound played when flag is lost in the field and respawns itself (default ctf/respawn.wav)... */
 +void spawnfunc_item_flag_team2()
 +{
 +      if(!g_ctf) { remove(self); return; }
 +
 +      ctf_SetupFlag(0, self); // the 0 is misleading, but -- 0 = blue.
 +}
 +
 +/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
 +Team declaration for CTF gameplay, this allows you to decide what team names and control point models are used in your map.
 +Note: If you use spawnfunc_ctf_team entities you must define at least 2!  However, unlike domination, you don't need to make a blank one too.
 +Keys:
 +"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)...
 +"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */
 +void spawnfunc_ctf_team()
 +{
 +      if(!g_ctf) { remove(self); return; }
 +      
 +      self.classname = "ctf_team";
 +      self.team = self.cnt + 1;
 +}
 +
 +
 +// ==============
 +// Initialization
 +// ==============
 +
 +// code from here on is just to support maps that don't have flag and team entities
 +void ctf_SpawnTeam (string teamname, float teamcolor)
 +{
 +      entity oldself;
 +      oldself = self;
 +      self = spawn();
 +      self.classname = "ctf_team";
 +      self.netname = teamname;
 +      self.cnt = teamcolor;
 +
 +      spawnfunc_ctf_team();
 +
 +      self = oldself;
 +}
 +
 +void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
 +{
 +      // if no teams are found, spawn defaults
 +      if(find(world, classname, "ctf_team") == world)
 +      {
 +              print("NO TEAMS FOUND FOR CTF! creating them anyway.\n");
 +              ctf_SpawnTeam("Red", COLOR_TEAM1 - 1);
 +              ctf_SpawnTeam("Blue", COLOR_TEAM2 - 1);
 +      }
 +      
 +      ScoreRules_ctf();
 +}
 +
 +void ctf_Initialize()
 +{
 +      ctf_captimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
 +
 +      ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
 +      ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
 +      ctf_captureshield_force = autocvar_g_ctf_shield_force;
 +
++      //g_ctf_win_mode = cvar("g_ctf_win_mode");
 +      
 +      InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
 +}
 +
 +
 +MUTATOR_DEFINITION(gamemode_ctf)
 +{
 +      MUTATOR_HOOK(MakePlayerObserver, ctf_RemovePlayer, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(ClientDisconnect, ctf_RemovePlayer, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerDies, ctf_RemovePlayer, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(GiveFragsForKill, ctf_GiveFragsForKill, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerPreThink, ctf_PlayerPreThink, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerDamage_Calculate, ctf_PlayerDamage, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerUseKey, ctf_PlayerUseKey, CBC_ORDER_ANY);
 +      //MUTATOR_HOOK(PlayerPowerups, ctf_PlayerPowerups, CBC_ORDER_ANY);
 +
 +      MUTATOR_ONADD
 +      {
 +              if(time > 1) // game loads at time 1
 +                      error("This is a game type and it cannot be added at runtime.");
 +              g_ctf = 1;
 +              ctf_Initialize();
 +      }
 +
 +      MUTATOR_ONREMOVE
 +      {
 +              g_ctf = 0;
 +              error("This is a game type and it cannot be removed at runtime.");
 +      }
 +
 +      return 0;
 +}
index 97c8907db00e6e873f32afb91a304be7cfb22186,825062e1cdcd50bdd0429502921eec76c4377a59..9945d84ba01626cb16a0b3976543eaf637bfa2eb
@@@ -1,9 -1,13 +1,14 @@@
  MUTATOR_DECLARATION(gamemode_keyhunt);
  MUTATOR_DECLARATION(gamemode_freezetag);
  MUTATOR_DECLARATION(gamemode_keepaway);
 +MUTATOR_DECLARATION(gamemode_ctf);
  
+ MUTATOR_DECLARATION(mutator_invincibleprojectiles);
  MUTATOR_DECLARATION(mutator_nix);
  MUTATOR_DECLARATION(mutator_dodging);
  MUTATOR_DECLARATION(mutator_rocketflying);
  MUTATOR_DECLARATION(mutator_vampire);
+ MUTATOR_DECLARATION(mutator_spawn_near_teammate);
+ MUTATOR_DECLARATION(mutator_spawn_near_teammate);
+ MUTATOR_DECLARATION(sandbox);
diff --combined qcsrc/server/nexball.qc
index a5b8d47a1c7ac3af7e0e897650e8e2973799616a,a068a33a54e236017194c3ec5ac05e5caa0b9df1..d3f4b55f4a49acf42693df6e04df24942a1b99f3
@@@ -28,9 -28,6 +28,9 @@@ float nb_teams
  
  .float teamtime;
  
 +.float nb_dropperid;
 +.float nb_droptime;
 +
  void nb_delayedinit();
  void nb_init() // Called early (worldspawn stage)
  {
@@@ -82,7 -79,7 +82,7 @@@ void ball_restart (void
  
  void nexball_setstatus (void)
  {
-       local entity oldself;
+       entity oldself;
        self.items &~= IT_KEY1;
        if (self.ballcarried)
        {
@@@ -121,7 -118,7 +121,7 @@@ void football_touch()
  
  void DropOwner (void)
  {
-       local entity ownr;
+       entity ownr;
        ownr = self.owner;
        DropBall(self, ownr.origin, ownr.velocity);
        makevectors(ownr.v_angle_y * '0 1 0');
  
  void GiveBall (entity plyr, entity ball)
  {
-       local entity ownr;
+       entity ownr;
  
        if ((ownr = ball.owner))
        {
        ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it
        ball.team = plyr.team;
        plyr.ballcarried = ball;
 -      ball.dropperid = plyr.playerid;
 +      ball.nb_dropperid = plyr.playerid;
  
        plyr.effects |= g_nexball_basketball_effects_default;
        ball.effects &~= g_nexball_basketball_effects_default;
@@@ -191,7 -188,7 +191,7 @@@ void DropBall (entity ball, vector org
        ball.flags &~= FL_ONGROUND;
        ball.scale = ball_scale;
        ball.velocity = vel;
 -      ball.ctf_droptime = time;
 +      ball.nb_droptime = time;
        ball.touch = basketball_touch;
        ball.think = ResetBall;
        ball.nextthink = min(time + g_nexball_delay_idle, ball.teamtime);
@@@ -305,7 -302,7 +305,7 @@@ void basketball_touch (void
                football_touch();
                return;
        }
 -      if (!self.cnt && other.classname == "player" && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_nexball_delay_collect)) {
 +      if (!self.cnt && other.classname == "player" && (other.playerid != self.nb_dropperid || time > self.nb_droptime + autocvar_g_nexball_delay_collect)) {
                if (other.health <= 0)
                        return;
                LogNB("caught", other);
@@@ -410,14 -407,14 +410,14 @@@ void spawnfunc_nexball_team (void
  void nb_spawnteam (string teamname, float teamcolor)
  {
        dprint("^2spawned team ", teamname, "\n");
-       local entity e;
+       entity e;
        e = spawn();
        e.classname = "nexball_team";
        e.netname = teamname;
        e.cnt = teamcolor;
        e.team = e.cnt + 1;
        nb_teams += 1;
- };
+ }
  
  void nb_spawnteams (void)
  {
@@@ -595,7 -592,7 +595,7 @@@ void spawnfunc_ball_bound      (void) 
  
  void W_Nexball_Touch (void)
  {
-       local entity ball, attacker;
+       entity ball, attacker;
        attacker = self.owner;
  
        PROJECTILE_TOUCH;
  
  void W_Nexball_Attack (float t)
  {
-       local entity ball;
-       local float mul, mi, ma;
+       entity ball;
+       float mul, mi, ma;
        if (!(ball = self.ballcarried))
                return;
  
  
  void W_Nexball_Attack2 (void)
  {
-       local entity missile;
+       entity missile;
        if (!(balls & BALL_BASKET))
                return;
        W_SetupShot (self, FALSE, 2, "nexball/shoot2.wav", CH_WEAPON_A, 0);
@@@ -720,7 -717,7 +720,7 @@@ float w_nexball_weapon(float req
        {
                precache_model ("models/weapons/g_porto.md3");
                precache_model ("models/weapons/v_porto.md3");
-               precache_model ("models/weapons/h_porto.dpm");
+               precache_model ("models/weapons/h_porto.iqm");
                precache_model ("models/elaser.mdl");
                precache_sound ("nexball/shoot1.wav");
                precache_sound ("nexball/shoot2.wav");
diff --combined qcsrc/server/portals.qc
index 291250d20a49ae91d6268e6d4853331beae7a14c,76af253dca5c0f3bd3b5785134f9c378687fdf8c..1c6cf17eb68a1af9eaec0acd73b56ce7b62d030d
@@@ -7,6 -7,7 +7,7 @@@
  .vector portal_safe_origin;
  .float portal_wants_to_vanish;
  .float portal_activatetime;
+ .float savemodelindex;
  
  float PlayerEdgeDistance(entity p, vector v)
  {
@@@ -154,7 -155,7 +155,7 @@@ float Portal_TeleportPlayer(entity tele
        player.right_vector = -1 * AnglesTransform_Apply(transform, player.right_vector);
  
        if(player.flagcarried)
 -              DropFlag(player.flagcarried, player, world);
 +              ctf_Handle_Drop(player); // FIXCTF
  
        if not(teleporter.enemy)
        {
@@@ -479,7 -480,7 +480,7 @@@ float Portal_Customize(
                other = other.enemy;
        if(other == self.aiment)
        {
-               self.modelindex = self.modelindex_lod0;
+               self.modelindex = self.savemodelindex;
        }
        else if(IS_INDEPENDENT_PLAYER(other) || IS_INDEPENDENT_PLAYER(self.aiment))
        {
        }
        else
        {
-               self.modelindex = self.modelindex_lod0;
+               self.modelindex = self.savemodelindex;
        }
        return TRUE;
  }
@@@ -617,7 -618,7 +618,7 @@@ entity Portal_Spawn(entity own, vector 
        portal.fade_time = time + autocvar_g_balance_portal_lifetime;
        portal.health = autocvar_g_balance_portal_health;
        setmodel(portal, "models/portal.md3");
-       portal.modelindex_lod0 = portal.modelindex;
+       portal.savemodelindex = portal.modelindex;
        portal.customizeentityforclient = Portal_Customize;
  
        if(!Portal_FindSafeOrigin(portal))
@@@ -644,11 -645,7 +645,7 @@@ float Portal_SpawnInPortalAtTrace(entit
  
        portal = Portal_Spawn(own, org, ang);
        if(!portal)
-       {
-               // if(!own.portal_out || own.portal_out.portal_id == portal_id_val)
-                       Portal_ClearAll_PortalsOnly(own);
                return 0;
-       }
  
        portal.portal_id = portal_id_val;
        Portal_SetInPortal(own, portal);
@@@ -668,11 -665,7 +665,7 @@@ float Portal_SpawnOutPortalAtTrace(enti
  
        portal = Portal_Spawn(own, org, ang);
        if(!portal)
-       {
-               // if(!own.portal_in || own.portal_in.portal_id == portal_id_val)
-                       Portal_ClearAll_PortalsOnly(own);
                return 0;
-       }
  
        portal.portal_id = portal_id_val;
        Portal_SetOutPortal(own, portal);
diff --combined qcsrc/server/progs.src
index faec297252f93aae94841655778b950c4f55876f,9a459c5fd464c0ef0249a39717a1a0e5335856d3..0ebe6d9fb5aaabd606c88b3c31e0f9cd0688dd95
@@@ -1,12 -1,10 +1,10 @@@
  ../../progs.dat // output filename
  
  ../common/util-pre.qh
- sys.qh
- pre-builtins.qh
- builtins.qh
- extensions.qh
- post-builtins.qh
+ sys-pre.qh
+ ../dpdefs/progsdefs.qc
+ ../dpdefs/dpextensions.qc
+ sys-post.qh
  
  ../warpzonelib/anglestransform.qh
  ../warpzonelib/mathlib.qh
  ../common/util.qh
  ../common/items.qh
  ../common/explosion_equation.qh
+ ../common/urllib.qh
+ ../common/command/markup.qh
+ ../common/command/rpn.qh
+ ../common/command/generic.qh
+ ../common/command/shared_defs.qh
  
  autocvars.qh
  constants.qh
@@@ -25,7 -28,6 +28,7 @@@ defs.qh               // Should rename this, it has 
  
  mutators/base.qh
  mutators/mutators.qh
 +mutators/gamemode_ctf.qh // for spawnfuncs
  mutators/gamemode_keyhunt.qh // TODO fix this
  mutators/mutator_dodging.qh
  
@@@ -36,10 -38,20 +39,20 @@@ vehicles/vehicles_def.q
  campaign.qh
  ../common/campaign_common.qh
  ../common/mapinfo.qh
- ../common/util.qc
+ command/common.qh
+ command/banning.qh
+ command/radarmap.qh
+ command/vote.qh
+ command/getreplies.qh
+ command/cmd.qh
+ command/sv_cmd.qh
  
  accuracy.qh
  csqcprojectile.qh
+ ../common/csqcmodel_settings.qh
+ ../csqcmodellib/common.qh
+ ../csqcmodellib/sv_model.qh
  csqceffects.qc
  
  anticheat.qh
@@@ -60,10 -72,12 +73,12 @@@ race.q
  
  antilag.qh
  
- vote.qh
  playerdemo.qh
  
+ // singleplayer stuff
+ item_key.qh
+ secret.qh
  scores_rules.qc
  
  miscfunctions.qc
@@@ -102,6 -116,10 +117,10 @@@ sv_main.q
  g_triggers.qc
  g_models.qc
  
+ // singleplayer stuff
+ item_key.qc
+ secret.qc
  cl_weaponsystem.qc
  w_common.qc
  
@@@ -118,7 -136,7 +137,7 @@@ cl_client.q
  t_plats.qc
  antilag.qc
  
 -ctf.qc
 +//ctf.qc
  domination.qc
  mode_onslaught.qc
  nexball.qc
@@@ -126,16 -144,21 +145,21 @@@ g_hook.q
  
  t_swamp.qc
  
- clientcommands.qc
- vote.qc
  campaign.qc
  ../common/campaign_file.qc
  ../common/campaign_setup.qc
- ../common/gamecommand.qc
- gamecommand.qc
+ ../common/urllib.qc
+ ../common/command/markup.qc
+ ../common/command/rpn.qc
+ ../common/command/generic.qc
+ command/common.qc
+ command/banning.qc
+ command/radarmap.qc
+ command/vote.qc
+ command/getreplies.qc
+ command/cmd.qc
+ command/sv_cmd.qc
  
  assault.qc
  
@@@ -143,9 -166,6 +167,6 @@@ ipban.q
  
  ../common/mapinfo.qc
  
  t_quake3.qc
  t_halflife.qc
  t_quake.qc
@@@ -167,12 -187,9 +188,9 @@@ target_music.q
  
  ../common/items.qc
  
- monsters/defs.qc
- monsters/fight.qc
- monsters/ai.qc
- monsters/m_monsters.qc
- monsters/monster_zombie.qc
  accuracy.qc
+ ../csqcmodellib/sv_model.qc
  csqcprojectile.qc
  
  playerdemo.qc
@@@ -184,14 -201,16 +202,17 @@@ playerstats.q
  ../common/explosion_equation.qc
  
  mutators/base.qc
 +mutators/gamemode_ctf.qc
  mutators/gamemode_keyhunt.qc
  mutators/gamemode_freezetag.qc
  mutators/gamemode_keepaway.qc
+ mutators/mutator_invincibleproj.qc
  mutators/mutator_nix.qc
  mutators/mutator_dodging.qc
  mutators/mutator_rocketflying.qc
  mutators/mutator_vampire.qc
+ mutators/mutator_spawn_near_teammate.qc
+ mutators/sandbox.qc
  
  ../warpzonelib/anglestransform.qc
  ../warpzonelib/mathlib.qc
  ../warpzonelib/util_server.qc
  ../warpzonelib/server.qc
  
+ ../common/util.qc
  ../common/if-this-file-errors-scroll-up-and-fix-the-warnings.fteqccfail
diff --combined qcsrc/server/teamplay.qc
index 4ee0dd041e5abef4b2e5e710aeb2fbf610f65542,f0ce8fd142ec4027b10c524db8b97297a90a8968..a8042859e4eb9823cbaeebe7bd98ef9961ae50cc
@@@ -68,7 -68,7 +68,7 @@@ string TeamNoName(float t
  }
  
  void dom_init();
 -void ctf_init();
 +//void ctf_init();
  void runematch_init();
  void tdm_init();
  void nb_init();
@@@ -85,74 -85,6 +85,6 @@@ void LogTeamchange(float player_id, flo
        GameLogEcho(strcat(":team:", ftos(player_id), ":", ftos(team_number), ":", ftos(type)));
  }
  
- void WriteGameCvars()
- {
-       cvar_set("g_dm", ftos(g_dm));
-       cvar_set("g_tdm", ftos(g_tdm));
-       cvar_set("g_domination", ftos(g_domination));
-       cvar_set("g_ctf", ftos(g_ctf));
-       cvar_set("g_runematch", ftos(g_runematch));
-       cvar_set("g_lms", ftos(g_lms));
-       cvar_set("g_arena", ftos(g_arena));
-       cvar_set("g_ca", ftos(g_ca));
-       cvar_set("g_keyhunt", ftos(g_keyhunt));
-       cvar_set("g_assault", ftos(g_assault));
-       cvar_set("g_onslaught", ftos(g_onslaught));
-       cvar_set("g_race", ftos(g_race));
-       cvar_set("g_nexball", ftos(g_nexball));
-       cvar_set("g_cts", ftos(g_cts));
-       cvar_set("g_freezetag", ftos(g_freezetag));
-       cvar_set("g_keepaway", ftos(g_keepaway));
- }
- void ReadGameCvars()
- {
-       float found;
-       float prev;
-       float i;
-       found = 0;
-       prev = autocvar_gamecfg;
-       for(i = 0; i < 2; ++i)
-       {
- //#NO AUTOCVARS START
-               found += (g_dm = (!found && (prev != GAME_DEATHMATCH) && cvar("g_dm")));
-               found += (g_tdm = (!found && (prev != GAME_TEAM_DEATHMATCH) && cvar("g_tdm")));
-               found += (g_domination = (!found && (prev != GAME_DOMINATION) && cvar("g_domination")));
-               found += (g_ctf = (!found && (prev != GAME_CTF) && cvar("g_ctf")));
-               found += (g_runematch = (!found && (prev != GAME_RUNEMATCH) && cvar("g_runematch")));
-               found += (g_lms = (!found && (prev != GAME_LMS) && cvar("g_lms")));
-               found += (g_arena = (!found && (prev != GAME_ARENA) && cvar("g_arena")));
-               found += (g_ca = (!found && (prev != GAME_CA) && cvar("g_ca")));
-               found += (g_keyhunt = (!found && (prev != GAME_KEYHUNT) && cvar("g_keyhunt")));
-               found += (g_assault = (!found && (prev != GAME_ASSAULT) && cvar("g_assault")));
-               found += (g_onslaught = (!found && (prev != GAME_ONSLAUGHT) && cvar("g_onslaught")));
-               found += (g_race = (!found && (prev != GAME_RACE) && cvar("g_race")));
-               found += (g_nexball = (!found && (prev != GAME_NEXBALL) && cvar("g_nexball")));
-               found += (g_cts = (!found && (prev != GAME_CTS) && cvar("g_cts")));
-               found += (g_freezetag = (!found && (prev != GAME_FREEZETAG) && cvar("g_freezetag")));
-               found += (g_keepaway = (!found && (prev != GAME_KEEPAWAY) && cvar("g_keepaway")));
- //#NO AUTOCVARS END
-               if(found)
-                       break;
-               prev = -1; // second attempt takes place WITHOUT prev set
-       }
-       if(!found)
-               g_dm = 1;
-       if(g_dm && autocvar_deathmatch_force_teamplay)
-       {
-               g_dm = 0;
-               g_tdm = 1;
-       }
-       teamplay = 0;
-       serverflags &~= SERVERFLAG_TEAMPLAY;
- }
  void default_delayedinit()
  {
        if(!scores_initialized)
@@@ -173,16 -105,14 +105,14 @@@ void InitGameplayMode(
  
        VoteReset();
  
-       // make sure only ONE type is selected
-       ReadGameCvars();
-       WriteGameCvars();
        // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
        get_mi_min_max(1);
        world.mins = mi_min;
        world.maxs = mi_max;
  
        MapInfo_LoadMapSettings(mapname);
+       teamplay = 0;
+       serverflags &~= SERVERFLAG_TEAMPLAY;
  
        if not(cvar_value_issafe(world.fog))
        {
  
        MapInfo_ClearTemps();
  
-       // in case mapinfo switched the type
-       ReadGameCvars();
        // set both here, gamemode can override it later
        timelimit_override = autocvar_timelimit_override;
        fraglimit_override = autocvar_fraglimit_override;
        leadlimit_override = autocvar_leadlimit_override;
+       gamemode_name = MapInfo_Type_ToText(MapInfo_LoadedGametype);
  
        if(g_dm)
        {
-               game = GAME_DEATHMATCH;
-               gamemode_name = "Deathmatch";
        }
  
        if(g_tdm)
        {
-               game = GAME_TEAM_DEATHMATCH;
-               gamemode_name = "Team Deathmatch";
                ActivateTeamplay();
                tdm_init();
                if(autocvar_g_tdm_team_spawns)
  
        if(g_domination)
        {
-               game = GAME_DOMINATION;
-               gamemode_name = "Domination";
                ActivateTeamplay();
                fraglimit_override = autocvar_g_domination_point_limit;
                leadlimit_override = autocvar_g_domination_point_leadlimit;
  
        if(g_ctf)
        {
-               game = GAME_CTF;
-               gamemode_name = "Capture the Flag";
                ActivateTeamplay();
                g_ctf_ignore_frags = autocvar_g_ctf_ignore_frags;
-               if(g_ctf_win_mode == 2)
-               {
-                       fraglimit_override = autocvar_g_ctf_capture_limit;
-                       leadlimit_override = autocvar_g_ctf_capture_leadlimit;
-               }
-               else
-               {
-                       fraglimit_override = autocvar_capturelimit_override;
-                       leadlimit_override = autocvar_captureleadlimit_override;
-               }
+               fraglimit_override = autocvar_capturelimit_override;
+               leadlimit_override = autocvar_captureleadlimit_override;
 -              ctf_init();
 +              MUTATOR_ADD(gamemode_ctf);
                have_team_spawns = -1; // request team spawns
        }
  
        if(g_runematch)
        {
-               game = GAME_RUNEMATCH;
-               gamemode_name = "Rune Match";
-               if(autocvar_deathmatch_force_teamplay)
-                       ActivateTeamplay();
+               // ActivateTeamplay();
                fraglimit_override = autocvar_g_runematch_point_limit;
                leadlimit_override = autocvar_g_runematch_point_leadlimit;
                runematch_init();
  
        if(g_lms)
        {
-               game = GAME_LMS;
-               gamemode_name = "Last Man Standing";
                fraglimit_override = autocvar_g_lms_lives_override;
                leadlimit_override = 0; // not supported by LMS
                if(fraglimit_override == 0)
  
        if(g_arena)
        {
-               game = GAME_ARENA;
-               gamemode_name = "Arena";
                fraglimit_override = autocvar_g_arena_point_limit;
                leadlimit_override = autocvar_g_arena_point_leadlimit;
                maxspawned = autocvar_g_arena_maxspawned;
  
        if(g_ca)
        {
-               game = GAME_CA;
-               gamemode_name = "Clan Arena";
                ActivateTeamplay();
                fraglimit_override = autocvar_g_ca_point_limit;
                leadlimit_override = autocvar_g_ca_point_leadlimit;
        }
        if(g_keyhunt)
        {
-               game = GAME_KEYHUNT;
-               gamemode_name = "Key Hunt";
                ActivateTeamplay();
                fraglimit_override = autocvar_g_keyhunt_point_limit;
                leadlimit_override = autocvar_g_keyhunt_point_leadlimit;
  
        if(g_freezetag)
        {
-               game = GAME_FREEZETAG;
-               gamemode_name = "Freeze Tag";
                ActivateTeamplay();
                fraglimit_override = autocvar_g_freezetag_point_limit;
                leadlimit_override = autocvar_g_freezetag_point_leadlimit;
  
        if(g_assault)
        {
-               game = GAME_ASSAULT;
-               gamemode_name = "Assault";
                ActivateTeamplay();
                ScoreRules_assault();
                have_team_spawns = -1; // request team spawns
  
        if(g_onslaught)
        {
-               game = GAME_ONSLAUGHT;
-               gamemode_name = "Onslaught";
                ActivateTeamplay();
                have_team_spawns = -1; // request team spawns
        }
  
        if(g_race)
        {
-               game = GAME_RACE;
-               gamemode_name = "Race";
  
                if(autocvar_g_race_teams)
                {
  
        if(g_cts)
        {
-               game = GAME_CTS;
-               gamemode_name = "CTS";
                g_race_qualifying = 1;
                fraglimit_override = 0;
                leadlimit_override = 0;
  
        if(g_nexball)
        {
-               game = GAME_NEXBALL;
-               gamemode_name = "Nexball";
                fraglimit_override = autocvar_g_nexball_goallimit;
                leadlimit_override = autocvar_g_nexball_goalleadlimit;
                ActivateTeamplay();
  
        if(g_keepaway)
        {
-               game = GAME_KEEPAWAY;
-               gamemode_name = "Keepaway";
                MUTATOR_ADD(gamemode_keepaway);
        }
  
        if(teamplay)
                entcs_init();
  
-       // save it (for the next startup)
-       cvar_set("gamecfg", ftos(game));
        cache_mutatormsg = strzone("");
        cache_lastmutatormsg = strzone("");
  
  }
  
  string GetClientVersionMessage() {
-       local string versionmsg;
+       string versionmsg;
        if (self.version_mismatch) {
                if(self.version < autocvar_gameversion) {
                        versionmsg = "^3Your client version is outdated.\n\n\n### YOU WON'T BE ABLE TO PLAY ON THIS SERVER ###\n\n\nPlease update!!!^8";
        return versionmsg;
  }
  
- void PrintWelcomeMessage(entity pl)
+ string getwelcomemessage(void)
  {
        string s, modifications, motd;
  
-       if(self.cvar_scr_centertime == 0) return;
-       if(autocvar_g_campaign)
-       {
-               if(self.classname == "player" && !self.BUTTON_INFO)
-                       return;
-       }
-       else
-       {
-               if((time - self.jointime) > autocvar_welcome_message_time && !self.BUTTON_INFO)
-                       return;
-       }
-       if( !(timeoutStatus >= 1) ) { //really print the WelcomeMessage to the player every frame when timeout-seconds are shown or the game is restarted, to make sure that the shown number is accurate
-               if(self.welcomemessage_time > time) return;
-               self.welcomemessage_time = time + max(0.5, self.cvar_scr_centertime * 0.6);
-       }
-       if(autocvar_g_campaign)
-       {
-               centerprint(pl, campaign_message);
-               return;
-       }
- //TODO GreEn`mArine: make the timeout-messages clientside as well (just like the ready restart countdown)!
-       if(!self.BUTTON_INFO)
-       {
-               // TODO get rid of this too
-               local string specString;
-               specString = NEWLINES;
-               //if(time < game_starttime) //also show the countdown when being a spectator
-               //      specString = strcat(specString, "\n\n^1Game starts in ", ftos(ceil(game_starttime - time)), " seconds^7");
-               //else
-               if (timeoutStatus != 0)
-                       specString = strcat(specString, "\n\n", getTimeoutText(1));
-               else
-               {
-                       if(self.classname == "player")
-                               return;
-                       goto normal;
-               }
-               return centerprint_atprio(self, CENTERPRIO_SPAM, specString);
-       }
- :normal
        ret_string = "";
        MUTATOR_CALLHOOK(BuildMutatorsPrettyString);
        modifications = ret_string;
                modifications = strcat(modifications, ", Blood loss");
        if(g_jetpack)
                modifications = strcat(modifications, ", Jet pack");
+       if(autocvar_g_powerups == 0)
+               modifications = strcat(modifications, ", No powerups");
+       if(autocvar_g_powerups > 0)
+               modifications = strcat(modifications, ", Powerups");
        modifications = substring(modifications, 2, strlen(modifications) - 2);
  
-       local string versionmessage;
+       string versionmessage;
        versionmessage = GetClientVersionMessage();
  
-       s = strcat(s, NEWLINES, "This is Xonotic ", autocvar_g_xonoticversion, "\n", versionmessage);
+       s = strcat("This is Xonotic ", autocvar_g_xonoticversion, "\n", versionmessage);
        s = strcat(s, "^8\n\nmatch type is ^1", gamemode_name, "^8\n");
  
        if(modifications != "")
                s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n");
  
-       if(timeoutStatus != 0)
-               s = strcat(s, "\n\n", getTimeoutText(1));
        if (g_grappling_hook)
                s = strcat(s, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
  
        if (motd != "") {
                s = strcat(s, "\n\n^8MOTD: ^7", strreplace("\\n", "\n", motd));
        }
-       s = strcat(s, "\n");
-       centerprint(pl, s);
+       return s;
  }
  
  void SetPlayerColors(entity pl, float _color)
  {
        /*string s;
@@@ -1327,13 -1163,13 +1163,13 @@@ void AuditTeams(
  // code from here on is just to support maps that don't have team entities
  void tdm_spawnteam (string teamname, float teamcolor)
  {
-       local entity e;
+       entity e;
        e = spawn();
        e.classname = "tdm_team";
        e.netname = teamname;
        e.cnt = teamcolor;
        e.team = e.cnt + 1;
- };
+ }
  
  // spawn some default teams if the map is not set up for tdm
  void tdm_spawnteams()
                tdm_spawnteam("Yellow", COLOR_TEAM3-1);
        if(numteams >= 4)
                tdm_spawnteam("Pink", COLOR_TEAM4-1);
- };
+ }
  
  void tdm_delayedinit()
  {
        // if no teams are found, spawn defaults
        if (find(world, classname, "tdm_team") == world)
                tdm_spawnteams();
- };
+ }
  
  void tdm_init()
  {
        InitializeEntity(world, tdm_delayedinit, INITPRIO_GAMETYPE);
- };
+ }
index 2aeab39500c3448be59540076eb9349d4a9a6349,20c74d3a17e24deb0012882c634c9d527d4747d0..97784315d1f0ba14e33cfca06913c0b8811e3ceb
@@@ -42,7 -42,7 +42,7 @@@ void UpdateAuxiliaryXhair(entity own, v
      entity axh;
  
      axh_id = bound(0, axh_id, MAX_AXH);
-     axh = own.AuxiliaryXhair[axh_id];
+     axh = own.(AuxiliaryXhair[axh_id]);
  
      if(axh == world || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
      {
@@@ -56,7 -56,7 +56,7 @@@
      setorigin(axh, loc);
      axh.colormod            = clr;
      axh.SendFlags           = 0x01;
-     own.AuxiliaryXhair[axh_id] = axh;
+     own.(AuxiliaryXhair[axh_id]) = axh;
  }
  
  /*
@@@ -339,6 -339,7 +339,7 @@@ void vehicles_spawn(
      self.touch              = vehicles_touch;
      self.event_damage       = vehicles_damage;
      self.iscreature         = TRUE;
+     self.damagedbycontents    = TRUE;
      self.movetype           = MOVETYPE_WALK;
      self.solid              = SOLID_SLIDEBOX;
      self.takedamage         = DAMAGE_AIM;
  
      // Return to spawn
      self.angles             = self.pos2;
-     setorigin(self, self.pos1 + '0 0 128');
+     setorigin(self, self.pos1 + '0 0 0');
      // Show it
      pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
  
@@@ -375,27 -376,47 +376,47 @@@ float vehicles_crushable(entity e
      return FALSE;
  }
  
+ void vehilces_impact(float _minspeed, float _speedfac, float _maxpain)
+ {    
+     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+         return;
+     
+     if(self.play_time < time)
+     {                    
+         float wc = vlen(self.velocity - self.oldvelocity);
+         //dprint("oldvel: ", vtos(self.oldvelocity), "\n");
+         //dprint("vel: ", vtos(self.velocity), "\n");
+         if(_minspeed < wc)
+         {
+             float take = take = min(_speedfac * wc, _maxpain);
+             Damage (self, world, world, take, DEATH_FALL, self.origin, '0 0 0');
+             self.play_time = time + 0.25;
+             
+             //dprint("wc: ", ftos(wc), "\n");
+             //dprint("take: ", ftos(take), "\n");
+         }
+     }
+ }
+ .void() vehicle_impact;
  void vehicles_touch()
  {
      // Vehicle currently in use
      if(self.owner)
      {
-         // Colided with world?
-         if(other == world)
-         {
-         }
-         else
+         if(other != world)
+         if(vehicles_crushable(other))
          {
-             if(other.vehicle_flags & VHF_ISVEHICLE)
-             {
-                 //other.velocity += self.velocity * (self.mass / other.mass);
-             }
-             else if(vehicles_crushable(other))
-             {
-                 if(vlen(self.velocity) != 0)
-                     Damage(other, self, self.owner, autocvar_g_vehicles_crush_dmg, DEATH_VHCRUSH, '0 0 0', normalize(other.origin - self.origin) * autocvar_g_vehicles_crush_force);
-             }
+             if(vlen(self.velocity) != 0)
+                 Damage(other, self, self.owner, autocvar_g_vehicles_crush_dmg, DEATH_VHCRUSH, '0 0 0', normalize(other.origin - self.origin) * autocvar_g_vehicles_crush_force);
+             
+             return; // Dont do selfdamage when hitting "soft targets".
          }
+         
+         if(self.play_time < time)
+         if(self.vehicle_impact)
+             self.vehicle_impact();
+         
          return;
      }
  
@@@ -428,6 -449,8 +449,8 @@@ void vehicles_enter(
      if(self.team)
      if(self.team != other.team)
          return;
+         
+     RemoveGrapplingHook(other);
  
      self.vehicle_ammo1   = 0;
      self.vehicle_ammo2   = 0;
  
      CSQCVehicleSetup(self.owner, self.hud);
      
 +    /* FIXCTF // THIS IS A BIG NO-NO, NO GAME MODE SPECIFIC CODE IN VEHICLES.
      if(other.flagcarried)
      {
          if(!autocvar_g_vehicles_allow_flagcarry)
              setorigin(other, '0 0 96');
          }
      }
 +    */
      
      self.vehicle_enter();
+     antilag_clear(other);
  }
  
  /** vehicles_findgoodexit
@@@ -607,6 -629,8 +631,8 @@@ void vehicles_exit(float eject
          self.owner.hud            = HUD_NORMAL;
          self.owner.switchweapon   = self.switchweapon;
          //self.owner.BUTTON_USE     = 0;
+         
+         CSQCVehicleSetup(self.owner, HUD_NORMAL);
      }
  
      if(self.deadflag == DEAD_NO)
      else
          self.team = self.tur_head.team;
      
 +    /* FIXCTF // THIS IS A BIG NO-NO, NO GAME MODE SPECIFIC CODE IN VEHICLES.
      if(self.owner.flagcarried)
      {
          self.owner.flagcarried.scale = 0.6;
          setattachment(self.owner.flagcarried, self.owner, ""); 
          setorigin(self.owner.flagcarried, FLAG_CARRY_POS);
      }
 +    */
      
      sound (self, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTN_NORM);
      self.vehicle_exit(eject);
@@@ -669,9 -691,7 +695,7 @@@ void shieldhit_think(
  }
  
  void vehicles_painframe()
- {
- //.float      pain_finished;                  //Added by Supajoe
-     
+ {    
      if(self.owner.vehicle_health <= 50)
      if(self.pain_frame < time)
      {  
@@@ -976,6 -996,7 +1000,7 @@@ float vehicle_initialize(string  net_na
      self.takedamage         = DAMAGE_AIM;
      self.bot_attack         = TRUE;
      self.iscreature         = TRUE;
+     self.damagedbycontents    = TRUE;
      self.hud                = vhud;
  
      self.vehicle_die         = dieproc;