Merge remote-tracking branch 'origin/master' into fruitiex/animations
authorFruitieX <fruitiex@gmail.com>
Thu, 29 Sep 2011 19:09:10 +0000 (22:09 +0300)
committerFruitieX <fruitiex@gmail.com>
Thu, 29 Sep 2011 19:09:10 +0000 (22:09 +0300)
1  2 
defaultXonotic.cfg
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_weaponsystem.qc
qcsrc/server/defs.qh
qcsrc/server/w_shotgun.qc

diff --combined defaultXonotic.cfg
index 6da1710d16562ad7e3855c64201b0e199ff8999a,a0d50e3e7a996bf234d765d634097f23d5b2357e..3f1ca3f53b0066fe2e1d3461f6faeb3218ad12ce
@@@ -10,7 -10,7 +10,7 @@@
  // e.g. Xonotic 1.5.1 RC1 will be 15101
  set g_xonoticversion git "Xonotic version (formatted for humans)"
  
- gameversion 100 // 0.1.0
+ gameversion 500 // 0.5.0
  gameversion_min 0 // git builds see all versions
  gameversion_max 65535 // git builds see all versions
  
@@@ -177,7 -177,7 +177,7 @@@ seta crosshair_seeker_size 0.8     "crossha
  seta crosshair_rifle ""       "crosshair to display when wielding the rifle"
  seta crosshair_rifle_color "0.85 0.5 0.25"    "crosshair color to display when wielding the rifle"
  seta crosshair_rifle_alpha 1  "crosshair alpha value to display when wielding the rifle"
- seta crosshair_rifle_size 0.65        "crosshair size when wielding the rifle"
+ seta crosshair_rifle_size 0.5 "crosshair size when wielding the rifle"
  seta crosshair_tuba ""        "crosshair to display when wielding the tuba"
  seta crosshair_tuba_color "0.85 0.5 0.25"     "crosshair color to display when wielding the tuba"
  seta crosshair_tuba_alpha 1   "crosshair alpha value to display when wielding the tuba"
@@@ -284,11 -284,11 +284,11 @@@ gl_polyblend 0 // whether to use scree
  r_motionblur 0 // motion blur value, default is 0
  r_damageblur 0 // motion blur when damaged, default is 0 (removed in Xonotic)
  
- r_bloom_blur 8
- r_bloom_brighten 3
+ r_bloom_blur 16
+ r_bloom_brighten 2.5
  r_bloom_colorexponent 1
  r_bloom_colorscale 1
- r_bloom_colorsubtract 0.25
+ r_bloom_colorsubtract 0.15
  r_bloom_resolution 320
  r_hdr_range 4
  
@@@ -381,12 -381,10 +381,11 @@@ set sv_player_crouch_viewoffset "0 0 20
  set sv_player_crouch_mins "-16 -16 -24" "mins of a crouched playermodel"
  set sv_player_crouch_maxs "16 16 25" "maxs of a crouched playermodel"
  
- set sv_pogostick 1 "don't require releasing the space bar for jumping again"
  set sv_doublejump 0 "allow Quake 2-style double jumps"
  set sv_jumpspeedcap_min "" "lower bound on the baseline velocity of a jump; final velocity will be >= (jumpheight * min + jumpheight)"
  set sv_jumpspeedcap_max "" "upper bound on the baseline velocity of a jump; final velocity will be <= (jumpheight * max + jumpheight)"
  set sv_jumpspeedcap_max_disable_on_ramps 0 "disable upper baseline velocity bound on ramps to preserve the old rampjump style"
 +set sv_player_jumpanim_minfall 48 "minimum distance player has to have below their feet before the jump animation will be activated (only when falling, +jump will play anim instantly)"
  
  seta sv_precacheplayermodels 1
  seta sv_precacheweapons 0
@@@ -433,7 -431,7 +432,7 @@@ set bot_wander_enable 1 "Have bots wand
  // general bot AI cvars
  set bot_ai_thinkinterval 0.05
  set bot_ai_strategyinterval 5 "How often a new objective is chosen"
- set bot_ai_enemydetectioninterval 3 "How often bots pick a new target"
+ set bot_ai_enemydetectioninterval 2 "How often bots pick a new target"
  set bot_ai_enemydetectionradius 10000 "How far bots can see enemies"
  set bot_ai_dodgeupdateinterval 0.2 "How often scan for items to dodge. Currently not in use."
  set bot_ai_chooseweaponinterval 0.5 "How often the best weapon according to the situation will be chosen"
@@@ -458,8 -456,8 +457,8 @@@ set bot_ai_weapon_combo_threshold 0.4      "
  set bot_ai_friends_aware_pickup_radius "500"  "Bots will not pickup items if a team mate is this distance near the item"
  set bot_ai_ignoregoal_timeout 3       "Ignore goals making bots to get stuck in front of a wall for N seconds"
  set bot_ai_bunnyhop_skilloffset 7     "Bots with skill equal or greater than this value will perform the  \"bunnyhop\" technique"
- set bot_ai_bunnyhop_startdistance 100 "Run to goals located further than this distance"
- set bot_ai_bunnyhop_stopdistance 125 "Stop jumping after reaching this distance to the goal"
+ set bot_ai_bunnyhop_startdistance 200 "Run to goals located further than this distance"
+ set bot_ai_bunnyhop_stopdistance 200 "Stop jumping after reaching this distance to the goal"
  set bot_ai_bunnyhop_firstjumpdelay 0.2 "Start running to the goal only if it was seen for more than N seconds"
  set bot_god 0 "god mode for bots"
  set bot_ai_navigation_jetpack 0 "Enable bots to navigate maps using the jetpack"
@@@ -707,8 -705,8 +706,8 @@@ set g_ctf_reverse 0        "if enabled, flags 
  set g_balance_ctf_delay_collect 1.0
  set g_balance_ctf_damageforcescale 1
  
- set g_ctf_shield_max_ratio 0  "shield at most 0% of a team from the enemy flag (try: 0.4 for 40%)"
- set g_ctf_shield_min_negscore 20      "shield the player from the flag if he's got -20 points or less"
+ set g_ctf_shield_max_ratio 0  "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)"
+ set g_ctf_shield_min_negscore 20      "shield the player from the flag if he's got this negative amount of points or less"
  set g_ctf_shield_force 100    "push force of the shield"
  
  // fun for server admins
@@@ -809,6 -807,7 +808,7 @@@ set g_arena_powerups 0     "enables powerup
  set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round."
  set g_ca_point_limit 10 "point limit 10 is standard for clan arena"
  set g_ca_point_leadlimit 0
+ set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games."
  set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
  
  // onslaught
@@@ -1187,8 -1186,7 +1187,7 @@@ set g_campaign 
  set g_campaign_forceteam 0 "Forces the player to a given team in campaign mode, 1 = red, 2 = blue, 3 = yellow, 4 = pink"
  seta g_campaign_name "xonoticbeta"
  set g_campaign_skill 0
- set g_campaignxonotic20_index 0
- set g_campaignxonotic25_index 1
+ alias warp "sv_cmd warp $*"
  
  alias singleplayer_start "g_campaign_index 0; set scmenu_campaign_goto 0"
  alias singleplayer_continue "set scmenu_campaign_goto -1"
@@@ -1412,6 -1410,7 +1411,7 @@@ seta slowmo 
  seta menu_skin "luminos"
  set menu_slowmo 1
  seta menu_sounds 0 "enables menu sound effects. 1 enables click sounds, 2 also enables hover sounds"
+ seta menu_tooltips 1 "menu tooltips: 0 disabled, 1 enabled, 2 also shows cvar or console command (when available) changed or executed by the item"
  set menu_picmip_bypass 0 "bypass texture quality enforcement based on system resources, not recommended and may cause crashes!"
  
  r_textbrightness 0.2
@@@ -1448,6 -1447,7 +1448,7 @@@ seta hud_panel_weapons_ammo_full_nails 
  seta hud_panel_weapons_ammo_full_cells 80 "show 100% of the status bar at this ammo count"
  seta hud_panel_weapons_ammo_full_rockets 80 "show 100% of the status bar at this ammo count"
  seta hud_panel_weapons_ammo_full_fuel 100 "show 100% of the status bar at this ammo count"
+ seta hud_panel_weapons_onlyowned 1 "show only owned weapons"
  
  seta hud_panel_ammo_maxammo "40" "when you have this much ammo, the ammo status bar is full"
  
@@@ -1528,6 -1528,7 +1529,7 @@@ seta hud_contents_water_color "0.4 0.3 
  
  seta hud_shownames 1 "draw names and health/armor of nearby players"
  seta hud_shownames_enemies 2 "1 = draw names of enemies you point at (TODO), 2 = draw names of all enemies in view"
+ seta hud_shownames_self 0 "also include your own name to be shown when third person camera mode is on (chase_active/cl_eventchase)"
  seta hud_shownames_status 1 "1 = draw health/armor status of teammates"
  seta hud_shownames_statusbar_height 4 "height of status bar"
  seta hud_shownames_aspect 8 "aspect ratio of total drawing area per name"
@@@ -1633,7 -1634,7 +1635,7 @@@ set con_completion_playermodel  models/p
  seta cl_port $cl_port
  seta r_showsurfaces $r_showsurfaces
  seta r_ambient $r_ambient
- seta skill $skill
+ seta skill 4
  seta gl_finish $gl_finish
  seta v_kicktime $v_kicktime
  seta r_subdivisions_tolerance $r_subdivisions_tolerance
@@@ -1743,7 -1744,9 +1745,9 @@@ alias gl_flashblend_update "_gl_flashbl
  
  set sv_clones 0       "number of clones a player may make (reset by the \"kill\" command)"
  
- set cl_handicap 1     "the higher, the more damage you will receive (client setting)"
+ set cl_handicap 1     "the higher, the more damage you will receive (client setting) NOTE: reconnect or use sendcvar command to update the choice."
+ seta cl_clippedspectating 1 "movement collision for spectators so that you can't pass through walls and such. (client setting) NOTE: reconnect or use sendcvar command to update the choice." 
  
  // must be at the bottom of this file:
  // alias for switching the teamselect menu
@@@ -1950,6 -1953,7 +1954,7 @@@ set g_triggerimpulse_accel_power 1 "tri
  set g_triggerimpulse_accel_multiplier 1 "trigger_impulse accelerator multiplier (applied AFTER the power)"
  set g_triggerimpulse_directional_multiplier 1 "trigger_impulse directional field multiplier"
  set g_triggerimpulse_radial_multiplier 1 "trigger_impulse radial field multiplier"
+ set the_goggles "they do nothing" "but the googles, they do"
  
  seta g_ghost_items 1 "enable ghosted items (when between 0 and 1, overrides the alpha value)"
  seta g_ghost_items_color "-1 -1 -1" "color of ghosted items, 0 0 0 leaves the color unchanged"
@@@ -1992,6 -1996,9 +1997,9 @@@ set _origin "0 0 0
  set _campaign_index ""
  set _campaign_name ""
  
+ // debug
+ set _independent_players 0 "DO NOT TOUCH"
  // define some engine cvars that we need even on dedicated server
  set r_showbboxes 0
  
@@@ -2087,6 -2094,7 +2095,7 @@@ sv_clmovement_inputtimeout 0.07 // mor
  
  // exact gloss looks better, e.g. on g-23
  r_shadow_glossexact 1
+ r_shadow_glossintensity 1
  
  // use fake light if map has no lightmaps
  r_fakelight 1
index 675af0397a9779e6d8eefc5bace1ecfecc20a8f1,e9d34ce17e8564ab7b9d683ab3e331a297992adf..6ccaf71d4c52053a96a6651bd6829f0d66754786
@@@ -1,3 -1,4 +1,4 @@@
+ float autocvar__independent_players;
  float autocvar__campaign_index;
  string autocvar__campaign_name;
  float autocvar__sv_init;
@@@ -324,6 -325,8 +325,8 @@@ 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;
@@@ -336,6 -339,8 +339,8 @@@ float autocvar_g_balance_hagar_secondar
  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;
@@@ -712,9 -717,10 +717,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;
@@@ -1128,7 -1134,6 +1134,7 @@@ 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;
index 36beed4e934b06b446ca9dbbfa69923df07c64cb,bddb5933233ba91f7d422a85820852276344722e..58d25b0e73474761c2a3d53797a31903456bb44e
@@@ -659,13 -659,13 +659,13 @@@ void PutObserverInServer (void
        accuracy_resend(self);
  
        self.spectatortime = time;
+       
        self.classname = "observer";
        self.iscreature = FALSE;
        self.health = -666;
        self.takedamage = DAMAGE_NO;
        self.solid = SOLID_NOT;
-       self.movetype = MOVETYPE_NOCLIP;
+       self.movetype = MOVETYPE_FLY_WORLDONLY; //(self.cvar_cl_clippedspectating ? MOVETYPE_NOCLIP : MOVETYPE_FLY); // it's too early for this anyway, lets just set it in playerprethink
        self.flags = FL_CLIENT | FL_NOTARGET;
        self.armorvalue = 666;
        self.effects = 0;
        self.fixangle = TRUE;
        self.crouch = FALSE;
  
-       self.view_ofs = PL_VIEW_OFS;
+       self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
        setorigin (self, spot.origin);
-       setsize (self, '0 0 0', '0 0 0');
+       setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
        self.prevorigin = self.origin;
        self.items = 0;
        self.weapons = 0;
        self.model = "";
        self.modelindex = 0;
        self.weapon = 0;
+       self.weaponname = "";
+       self.switchingweapon = 0;
        self.weaponmodel = "";
        self.weaponentity = world;
        self.exteriorweaponentity = world;
                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;
  }
@@@ -898,7 -907,7 +907,7 @@@ void PutClientInServer (void
                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;
                self.takedamage = DAMAGE_AIM;
                        self.killcount = 0;
                }
  
-               self.cnt = WEP_LASER;
                CL_SpawnWeaponentity();
                self.alpha = default_player_alpha;
                self.colormod = '1 1 1' * autocvar_g_player_brightness;
                        if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
                                self.weapon_load[j] = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));
                }
-               self.weapon_forbidchange = FALSE;
  
                oldself = self;
                self = spot;
                MUTATOR_CALLHOOK(PlayerSpawn);
  
                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;
@@@ -1335,9 -1343,11 +1343,11 @@@ void KillIndicator_Think(
        }
  }
  
+ float clientkilltime;
  void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2 = spec
  {
        float killtime;
+       float starttime;
        entity e;
  
        if (gameover)
                }
                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;
@@@ -1659,8 -1673,7 +1673,7 @@@ void ClientConnect (void
        bprint("\n");
  
        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);
  
@@@ -2300,6 -2313,7 +2313,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);
@@@ -2359,12 -2374,41 +2374,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;
  
@@@ -2422,6 -2466,7 +2466,7 @@@ void ShowRespawnCountdown(
        }
  }
  
+ .float prevent_join_msgtime;
  void LeaveSpectatorMode()
  {
        if(nJoinAllowed(1)) {
                        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 {
                        if (g_ca && self.caplayer) {
        }
        else {
                //player may not join because of g_maxplayers is set
-               centerprint(self, 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;
+               }
        }
  }
  
@@@ -2501,8 -2556,45 +2556,45 @@@ 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.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();
  }
  
  void SpectatorThink()
                        PutObserverInServer();
        }
  
+       PrintWelcomeMessage();
        self.flags |= FL_CLIENT | FL_NOTARGET;
  }
  
@@@ -2597,7 -2696,6 +2696,6 @@@ Called every frame for each client befo
  void() ctf_setstatus;
  void() nexball_setstatus;
  .float items_added;
- .float motd_actived_time; // used for both motd and campaign_message
  void PlayerPreThink (void)
  {
        WarpZone_PlayerPhysics_FixVAngle();
                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"));
                                                }
                                        }
                PlayerUseKey();
        self.usekeypressed = self.BUTTON_USE;
  
-       if (self.motd_actived_time == 0) {
-               if (autocvar_g_campaign) {
-                       if (self.classname == "player" && self.BUTTON_INFO) {
-                               self.motd_actived_time = time;
-                               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, campaign_message, -1, 0);
-                       }
-               } else {
-                       if ((self.classname == "player" || 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.classname == "player") {
-                               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);
-                               }
-                       }
-               } else {
-                       if (self.classname == "player" || (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);
-                               }
-                       }
-               }
-       }
+       PrintWelcomeMessage();
  
        if(self.classname == "player") {
  //            if(self.netname == "Wazat")
  
                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)
                        {
@@@ -3099,6 -3169,8 +3169,8 @@@ void PlayerPostThink (void
  
        CheatFrame();
  
+       //CheckPlayerJump();
        if(self.classname == "player") {
                CheckRules_Player();
                UpdateChatBubble();
index e6a8f9999ddb3d8018308c9fb1c0c8db76eeaee4,b7e8bc22648b631647bf33898c4be713dc3d46f9..85d4cd00029c22160f8ab30f1fb36a155115e233
@@@ -34,7 -34,15 +34,15 @@@ void PlayerJump (void
        {
                tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self);
                if (trace_fraction < 1 && trace_plane_normal_z > 0.7)
+               {
                        doublejump = TRUE;
+                       // we MUST clip velocity here!
+                       float f;
+                       f = self.velocity * trace_plane_normal;
+                       if(f < 0)
+                               self.velocity -= f * trace_plane_normal;
+               }
        }
  
        mjumpheight = autocvar_sv_jumpvelocity;
                if (!(self.flags & FL_ONGROUND))
                        return;
  
-       if(!sv_pogostick)
+       if(self.cvar_cl_movement_track_canjump)
                if (!(self.flags & FL_JUMPRELEASED))
                        return;
  
  
        if (self.crouch)
                setanim(self, self.anim_duckjump, FALSE, TRUE, TRUE);
 -      else
 +      else if (self.animstate_startframe != self.anim_melee_x || (self.animstate_startframe == self.anim_melee_x && time - self.animstate_starttime >= 21/20)) // jump animation shouldn't override melee until we have animation blending (or until the anim finished, 21/20 = numframes/fps)
                setanim(self, self.anim_jump, FALSE, TRUE, TRUE);
  
        if(g_jump_grunt)
        self.restart_jump = -1; // restart jump anim next time
        // value -1 is used to not use the teleport bit (workaround for tiny hitch when re-jumping)
  }
  void CheckWaterJump()
  {
        local vector start, end;
                }
        }
  };
+ void CheckPlayerJump()
+ {
+       if(self.flags & FL_ONGROUND)
+       {
+               if (autocvar_g_multijump > 0)
+                       self.multijump_count = 0;
+               else
+                       self.multijump_count = -2; // the cvar value for infinite jumps is -1, so this needs to be smaller
+       }
+       if (self.BUTTON_JUMP)
+               PlayerJump ();
+       else
+               self.flags |= FL_JUMPRELEASED;
+       if (self.waterlevel == WATERLEVEL_SWIMMING)
+               CheckWaterJump ();
+       self.prevjumpbutton = self.BUTTON_JUMP;
+ }
  
  float racecar_angle(float forward, float down)
  {
@@@ -904,6 -930,7 +930,7 @@@ void SV_PlayerPhysics(
        }
  
        if(self.flags & FL_ONGROUND)
+       if(self.classname == "player") // no fall sounds for observers thank you very much
        if(self.wasFlying)
        {
                self.wasFlying = 0;
                self.wasFlying = 1;
  
        if(self.classname == "player")
-       {
-               if(self.flags & FL_ONGROUND)
-               {
-                       if (autocvar_g_multijump > 0)
-                               self.multijump_count = 0;
-                       else
-                               self.multijump_count = -2; // the cvar value for infinite jumps is -1, so this needs to be smaller
-               }
-               if (self.BUTTON_JUMP)
-                       PlayerJump ();
-               else
-                       self.flags |= FL_JUMPRELEASED;
-               if (self.waterlevel == WATERLEVEL_SWIMMING)
-                       CheckWaterJump ();
-               self.prevjumpbutton = self.BUTTON_JUMP;
-       }
+               CheckPlayerJump();
  
        if (self.flags & FL_WATERJUMP )
        {
        {
                RaceCarPhysics();
        }
-       else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY)
+       else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY || self.movetype == MOVETYPE_FLY_WORLDONLY)
        {
                // noclipping or flying
                self.flags &~= FL_ONGROUND;
index 18817bdc6c48283d60eb0113b160f2125ccadcbe,e5e2d377cf4981f7d9527d1a4ca7f927caf87bec..94f3221657b65845509ee1ca74d0de89af1f7374
@@@ -13,27 -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, n, ibot, jbot, idx;
-       float fh;
        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_purechanges)), "\n"));
-                       fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n"));
+               case URL_READY_CANWRITE:
+                       // url_fopen returned, 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)
-                               fputs(fh, strcat("#cvar_purechange ", argv(i), "\n"));
+                               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, WeaponStats_ready, world);
+                       buf_del(weaponstats_buffer);
+                       weaponstats_buffer = -1;
+                       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, WeaponStats_ready, world);
+                       break;
+               case URL_READY_CLOSED:
+                       // url_fclose has finished
                        print("Weapon stats written\n");
-               }
+                       break;
+               case URL_READY_ERROR:
+               default:
+                       print("Weapon stats writing failed: ", ftos(status), "\n");
+                       break;
+       }
+ }
+ void WeaponStats_Shutdown()
+ {
+       if(weaponstats_buffer < 0)
+               return;
+       if(autocvar_sv_weaponstats_file != "")
+       {
+               url_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)
@@@ -196,7 -226,6 +226,7 @@@ void player_setupanimsformodel(
        self.anim_backright = '21 1 1';
        self.anim_backleft  = '22 1 1';
        self.anim_melee = '23 1 1';
 +      self.anim_fly = '24 1 1';
        animparseerror = FALSE;
        animfilename = strcat(self.model, ".animinfo");
        animfile = fopen(animfilename, FILE_READ);
                self.anim_backright    = animparseline(animfile);
                self.anim_backleft     = animparseline(animfile);
                self.anim_melee        = animparseline(animfile);
 +              self.anim_fly          = animparseline(animfile);
                fclose(animfile);
  
                // derived anims
@@@ -262,39 -290,13 +292,39 @@@ void player_anim (void
  
        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;
 +                      {
 +                              // 21/25 is the magic number here for how long the jump animation takes to play once (numframes/framerate)
 +                              if((self.animstate_startframe == self.anim_jump_x && time - self.animstate_starttime >= 21/25))
 +                                      setanim(self, self.anim_fly, TRUE, FALSE, FALSE);
 +
 +                              if(self.animstate_startframe != self.anim_fly_x) // no tracing if we're in the fly anim
 +                              {
 +                                      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)
                {
@@@ -419,7 -421,7 +449,7 @@@ void PlayerDamage (entity inflictor, en
        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);
                                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))
index f6fc12a68e3b4b7409aa4cd9a45a4d40a81882b0,af76117fec8f2ae7cdb50f539e9b7c31784decfa..21a9bc9428a71d6ec6d9bab9acc51a3db464a105
@@@ -34,10 -34,6 +34,6 @@@ float W_WeaponRateFactor(
  
  void W_SwitchWeapon_Force(entity e, float w)
  {
-       // don't switch to another weapon if we're not allowed to
-       if(e.weapon_forbidchange)
-               return;
        e.cnt = e.switchweapon;
        e.switchweapon = w;
        e.selectweapon = w;
@@@ -297,7 -293,6 +293,6 @@@ vector weapon_adjust = '10 0 -15'
  .vector weapon_morph4origin;
  .vector weapon_morph4angles;
  .float  weapon_morph4time;
- .string weaponname;
  #define QCWEAPONANIMATION_ORIGIN(e) ((weapon_offset_x + e.view_ofs_x) * v_forward - (weapon_offset_y + e.view_ofs_y) * v_right + (weapon_offset_z + e.view_ofs_z) * v_up + weapon_adjust)
  
  /*
@@@ -587,9 -582,9 +582,9 @@@ void CL_Weaponentity_Think(
                        self.weaponentity.model = "";
                return;
        }
-       if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
+       if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
        {
-               self.cnt = self.owner.weapon;
+               self.weaponname = self.owner.weaponname;
                self.dmg = self.owner.modelindex;
                self.deadflag = self.owner.deadflag;
  
@@@ -793,9 -788,9 +788,9 @@@ void CL_ExteriorWeaponentity_Think(
                self.model = "";
                return;
        }
-       if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
+       if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
        {
-               self.cnt = self.owner.weapon;
+               self.weaponname = self.owner.weaponname;
                self.dmg = self.owner.modelindex;
                self.deadflag = self.owner.deadflag;
                if (self.owner.weaponname != "")
@@@ -1007,7 -1002,10 +1002,10 @@@ float client_hasweapon(entity cl, floa
  void w_clear()
  {
        if (self.weapon != -1)
+       {
                self.weapon = 0;
+               self.switchingweapon = 0;
+       }
        if (self.weaponentity)
        {
                self.weaponentity.state = WS_CLEAR;
@@@ -1033,6 -1031,7 +1031,7 @@@ void weapon_setup(float windex
  
        // the two weapon entities will notice this has changed and update their models
        self.weapon = windex;
+       self.switchingweapon = windex; // to make sure
        self.weaponname = e.mdl;
        self.bulletcounter = 0;
  };
@@@ -1273,12 -1272,6 +1272,12 @@@ void weapon_thinkf(float fr, float t, v
                        anim = self.anim_melee;
                        anim_z = anim_y / (t + sys_frametime);
                        setanim(self, anim, FALSE, TRUE, TRUE);
 +              }
 +              else if (self.animstate_startframe == self.anim_idle_x) // only allow shoot anim to override idle animation until we have animation blending
 +              {
 +                      anim = self.anim_shoot;
 +                      anim_z = anim_y / (t + sys_frametime);
 +                      setanim(self, anim, FALSE, TRUE, TRUE);
                }
        }
  };
diff --combined qcsrc/server/defs.qh
index e42dfe086ce50d67f8082d64867af7992c491cc6,bc05d1b04849b05ced40ef7b3ae1a20f0f7c36c8..7dfa8106b1726981171d7ff1ec72de54c01cf3a3
@@@ -149,7 -149,6 +149,7 @@@ float maxclients
  .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_fly; // player animation played after jump, if player is still in air. Also if falling from a ledge
  
  // weapon animation vectors:
  .vector anim_fire1;
@@@ -203,7 -202,12 +203,12 @@@ void setanim(entity e, vector anim, flo
  .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);
@@@ -211,7 -215,6 +216,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 = );
@@@ -310,6 -313,9 +314,9 @@@ float default_weapon_alpha
  .float() customizeentityforclient;
  .float cvar_cl_handicap;
  .float cvar_cl_playerdetailreduction;
+ .float cvar_cl_clippedspectating;
+ .float cvar_cl_movement_track_canjump;
  .string cvar_g_xonoticversion;
  .string cvar_cl_weaponpriority;
  .string cvar_cl_weaponpriorities[10];
@@@ -384,9 -390,6 +391,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;
@@@ -409,7 -412,6 +413,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;
@@@ -495,6 -497,7 +498,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
index 4b6e99d86a4078a34138fe84bf283f58804420fc,2088073f9970e62a1836b96725ab231f85898638..a9d1ba056aca2f2691e731acccf9257161009425
@@@ -98,7 -98,7 +98,7 @@@ void W_Shotgun_Attack2 (void
        meleetemp = spawn();
        meleetemp.owner = meleetemp.realowner = self;
        meleetemp.think = shotgun_meleethink;
-       meleetemp.nextthink = time + autocvar_g_balance_shotgun_secondary_melee_delay;
+       meleetemp.nextthink = time + autocvar_g_balance_shotgun_secondary_melee_delay * W_WeaponRateFactor();
        W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_shotgun_secondary_damage, autocvar_g_balance_shotgun_secondary_melee_range);
  }
  
@@@ -137,14 -137,13 +137,14 @@@ float w_shotgun(float req
                                        if(weapon_prepareattack(0, autocvar_g_balance_shotgun_primary_animtime))
                                        {
                                                W_Shotgun_Attack();
-                                               self.shotgun_primarytime = time + autocvar_g_balance_shotgun_primary_refire;
+                                               self.shotgun_primarytime = time + autocvar_g_balance_shotgun_primary_refire * W_WeaponRateFactor();
                                                weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_shotgun_primary_animtime, w_ready);
                                        }
                                }
                        }
                }
                if (self.clip_load >= 0) // we are not currently reloading
 +              if (!self.crouch) // we are not currently crouching; this fixes an exploit where your melee anim is not visible, and besides wouldn't make much sense
                if (self.BUTTON_ATCK2 && autocvar_g_balance_shotgun_secondary)
                if (weapon_prepareattack(1, autocvar_g_balance_shotgun_secondary_refire))
                {