Merged master.
authorLyberta <lyberta@lyberta.net>
Fri, 4 Aug 2017 22:17:37 +0000 (01:17 +0300)
committerLyberta <lyberta@lyberta.net>
Fri, 4 Aug 2017 22:17:37 +0000 (01:17 +0300)
1  2 
defaultXonotic.cfg
qcsrc/client/view.qc
qcsrc/common/mutators/mutator/overkill/rpc.qc
qcsrc/common/stats.qh
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/server/client.qc
qcsrc/server/defs.qh

diff --combined defaultXonotic.cfg
index 1312d6fa299d1e125b890099581981de504c9bea,c63fc31e3dbc8d1cd0175f4375c1d31d4eccbd8b..f6cba874d4ddb16b68b6029fa4b3c280d71190de
@@@ -375,9 -375,9 +375,9 @@@ set bot_ai_keyboard_threshold 0.5
  set bot_ai_aimskill_offset 0.3 "Amount of error induced to the bots aim"
  set bot_ai_aimskill_think 1 "Aiming velocity. Use values below 1 for slower aiming"
  set bot_ai_custom_weapon_priority_distances "300 850" "Define close and far distances in any order. Based on the distance to the enemy bots will choose different weapons"
 -set bot_ai_custom_weapon_priority_far   "vaporizer vortex rifle electro devastator mortar hagar hlac crylink blaster machinegun fireball seeker shotgun shockwave tuba minelayer"     "Desired weapons for far distances ordered by priority"
 -set bot_ai_custom_weapon_priority_mid   "vaporizer devastator vortex fireball seeker mortar electro machinegun arc crylink hlac hagar shotgun shockwave blaster rifle tuba minelayer" "Desired weapons for middle distances ordered by priority"
 -set bot_ai_custom_weapon_priority_close "vaporizer vortex shotgun shockwave machinegun arc hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer" "Desired weapons for close distances ordered by priority"
 +set bot_ai_custom_weapon_priority_far   "vaporizer okvortex vortex rifle electro devastator mortar hagar hlac crylink blaster okmachinegun machinegun fireball seeker okshotgun shotgun shockwave tuba minelayer"     "Desired weapons for far distances ordered by priority"
 +set bot_ai_custom_weapon_priority_mid   "vaporizer devastator okvortex vortex fireball seeker mortar electro okmachinegun machinegun arc crylink hlac hagar okshotgun shotgun shockwave blaster rifle tuba minelayer" "Desired weapons for middle distances ordered by priority"
 +set bot_ai_custom_weapon_priority_close "vaporizer okvortex vortex okshotgun shotgun shockwave okmachinegun machinegun arc hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer" "Desired weapons for close distances ordered by priority"
  set bot_ai_weapon_combo 1     "Enable bots to do weapon combos"
  set bot_ai_weapon_combo_threshold 0.4 "Try to make a combo N seconds after the last attack"
  set bot_ai_friends_aware_pickup_radius "500"  "Bots will not pickup items if a team mate is this distance near the item"
@@@ -589,7 -589,9 +589,9 @@@ r_glsl_offsetmapping 
  r_glsl_offsetmapping_lod 1
  r_glsl_offsetmapping_reliefmapping 0
  r_glsl_offsetmapping_scale 0.02
- // execute effects-normal.cfg to make sure that all effect settings are reset
+ // create a temporary empty alias for menu_sync so that execution of effects-normal.cfg
+ // on game start doesn't show an error message in the console
  alias menu_sync "" // will be re-aliased later
  
  // misc
@@@ -815,6 -817,15 +817,15 @@@ seta cl_damagetext_accumulate_alpha_re
  seta cl_damagetext_friendlyfire "1" "Show damage text for friendlyfire too"
  seta cl_damagetext_friendlyfire_color "1 0 0" "Damage text color for friendlyfire"
  
+ seta cl_damagetext_2d_pos "0.47 0.53 0" "2D damage text initial position (X and Y between 0 and 1)"
+ seta cl_damagetext_2d_alpha_start 1 "2D damage text initial alpha"
+ seta cl_damagetext_2d_alpha_lifetime 1.3 "2D damage text lifetime (alpha fading) in seconds"
+ seta cl_damagetext_2d_size_lifetime 3 "2D damage text lifetime (size shrinking) in seconds"
+ seta cl_damagetext_2d_velocity "-25 0 0" "2D damage text move direction (screen coordinates)"
+ seta cl_damagetext_2d_overlap_offset "0 -15 0" "Offset 2D damage text by this much to prevent overlapping (screen coordinates)"
+ seta cl_damagetext_2d_close_range 125 "Always use 2D damagetext for hits closer that this"
+ seta cl_damagetext_2d_out_of_view 1 "Always use 2D damagetext for hits that occured off-screen"
  seta cl_vehicles_alarm 1 "Play an alarm sound when the vehicle you are driving is heavily damaged"
  seta cl_vehicles_hud_tactical 1
  seta cl_vehicles_hudscale 0.5
@@@ -1060,13 -1071,13 +1071,13 @@@ sv_allowdownloads 0 // download protoco
  
  set g_jump_grunt 0    "Do you make a grunting noise every time you jump? Is it the same grunting noise every time?"
  
 -seta cl_weaponpriority "vaporizer hmg rpc vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun shockwave hlac tuba blaster porto seeker hook" "weapon priority list"
 +seta cl_weaponpriority "vaporizer hmg rpc okvortex okshotgun okmachinegun vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun shockwave hlac tuba blaster porto seeker hook" "weapon priority list"
  seta cl_weaponpriority_useforcycling 0 "when set, weapon cycling by the mouse wheel makes use of the weapon priority list (the special value 2 uses the weapon ID list for cycling)"
  seta cl_weaponpriority0 "rpc devastator mortar hagar seeker fireball"                   "use weapon_priority_0_prev for prev gun from this list, weapon_priority_0_best for best gun, weapon_priority_0_next for next gun.  Default value: explosives"
 -seta cl_weaponpriority1 "vaporizer vortex crylink hlac arc electro blaster shockwave"   "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun.  Default value: energy"
 -seta cl_weaponpriority2 "vaporizer vortex rifle"                                        "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun.  Default value: hitscan exact"
 -seta cl_weaponpriority3 "vaporizer hmg vortex rifle machinegun shotgun shockwave"       "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun.  Default value: hitscan all"
 -seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker shotgun shockwave"  "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun.  Default value: spam weapons"
 +seta cl_weaponpriority1 "vaporizer okvortex vortex crylink hlac arc electro blaster shockwave"   "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun.  Default value: energy"
 +seta cl_weaponpriority2 "vaporizer okvortex vortex rifle"                                        "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun.  Default value: hitscan exact"
 +seta cl_weaponpriority3 "vaporizer hmg okvortex vortex rifle okmachinegun machinegun okshotgun shotgun shockwave"       "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun.  Default value: hitscan all"
 +seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker okshotgun shotgun shockwave"  "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun.  Default value: spam weapons"
  seta cl_weaponpriority5 "blaster shockwave hook porto"                                  "use weapon_priority_5_prev for prev gun from this list, weapon_priority_5_best for best gun, weapon_priority_5_next for next gun.  Default value: weapons for moving"
  seta cl_weaponpriority6 ""                                                              "use weapon_priority_6_prev for prev gun from this list, weapon_priority_6_best for best gun, weapon_priority_6_next for next gun"
  seta cl_weaponpriority7 ""                                                              "use weapon_priority_7_prev for prev gun from this list, weapon_priority_7_best for best gun, weapon_priority_7_next for next gun"
@@@ -1093,6 -1104,9 +1104,9 @@@ seta cl_autoscreenshot 1 "Take a screen
  
  seta cl_jetpack_jump 1 "Activate jetpack by pressing jump in the air. 0 = Disable, 1 = Stop when touching ground, 2 = Enable"
  
+ seta cl_race_cptimes_showself 1 "Always show your own times as well as the current best on checkpoints in Race/CTS"
+ seta cl_race_cptimes_onlyself 0 "Only show your own times on checkpoints in Race/CTS"
  // must be at the bottom of this file:
  
  set g_bugrigs 0
@@@ -1156,6 -1170,7 +1170,7 @@@ seta cl_gentle_messages 0       "client side 
  seta cl_gentle_damage 0       "client side gentle mode (only replaces damage flash); when set to 1, a white flash replaces the blood image, when set to 2, a randomly colored flash is used instead"
  
  set g_jetpack 0 "Jetpack mutator"
+ set cl_jetpack_attenuation 2 "jetpack sound attenuation"
  
  set g_running_guns 0 "... or wonder, till it drives you mad, what would have followed if you had."
  
diff --combined qcsrc/client/view.qc
index efdf2e108060e6b54753226af9fa0e09cd31bb73,ece38598ecc96dd42dc7792f70d974fdac5851c3..c779734562f7b574f56898736e7e2bb3436f193b
@@@ -26,7 -26,6 +26,7 @@@
  
  #include <common/vehicles/all.qh>
  #include <common/weapons/_all.qh>
 +#include <common/mutators/mutator/overkill/okvortex.qh>
  #include <common/viewloc.qh>
  #include <common/minigames/cl_minigames.qh>
  #include <common/minigames/cl_minigames_hud.qh>
@@@ -312,6 -311,7 +312,7 @@@ void viewmodel_draw(entity this
                e.csqcmodel_effects = fx;
                CSQCModel_Effects_Apply(e);
        }
+       if(a >= 0)
        {
                string name = wep.mdl;
                string newname = wep.wr_viewmodel(wep, this);
@@@ -467,7 -467,7 +468,7 @@@ vector GetCurrentFov(float fov
                {
                        entity wepent = viewmodels[slot];
                        if(wepent.switchweapon == wepent.activeweapon)
 -                      if((wepent.activeweapon == WEP_VORTEX && !WEP_CVAR(vortex, secondary)) || (wepent.activeweapon == WEP_RIFLE && !WEP_CVAR(rifle, secondary))) // do NOT use switchweapon here
 +                      if((wepent.activeweapon == WEP_VORTEX && !WEP_CVAR(vortex, secondary)) || (wepent.activeweapon == WEP_RIFLE && !WEP_CVAR(rifle, secondary)) || (wepent.activeweapon == WEP_OVERKILL_VORTEX && !WEP_CVAR(okvortex, secondary))) // do NOT use switchweapon here
                                zoomdir += button_attack2;
                }
        }
@@@ -659,7 -659,6 +660,7 @@@ float TrueAimCheck(entity wepent
                case WEP_MORTAR: // toss curve
                        return SHOTTYPE_HITWORLD;
                case WEP_VORTEX:
 +              case WEP_OVERKILL_VORTEX:
                case WEP_VAPORIZER:
                        mv = MOVE_NORMAL;
                        break;
@@@ -1184,10 -1183,6 +1185,10 @@@ void HUD_Crosshair(entity this
                                vortex_charge = STAT(VORTEX_CHARGE);
                                vortex_chargepool = STAT(VORTEX_CHARGEPOOL);
  
 +                              float okvortex_charge, okvortex_chargepool;
 +                              okvortex_charge = STAT(OVERKILL_VORTEX_CHARGE);
 +                              okvortex_chargepool = STAT(OVERKILL_VORTEX_CHARGEPOOL);
 +
                                float arc_heat = STAT(ARC_HEAT);
  
                                if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game
                                        ring_rgb = wcross_color;
                                        ring_image = "gfx/crosshair_ring_nexgun.tga";
                                }
 +                              else if (autocvar_crosshair_ring && (wepent.activeweapon == WEP_OVERKILL_VORTEX) && okvortex_charge && autocvar_crosshair_ring_vortex)
 +                              {
 +                                      if (okvortex_chargepool || use_vortex_chargepool) {
 +                                              use_vortex_chargepool = 1;
 +                                              ring_inner_value = okvortex_chargepool;
 +                                      } else {
 +                                              vortex_charge_movingavg = (1 - autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate) * vortex_charge_movingavg + autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate * okvortex_charge;
 +                                              ring_inner_value = bound(0, autocvar_crosshair_ring_vortex_currentcharge_scale * (okvortex_charge - vortex_charge_movingavg), 1);
 +                                      }
 +
 +                                      ring_inner_alpha = autocvar_crosshair_ring_vortex_inner_alpha;
 +                                      ring_inner_rgb = eX * autocvar_crosshair_ring_vortex_inner_color_red + eY * autocvar_crosshair_ring_vortex_inner_color_green + eZ * autocvar_crosshair_ring_vortex_inner_color_blue;
 +                                      ring_inner_image = "gfx/crosshair_ring_inner.tga";
 +
 +                                      // draw the outer ring to show the current charge of the weapon
 +                                      ring_value = okvortex_charge;
 +                                      ring_alpha = autocvar_crosshair_ring_vortex_alpha;
 +                                      ring_rgb = wcross_color;
 +                                      ring_image = "gfx/crosshair_ring_nexgun.tga";
 +                              }
                                else if (autocvar_crosshair_ring && wepent.activeweapon == WEP_MINE_LAYER && WEP_CVAR(minelayer, limit) && autocvar_crosshair_ring_minelayer)
                                {
                                        ring_value = bound(0, STAT(LAYED_MINES) / WEP_CVAR(minelayer, limit), 1); // if you later need to use the count of bullets in another place, then add a float for it. For now, no need to.
        }
  }
  
+ const int MAX_SPECIALCOMMAND = 15;
+ vector specialcommand_slots[MAX_SPECIALCOMMAND];
+ vector specialcommand_colors[MAX_SPECIALCOMMAND];
+ const float SPECIALCOMMAND_SPEED = 150;
+ const float SPECIALCOMMAND_TURNSPEED = 2;
+ const float SPECIALCOMMAND_SIZE = 0.025;
+ const float SPECIALCOMMAND_CHANCE = 0.35;
+ float sc_spawntime, sc_changetime;
+ vector sc_color = '1 1 1';
+ void SpecialCommand()
+ {
+       if(!STAT(MOVEVARS_SPECIALCOMMAND))
+               return;
+       if(time >= sc_changetime)
+       {
+               sc_changetime = time + 1;
+               sc_color = randomvec() * 1.5;
+               sc_color.x = bound(0.2, sc_color.x, 0.75);
+               sc_color.y = bound(0.2, sc_color.y, 0.75);
+               sc_color.z = bound(0.2, sc_color.z, 0.75);
+       }
+       drawfill('0 0 0', vec2(vid_conwidth, vid_conheight), sc_color, autocvar_hud_colorflash_alpha * bound(0.1, sc_changetime - time, 0.3), DRAWFLAG_ADDITIVE);
+       if(!precache_pic("gfx/smile"))
+               return; // damn party poopers
+       for(int j = MAX_SPECIALCOMMAND - 1; j >= 0; --j)
+       {
+               vector slot = specialcommand_slots[j];
+               if(slot.y)
+                       slot.y += SPECIALCOMMAND_SPEED * frametime;
+               if(slot.z)
+                       slot.z = sin(SPECIALCOMMAND_TURNSPEED * M_PI * time);
+               if(slot.y >= vid_conheight)
+                       slot = '0 0 0';
+               if(slot == '0 0 0')
+               {
+                       if(random() <= SPECIALCOMMAND_CHANCE && time > sc_spawntime) // low chance to spawn!
+                       {
+                               slot.x = bound(0, (random() * vid_conwidth + 1), vid_conwidth);
+                               slot.y = 1; // start it off 0 so we can use it
+                               slot.z = random();
+                               sc_spawntime = time + bound(0.4, random(), 0.75); // prevent spawning another one for this amount of time!
+                               vector newcolor = randomvec() * 2;
+                               newcolor.x = bound(0.4, newcolor.x, 1);
+                               newcolor.y = bound(0.4, newcolor.y, 1);
+                               newcolor.z = bound(0.4, newcolor.z, 1);
+                               specialcommand_colors[j] = newcolor;
+                       }
+               }
+               else
+               {
+                       vector splash_size = '0 0 0';
+                       splash_size.x = max(vid_conwidth, vid_conheight) * SPECIALCOMMAND_SIZE;
+                       splash_size.y = max(vid_conwidth, vid_conheight) * SPECIALCOMMAND_SIZE;
+                       drawpic(vec2(slot), "gfx/smile", vec2(splash_size), specialcommand_colors[j], 0.95, DRAWFLAG_NORMAL);
+                       //drawrotpic(vec2(slot), slot.z, "gfx/smile", vec2(splash_size), vec2(splash_size) / 2, specialcommand_colors[j], 0.95, DRAWFLAG_NORMAL);
+               }
+               specialcommand_slots[j] = slot;
+       }
+ }
  void HUD_Draw(entity this)
  {
        // if we don't know gametype and scores yet avoid drawing the scoreboard
                }
  
        // crosshair goes VERY LAST
+       SpecialCommand();
        UpdateDamage();
        HUD_Crosshair(this);
        HitSound();
@@@ -2071,7 -2112,7 +2138,7 @@@ void CSQC_UpdateView(entity this, floa
  
  
        // improved polyblend
-       if(autocvar_hud_contents)
+       if(autocvar_hud_contents && !MUTATOR_CALLHOOK(HUD_Contents))
        {
                float contentalpha_temp, incontent, liquidalpha, contentfadetime;
                vector liquidcolor;
        // draw 2D entities
        IL_EACH(g_drawables_2d, it.draw2d, it.draw2d(it));
        Draw_ShowNames_All();
- #ifdef DEBUGDRAW
+ #if ENABLE_DEBUGDRAW
        Debug_Draw();
  #endif
  
index afca9517cfb74f28e743c85db632e651a25816f0,e9a5ce2c3fc01b3a39a0ab055234b5f23170b4fd..536435d34ff3c48cc94ed797eabbbb7042708007
@@@ -8,9 -8,9 +8,9 @@@ void W_RocketPropelledChainsaw_Explode(
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
  
 -      RadiusDamage (this, this.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), NULL, NULL, WEP_CVAR(rpc, force), this.projectiledeathtype, directhitentity);
 +      RadiusDamage (this, this.realowner, WEP_CVAR_PRI(rpc, damage), WEP_CVAR_PRI(rpc, edgedamage), WEP_CVAR_PRI(rpc, radius), NULL, NULL, WEP_CVAR_PRI(rpc, force), this.projectiledeathtype, directhitentity);
  
-       delete (this);
+       delete(this);
  }
  
  void W_RocketPropelledChainsaw_Explode_think(entity this)
@@@ -55,9 -55,9 +55,9 @@@ void W_RocketPropelledChainsaw_Think(en
  
        tracebox(this.origin, this.mins, this.maxs, this.origin + this.pos1 * (2 * this.wait), MOVE_NORMAL, this);
        if(IS_PLAYER(trace_ent))
 -              Damage (trace_ent, this, this.realowner, WEP_CVAR(rpc, damage2), this.projectiledeathtype, this.origin, normalize(this.origin - trace_ent.origin) * WEP_CVAR(rpc, force));
 +              Damage (trace_ent, this, this.realowner, WEP_CVAR_PRI(rpc, damage2), this.projectiledeathtype, this.origin, normalize(this.origin - other.origin) * WEP_CVAR_PRI(rpc, force));
  
 -      this.velocity = this.pos1 * (this.cnt + (WEP_CVAR(rpc, speedaccel) * sys_frametime));
 +      this.velocity = this.pos1 * (this.cnt + (WEP_CVAR_PRI(rpc, speedaccel) * sys_frametime));
  
        UpdateCSQCProjectile(this);
        this.nextthink = time;
@@@ -68,18 -68,18 +68,18 @@@ void W_RocketPropelledChainsaw_Attack (
        entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(actor);
        entity flash = spawn ();
  
 -      W_DecreaseAmmo(thiswep, actor, WEP_CVAR(rpc, ammo), weaponentity);
 -      W_SetupShot_ProjectileSize (actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(rpc, damage));
 +      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(rpc, ammo), weaponentity);
 +      W_SetupShot_ProjectileSize (actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(rpc, damage));
        Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
        PROJECTILE_MAKETRIGGER(missile);
  
        missile.owner = missile.realowner = actor;
        missile.bot_dodge = true;
 -      missile.bot_dodgerating = WEP_CVAR(rpc, damage) * 2;
 +      missile.bot_dodgerating = WEP_CVAR_PRI(rpc, damage) * 2;
  
        missile.takedamage = DAMAGE_YES;
 -      missile.damageforcescale = WEP_CVAR(rpc, damageforcescale);
 -      missile.health = WEP_CVAR(rpc, health);
 +      missile.damageforcescale = WEP_CVAR_PRI(rpc, damageforcescale);
 +      missile.health = WEP_CVAR_PRI(rpc, health);
        missile.event_damage = W_RocketPropelledChainsaw_Damage;
        missile.damagedbycontents = true;
        IL_PUSH(g_damagedbycontents, missile);
        setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
  
        setorigin(missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point
 -      W_SetupProjVelocity_Basic(missile, WEP_CVAR(rpc, speed), 0);
 +      W_SetupProjVelocity_Basic(missile, WEP_CVAR_PRI(rpc, speed), 0);
  
        settouch(missile, W_RocketPropelledChainsaw_Touch);
  
        setthink(missile, W_RocketPropelledChainsaw_Think);
 -      missile.cnt = time + WEP_CVAR(rpc, lifetime);
 +      missile.cnt = time + WEP_CVAR_PRI(rpc, lifetime);
        missile.nextthink = time;
        missile.flags = FL_PROJECTILE;
        IL_PUSH(g_projectiles, missile);
  
  METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
  {
 -    PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false);
 +    PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(rpc, speed), 0, WEP_CVAR_PRI(rpc, lifetime), false);
  }
  
  METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
  {
 -    if(WEP_CVAR(rpc, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR(rpc, ammo)) {
 -        thiswep.wr_reload(thiswep, actor, weaponentity);
 -    } else
 -    {
 -        if (fire & 1)
 -        {
 -            if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(rpc, refire)))
 -            {
 -                W_RocketPropelledChainsaw_Attack(thiswep, actor, weaponentity);
 -                weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready);
 -            }
 -        }
 -
 -        if (fire & 2)
 -        {
 -            // to-do
 -        }
 -    }
 +      if ((WEP_CVAR_SEC(rpc, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval))
 +      {
 +              // Secondary uses it's own refire timer if refire_type is 1.
 +              actor.jump_interval = time + WEP_CVAR_SEC(rpc, refire) * W_WeaponRateFactor(actor);
 +              // Ugly hack to reuse the fire mode of the blaster.
 +              makevectors(actor.v_angle);
 +              Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
 +              actor.(weaponentity).m_weapon = WEP_BLASTER;
 +              W_Blaster_Attack(
 +                      actor,
 +                      weaponentity,
 +                      WEP_BLASTER.m_id | HITTYPE_SECONDARY,
 +                      WEP_CVAR_SEC(rpc, shotangle),
 +                      WEP_CVAR_SEC(rpc, damage),
 +                      WEP_CVAR_SEC(rpc, edgedamage),
 +                      WEP_CVAR_SEC(rpc, radius),
 +                      WEP_CVAR_SEC(rpc, force),
 +                      WEP_CVAR_SEC(rpc, speed),
 +                      WEP_CVAR_SEC(rpc, spread),
 +                      WEP_CVAR_SEC(rpc, delay),
 +                      WEP_CVAR_SEC(rpc, lifetime)
 +              );
 +              actor.(weaponentity).m_weapon = oldwep;
 +              if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
 +                      (actor.(weaponentity).wframe == WFRAME_FIRE2))
 +              {
 +                      // Set secondary fire animation.
 +                      vector a = '0 0 0';
 +                      actor.(weaponentity).wframe = WFRAME_FIRE2;
 +                      a = actor.(weaponentity).anim_fire2;
 +                      a.z *= g_weaponratefactor;
 +                      FOREACH_CLIENT(true, LAMBDA(
 +                              if (it == actor || (IS_SPEC(it) && it.enemy == actor))
 +                              {
 +                                      wframe_send(it, actor.(weaponentity), a, true);
 +                              }
 +                      ));
 +                      animdecide_setaction(actor, ANIMACTION_SHOOT, true);
 +              }
 +      }
 +      if (WEP_CVAR(rpc, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(rpc, ammo))
 +      {
 +              // Forced reload
 +              thiswep.wr_reload(thiswep, actor, weaponentity);
 +              return;
 +      }
 +      if (fire & 1) // Primary attack
 +      {
 +              if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(rpc, refire)))
 +              {
 +                      return;
 +              }
 +              W_RocketPropelledChainsaw_Attack(thiswep, actor, weaponentity);
 +              weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(rpc, animtime), w_ready);
 +              return;
 +      }
 +      if ((fire & 2) && (WEP_CVAR_SEC(rpc, refire_type) == 0)) // Secondary attack
 +      {
 +              if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(rpc, refire)))
 +              {
 +                      return;
 +              }
 +              // ugly instagib hack to reuse the fire mode of the laser
 +              makevectors(actor.v_angle);
 +              Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
 +              actor.(weaponentity).m_weapon = WEP_BLASTER;
 +              W_Blaster_Attack(
 +                      actor,
 +                      weaponentity,
 +                      WEP_BLASTER.m_id | HITTYPE_SECONDARY,
 +                      WEP_CVAR_SEC(rpc, shotangle),
 +                      WEP_CVAR_SEC(rpc, damage),
 +                      WEP_CVAR_SEC(rpc, edgedamage),
 +                      WEP_CVAR_SEC(rpc, radius),
 +                      WEP_CVAR_SEC(rpc, force),
 +                      WEP_CVAR_SEC(rpc, speed),
 +                      WEP_CVAR_SEC(rpc, spread),
 +                      WEP_CVAR_SEC(rpc, delay),
 +                      WEP_CVAR_SEC(rpc, lifetime)
 +              );
 +              actor.(weaponentity).m_weapon = oldwep;
 +              weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(rpc, animtime), w_ready);
 +      }
  }
  
  METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
  {
 -    float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(rpc, ammo);
 -    ammo_amount += actor.(weaponentity).(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo);
 +    float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(rpc, ammo);
 +    ammo_amount += actor.(weaponentity).(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR_PRI(rpc, ammo);
      return ammo_amount;
  }
  
  METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
  {
 -    return false;
 +    float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(rpc, ammo);
 +    ammo_amount += actor.(weaponentity).(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR_SEC(rpc, ammo);
 +    return ammo_amount;
  }
  
  METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
  {
 -    W_Reload(actor, weaponentity, WEP_CVAR(rpc, ammo), SND_RELOAD);
 +    W_Reload(actor, weaponentity, WEP_CVAR_PRI(rpc, ammo), SND_RELOAD);
  }
  
  METHOD(RocketPropelledChainsaw, wr_suicidemessage, Notification(entity thiswep))
diff --combined qcsrc/common/stats.qh
index 8b6b1cfdf6a12b4137a488ddd8503d50246d3c11,c1503ca68b5b167f414992d97a405cbbec5dc8d2..d43b4333cb9a3e45a56e57cff18570ac50496456
@@@ -41,8 -41,11 +41,11 @@@ const int MAX_CL_STATS = 256
      #define stat_VIEWHEIGHT view_ofs_z
  #endif
  
+ #ifdef SVQC
+ vector weaponsInMap;
+ #endif
  REGISTER_STAT(WEAPONS, vectori)
- REGISTER_STAT(WEAPONSINMAP, vectori)
+ REGISTER_STAT(WEAPONSINMAP, vectori, weaponsInMap)
  
  REGISTER_STAT(PL_VIEW_OFS, vector)
  REGISTER_STAT(PL_CROUCH_VIEW_OFS, vector)
@@@ -58,31 -61,33 +61,35 @@@ REGISTER_STAT(KH_KEYS, int
  #ifdef SVQC
  float W_WeaponRateFactor(entity this);
  float game_stopped;
+ float game_starttime;
+ float round_starttime;
+ bool autocvar_g_allow_oldvortexbeam;
+ int autocvar_leadlimit;
  #endif
  REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
  REGISTER_STAT(GAME_STOPPED, int, game_stopped)
- REGISTER_STAT(GAMESTARTTIME, float)
+ REGISTER_STAT(GAMESTARTTIME, float, game_starttime)
  REGISTER_STAT(STRENGTH_FINISHED, float)
  REGISTER_STAT(INVINCIBLE_FINISHED, float)
  /** arc heat in [0,1] */
  REGISTER_STAT(ARC_HEAT, float)
  REGISTER_STAT(PRESSED_KEYS, int)
  /** this stat could later contain some other bits of info, like, more server-side particle config */
- REGISTER_STAT(ALLOW_OLDVORTEXBEAM, bool)
+ REGISTER_STAT(ALLOW_OLDVORTEXBEAM, bool, autocvar_g_allow_oldvortexbeam)
  REGISTER_STAT(FUEL, int)
  REGISTER_STAT(NB_METERSTART, float)
  /** compressShotOrigin */
  REGISTER_STAT(SHOTORG, int)
- REGISTER_STAT(LEADLIMIT, float)
+ REGISTER_STAT(LEADLIMIT, float, autocvar_leadlimit)
  REGISTER_STAT(WEAPON_CLIPLOAD, int)
  REGISTER_STAT(WEAPON_CLIPSIZE, int)
  
  REGISTER_STAT(VORTEX_CHARGE, float)
 +REGISTER_STAT(OVERKILL_VORTEX_CHARGE, float)
  REGISTER_STAT(LAST_PICKUP, float)
  REGISTER_STAT(HUD, int)
  REGISTER_STAT(VORTEX_CHARGEPOOL, float)
 +REGISTER_STAT(OVERKILL_VORTEX_CHARGEPOOL, float)
  REGISTER_STAT(HIT_TIME, float)
  REGISTER_STAT(DAMAGE_DEALT_TOTAL, int)
  REGISTER_STAT(TYPEHIT_TIME, float)
@@@ -101,7 -106,7 +108,7 @@@ REGISTER_STAT(NADE_TIMER, float
  REGISTER_STAT(SECRETS_TOTAL, float)
  REGISTER_STAT(SECRETS_FOUND, float)
  REGISTER_STAT(RESPAWN_TIME, float)
- REGISTER_STAT(ROUNDSTARTTIME, float)
+ REGISTER_STAT(ROUNDSTARTTIME, float, round_starttime)
  REGISTER_STAT(MONSTERS_TOTAL, int)
  REGISTER_STAT(MONSTERS_KILLED, int)
  REGISTER_STAT(BUFFS, int)
@@@ -336,6 -341,7 +343,7 @@@ REGISTER_STAT(MOVEVARS_MAXAIRSPEED, flo
  REGISTER_STAT(MOVEVARS_STEPHEIGHT, float, autocvar_sv_stepheight)
  REGISTER_STAT(MOVEVARS_AIRACCEL_QW, float)
  REGISTER_STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, float)
+ REGISTER_STAT(MOVEVARS_SPECIALCOMMAND, bool)
  
  
  #ifdef CSQC
index b873a9aeb99525aa31f9c54d71d8d29003328a6a,87a24d13a3a96a2d3985073f911f70212d13c956..3d2bb7d097c8945dfc6c5918eeac9c6f62bf8eaf
@@@ -59,15 -59,15 +59,15 @@@ REGISTER_WEAPON(SHOTGUN, shotgun, NEW(S
  #ifdef SVQC
  spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); }
  
 -void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary)
 +void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary, float ammocount, float damage, float bullets, float spread, float solidpenetration, float force)
  {
 -      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(shotgun, ammo), weaponentity);
 +      W_DecreaseAmmo(thiswep, actor, ammocount, weaponentity);
  
 -      W_SetupShot(actor, weaponentity, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets));
 -      for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1)
 -              fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR_PRI(shotgun, spread), WEP_CVAR_PRI(shotgun, solidpenetration), WEP_CVAR_PRI(shotgun, damage), WEP_CVAR_PRI(shotgun, force), WEP_SHOTGUN.m_id, 0);
 +      W_SetupShot(actor, weaponentity, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), damage * bullets);
 +      for(int sc = 0;sc < bullets;sc = sc + 1)
 +              fireBullet(actor, weaponentity, w_shotorg, w_shotdir, spread, solidpenetration, damage, force, WEP_SHOTGUN.m_id, 0);
  
 -      Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo));
 +      Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, ammocount);
  
        // casing code
        if(autocvar_g_casings >= 1)
@@@ -125,7 -125,7 +125,7 @@@ void W_Shotgun_Melee_Think(entity this
                        + (v_up * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_up))
                        + (v_right * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_side)));
  
-               WarpZone_traceline_antilag(this, this.realowner.origin + this.realowner.view_ofs, targpos, false, this.realowner, ANTILAG_LATENCY(this.realowner));
+               WarpZone_traceline_antilag(this, this.realowner.origin + this.realowner.view_ofs, targpos, false, this.realowner, ((IS_CLIENT(this.realowner)) ? ANTILAG_LATENCY(this.realowner) : 0));
  
                // draw lightning beams for debugging
                //te_lightning2(NULL, targpos, this.realowner.origin + this.realowner.view_ofs + v_forward * 5 - v_up * 5);
@@@ -208,13 -208,7 +208,13 @@@ void W_Shotgun_Attack3_Frame2(Weapon th
        }
  
        sound(actor, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound
 -      W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, true); // actually is secondary, but we trick the last shot into playing full reload sound
 +      W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, true,
 +              WEP_CVAR_PRI(shotgun, ammo),
 +              WEP_CVAR_PRI(shotgun, damage),
 +              WEP_CVAR_PRI(shotgun, bullets),
 +              WEP_CVAR_PRI(shotgun, spread),
 +              WEP_CVAR_PRI(shotgun, solidpenetration),
 +              WEP_CVAR_PRI(shotgun, force)); // actually is secondary, but we trick the last shot into playing full reload sound
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready);
  }
  void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire)
                return;
        }
  
 -      W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, false);
 +      W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, false,
 +              WEP_CVAR_PRI(shotgun, ammo),
 +              WEP_CVAR_PRI(shotgun, damage),
 +              WEP_CVAR_PRI(shotgun, bullets),
 +              WEP_CVAR_PRI(shotgun, spread),
 +              WEP_CVAR_PRI(shotgun, solidpenetration),
 +              WEP_CVAR_PRI(shotgun, force));
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2);
  }
  
@@@ -246,7 -234,6 +246,7 @@@ METHOD(Shotgun, wr_aim, void(entity thi
      else
          PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
  }
 +
  METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
  {
      if(WEP_CVAR(shotgun, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload
              {
                  if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(shotgun, animtime)))
                  {
 -                    W_Shotgun_Attack(thiswep, actor, weaponentity, true);
 +                    W_Shotgun_Attack(thiswep, actor, weaponentity, true,
 +                                              WEP_CVAR_PRI(shotgun, ammo),
 +                                              WEP_CVAR_PRI(shotgun, damage),
 +                                              WEP_CVAR_PRI(shotgun, bullets),
 +                                              WEP_CVAR_PRI(shotgun, spread),
 +                                              WEP_CVAR_PRI(shotgun, solidpenetration),
 +                                              WEP_CVAR_PRI(shotgun, force));
                      actor.(weaponentity).shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(actor);
                      weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready);
                  }
              {
                  if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(shotgun, alt_animtime)))
                  {
 -                    W_Shotgun_Attack(thiswep, actor, weaponentity, false);
 +                    W_Shotgun_Attack(thiswep, actor, weaponentity, false,
 +                                              WEP_CVAR_PRI(shotgun, ammo),
 +                                              WEP_CVAR_PRI(shotgun, damage),
 +                                              WEP_CVAR_PRI(shotgun, bullets),
 +                                              WEP_CVAR_PRI(shotgun, spread),
 +                                              WEP_CVAR_PRI(shotgun, solidpenetration),
 +                                              WEP_CVAR_PRI(shotgun, force));
                      actor.(weaponentity).shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(actor);
                      weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1);
                  }
diff --combined qcsrc/server/client.qc
index 7401c2cd1a1f7f141f0a392f82d28154bb7f79e8,804868fa0031ac041b3e24cf65ae737fb0bed114..d8a8c3c52e5f4aeeffa9274870b324eb5b2eaedd
@@@ -56,8 -56,6 +56,8 @@@
  
  #include "../lib/warpzone/server.qh"
  
 +#include <common/mutators/mutator/overkill/okvortex.qh>
 +
  STATIC_METHOD(Client, Add, void(Client this, int _team))
  {
      ClientConnect(this);
@@@ -112,9 -110,9 +112,9 @@@ bool ClientData_Send(entity this, entit
        if (IS_SPEC(e)) e = e.enemy;
  
        sf = 0;
-       if (e.race_completed)       sf |= 1; // forced scoreboard
-       if (to.spectatee_status)    sf |= 2; // spectator ent number follows
-       if (e.zoomstate)            sf |= 4; // zoomed
+       if (CS(e).race_completed)       sf |= 1; // forced scoreboard
+       if (CS(to).spectatee_status)    sf |= 2; // spectator ent number follows
+       if (CS(e).zoomstate)            sf |= 4; // zoomed
        if (autocvar_sv_showspectators) sf |= 16; // show spectators
  
        WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
  
        if (sf & 2)
        {
-               WriteByte(MSG_ENTITY, to.spectatee_status);
+               WriteByte(MSG_ENTITY, CS(to).spectatee_status);
        }
  
        if(sf & 16)
  
  void ClientData_Attach(entity this)
  {
-       Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send);
-       this.clientdata.drawonlytoclient = this;
-       this.clientdata.owner = this;
+       Net_LinkEntity(CS(this).clientdata = new_pure(clientdata), false, 0, ClientData_Send);
+       CS(this).clientdata.drawonlytoclient = this;
+       CS(this).clientdata.owner = this;
  }
  
  void ClientData_Detach(entity this)
  {
-       delete(this.clientdata);
-       this.clientdata = NULL;
+       delete(CS(this).clientdata);
+       CS(this).clientdata = NULL;
  }
  
  void ClientData_Touch(entity e)
  {
-       e.clientdata.SendFlags = 1;
+       CS(e).clientdata.SendFlags = 1;
  
        // make it spectatable
-       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1));
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(CS(it).clientdata.SendFlags = 1));
  }
  
- .string netname_previous;
  void SetSpectatee(entity this, entity spectatee);
  void SetSpectatee_status(entity this, int spectatee_num);
  
@@@ -229,12 -225,10 +227,10 @@@ void PutObserverInServer(entity this
      {
          entity spot = SelectSpawnPoint(this, true);
          if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
-         this.angles = spot.angles;
-         this.angles_z = 0;
+         this.angles = vec2(spot.angles);
          this.fixangle = true;
          // offset it so that the spectator spawns higher off the ground, looks better this way
          setorigin(this, spot.origin + STAT(PL_VIEW_OFS, this));
-         this.prevorigin = this.origin;
          if (IS_REAL_CLIENT(this))
          {
              msg_entity = this;
          PlayerScore_Clear(this);  // clear scores when needed
      }
  
-       if (this.killcount != FRAGS_SPECTATOR)
+       if (CS(this).killcount != FRAGS_SPECTATOR)
        {
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
                if(!game_stopped)
                if(autocvar_g_chat_nospectators == 1 || (!warmup_stage && autocvar_g_chat_nospectators == 2))
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
  
-               if(this.just_joined == false) {
+               if(!CS(this).just_joined)
                        LogTeamchange(this.playerid, -1, 4);
-               else
-                       this.just_joined = false;
+               else
+                       CS(this).just_joined = false;
        }
  
        accuracy_resend(this);
  
-       this.spectatortime = time;
+       CS(this).spectatortime = time;
        if(this.bot_attack)
                IL_REMOVE(g_bot_targets, this);
        this.bot_attack = false;
  
        this.items = 0;
        this.weapons = '0 0 0';
-       this.dual_weapons = '0 0 0';
        this.drawonlytoclient = this;
  
        this.weaponmodel = "";
                this.weaponentities[slot] = NULL;
        }
        this.exteriorweaponentity = NULL;
-       this.killcount = FRAGS_SPECTATOR;
+       CS(this).killcount = FRAGS_SPECTATOR;
        this.velocity = '0 0 0';
        this.avelocity = '0 0 0';
        this.punchangle = '0 0 0';
@@@ -404,7 -397,7 +399,7 @@@ void FixPlayermodel(entity player
                int n = tokenize_console(defaultmodel);
                if(n > 0)
                {
-                       defaultmodel = argv(floor(n * player.model_randomizer));
+                       defaultmodel = argv(floor(n * CS(player).model_randomizer));
                        // However, do NOT randomize if the player-selected model is in the list.
                        for (int i = 0; i < n; ++i)
                                if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin))
                                setcolor(player, stof(autocvar_sv_defaultplayercolors));
  }
  
- /** Called when a client spawns in the server */
- void PutClientInServer(entity this)
+ void PutPlayerInServer(entity this)
  {
-       if (IS_BOT_CLIENT(this)) {
-               TRANSMUTE(Player, this);
-       } else if (IS_REAL_CLIENT(this)) {
-               msg_entity = this;
-               WriteByte(MSG_ONE, SVC_SETVIEW);
-               WriteEntity(MSG_ONE, this);
+       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+       PlayerState_attach(this);
+       accuracy_resend(this);
+       if (this.team < 0)
+               JoinBestTeam(this, false, true);
+       entity spot = SelectSpawnPoint(this, false);
+       if (!spot) {
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
+               return; // spawn failed
        }
-       if (game_stopped)
-               TRANSMUTE(Observer, this);
  
-       SetSpectatee(this, NULL);
+       TRANSMUTE(Player, this);
  
-       // reset player keys
-       this.itemkeys = 0;
+       CS(this).wasplayer = true;
+       this.iscreature = true;
+       this.teleportable = TELEPORT_NORMAL;
+       if(!this.damagedbycontents)
+               IL_PUSH(g_damagedbycontents, this);
+       this.damagedbycontents = true;
+       set_movetype(this, MOVETYPE_WALK);
+       this.solid = SOLID_SLIDEBOX;
+       this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
+       if (autocvar_g_playerclip_collisions)
+               this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
+       if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions)
+               this.dphitcontentsmask |= DPCONTENTS_BOTCLIP;
+       this.frags = FRAGS_PLAYER;
+       if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this);
+       this.flags = FL_CLIENT | FL_PICKUPITEMS;
+       if (autocvar__notarget)
+               this.flags |= FL_NOTARGET;
+       this.takedamage = DAMAGE_AIM;
+       this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
+       this.dmg = 2; // WTF
+       if (warmup_stage) {
+               this.ammo_shells = warmup_start_ammo_shells;
+               this.ammo_nails = warmup_start_ammo_nails;
+               this.ammo_rockets = warmup_start_ammo_rockets;
+               this.ammo_cells = warmup_start_ammo_cells;
+               this.ammo_plasma = warmup_start_ammo_plasma;
+               this.ammo_fuel = warmup_start_ammo_fuel;
+               this.health = warmup_start_health;
+               this.armorvalue = warmup_start_armorvalue;
+               this.weapons = WARMUP_START_WEAPONS;
+       } else {
+               this.ammo_shells = start_ammo_shells;
+               this.ammo_nails = start_ammo_nails;
+               this.ammo_rockets = start_ammo_rockets;
+               this.ammo_cells = start_ammo_cells;
+               this.ammo_plasma = start_ammo_plasma;
+               this.ammo_fuel = start_ammo_fuel;
+               this.health = start_health;
+               this.armorvalue = start_armorvalue;
+               this.weapons = start_weapons;
+       }
+       SetSpectatee_status(this, 0);
+       PS(this).dual_weapons = '0 0 0';
+       this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0;
+       this.items = start_items;
+       this.spawnshieldtime = time + autocvar_g_spawnshieldtime;
+       this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
+       this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
+       this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
+       this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
+       // extend the pause of rotting if client was reset at the beginning of the countdown
+       if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted?
+               float f = game_starttime - time;
+               this.spawnshieldtime += f;
+               this.pauserotarmor_finished += f;
+               this.pauserothealth_finished += f;
+               this.pauseregen_finished += f;
+       }
+       this.damageforcescale = 2;
+       this.death_time = 0;
+       this.respawn_flags = 0;
+       this.respawn_time = 0;
+       this.stat_respawn_time = 0;
+       this.scale = autocvar_sv_player_scale;
+       this.fade_time = 0;
+       this.pain_frame = 0;
+       this.pain_finished = 0;
+       this.pushltime = 0;
+       setthink(this, func_null); // players have no think function
+       this.nextthink = 0;
+       this.dmg_team = 0;
+       PS(this).ballistics_density = autocvar_g_ballistics_density_player;
  
-       MUTATOR_CALLHOOK(PutClientInServer, this);
+       this.deadflag = DEAD_NO;
  
-       if (IS_OBSERVER(this)) {
-               PutObserverInServer(this);
-       } else if (IS_PLAYER(this)) {
-               if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+       this.angles = spot.angles;
+       this.angles_z = 0; // never spawn tilted even if the spot says to
+       if (IS_BOT_CLIENT(this))
+               this.v_angle = this.angles;
+       this.fixangle = true; // turn this way immediately
+       this.oldvelocity = this.velocity = '0 0 0';
+       this.avelocity = '0 0 0';
+       this.punchangle = '0 0 0';
+       this.punchvector = '0 0 0';
  
-               PlayerState_attach(this);
-               accuracy_resend(this);
+       this.strength_finished = 0;
+       this.invincible_finished = 0;
+       this.fire_endtime = -1;
+       this.revive_progress = 0;
+       this.revival_time = 0;
+       this.air_finished = time + 12;
  
-               if (this.team < 0)
-                       JoinBestTeam(this, false, true);
+       entity spawnevent = new_pure(spawnevent);
+       spawnevent.owner = this;
+       Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
  
-               entity spot = SelectSpawnPoint(this, false);
-               if (!spot) {
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
-                       return; // spawn failed
-               }
+       // Cut off any still running player sounds.
+       stopsound(this, CH_PLAYER_SINGLE);
  
-               TRANSMUTE(Player, this);
+       this.model = "";
+       FixPlayermodel(this);
+       this.drawonlytoclient = NULL;
  
-               this.wasplayer = true;
-               this.iscreature = true;
-               this.teleportable = TELEPORT_NORMAL;
-               if(!this.damagedbycontents)
-                       IL_PUSH(g_damagedbycontents, this);
-               this.damagedbycontents = true;
-               set_movetype(this, MOVETYPE_WALK);
-               this.solid = SOLID_SLIDEBOX;
-               this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
-               if (autocvar_g_playerclip_collisions)
-                       this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
-               if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions)
-                       this.dphitcontentsmask |= DPCONTENTS_BOTCLIP;
-               this.frags = FRAGS_PLAYER;
-               if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this);
-               this.flags = FL_CLIENT | FL_PICKUPITEMS;
-               if (autocvar__notarget)
-                       this.flags |= FL_NOTARGET;
-               this.takedamage = DAMAGE_AIM;
-               this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
-               this.dmg = 2; // WTF
-               if (warmup_stage) {
-                       this.ammo_shells = warmup_start_ammo_shells;
-                       this.ammo_nails = warmup_start_ammo_nails;
-                       this.ammo_rockets = warmup_start_ammo_rockets;
-                       this.ammo_cells = warmup_start_ammo_cells;
-                       this.ammo_plasma = warmup_start_ammo_plasma;
-                       this.ammo_fuel = warmup_start_ammo_fuel;
-                       this.health = warmup_start_health;
-                       this.armorvalue = warmup_start_armorvalue;
-                       this.weapons = WARMUP_START_WEAPONS;
-               } else {
-                       this.ammo_shells = start_ammo_shells;
-                       this.ammo_nails = start_ammo_nails;
-                       this.ammo_rockets = start_ammo_rockets;
-                       this.ammo_cells = start_ammo_cells;
-                       this.ammo_plasma = start_ammo_plasma;
-                       this.ammo_fuel = start_ammo_fuel;
-                       this.health = start_health;
-                       this.armorvalue = start_armorvalue;
-                       this.weapons = start_weapons;
-               }
-               SetSpectatee_status(this, 0);
-               this.dual_weapons = '0 0 0';
-               this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0;
-               this.items = start_items;
-               this.spawnshieldtime = time + autocvar_g_spawnshieldtime;
-               this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
-               this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
-               this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
-               this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
-               // extend the pause of rotting if client was reset at the beginning of the countdown
-               if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted?
-                       float f = game_starttime - time;
-                       this.spawnshieldtime += f;
-                       this.pauserotarmor_finished += f;
-                       this.pauserothealth_finished += f;
-                       this.pauseregen_finished += f;
-               }
-               this.damageforcescale = 2;
-               this.death_time = 0;
-               this.respawn_flags = 0;
-               this.respawn_time = 0;
-               this.stat_respawn_time = 0;
-               this.scale = autocvar_sv_player_scale;
-               this.fade_time = 0;
-               this.pain_frame = 0;
-               this.pain_finished = 0;
-               this.pushltime = 0;
-               setthink(this, func_null); // players have no think function
-               this.nextthink = 0;
-               this.dmg_team = 0;
-               this.ballistics_density = autocvar_g_ballistics_density_player;
-               this.deadflag = DEAD_NO;
-               this.angles = spot.angles;
-               this.angles_z = 0; // never spawn tilted even if the spot says to
-               if (IS_BOT_CLIENT(this))
-                       this.v_angle = this.angles;
-               this.fixangle = true; // turn this way immediately
-               this.oldvelocity = this.velocity = '0 0 0';
-               this.avelocity = '0 0 0';
-               this.punchangle = '0 0 0';
-               this.punchvector = '0 0 0';
-               this.strength_finished = 0;
-               this.invincible_finished = 0;
-               this.fire_endtime = -1;
-               this.revive_progress = 0;
-               this.revival_time = 0;
-               this.air_finished = time + 12;
-               entity spawnevent = new_pure(spawnevent);
-               spawnevent.owner = this;
-               Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
-               // Cut off any still running player sounds.
-               stopsound(this, CH_PLAYER_SINGLE);
-               this.model = "";
-               FixPlayermodel(this);
-               this.drawonlytoclient = NULL;
-               this.viewloc = NULL;
-               this.crouch = false;
-               this.view_ofs = STAT(PL_VIEW_OFS, this);
-               setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
-               this.spawnorigin = spot.origin;
-               setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
-               // don't reset back to last position, even if new position is stuck in solid
-               this.oldorigin = this.origin;
-               this.prevorigin = this.origin;
-               this.lastteleporttime = time; // prevent insane speeds due to changing origin
-               if(this.conveyor)
-                       IL_REMOVE(g_conveyed, this);
-               this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
-               this.hud = HUD_NORMAL;
-               this.event_damage = PlayerDamage;
-               if(!this.bot_attack)
-                       IL_PUSH(g_bot_targets, this);
-               this.bot_attack = true;
-               if(!this.monster_attack)
-                       IL_PUSH(g_monster_targets, this);
-               this.monster_attack = true;
-               navigation_dynamicgoal_init(this, false);
-               PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
-               if (this.killcount == FRAGS_SPECTATOR) {
-                       PlayerScore_Clear(this);
-                       this.killcount = 0;
-               }
+       this.viewloc = NULL;
  
-               for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       .entity weaponentity = weaponentities[slot];
-                       entity oldwep = this.(weaponentity);
-                       CL_SpawnWeaponentity(this, weaponentity);
-                       if(oldwep && oldwep.owner == this)
-                               this.(weaponentity).m_gunalign = oldwep.m_gunalign;
-               }
-               this.alpha = default_player_alpha;
-               this.colormod = '1 1 1' * autocvar_g_player_brightness;
-               this.exteriorweaponentity.alpha = default_weapon_alpha;
+       this.crouch = false;
+       this.view_ofs = STAT(PL_VIEW_OFS, this);
+       setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
+       this.spawnorigin = spot.origin;
+       setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
+       // don't reset back to last position, even if new position is stuck in solid
+       this.oldorigin = this.origin;
+       this.lastteleporttime = time; // prevent insane speeds due to changing origin
+       if(this.conveyor)
+               IL_REMOVE(g_conveyed, this);
+       this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
+       this.hud = HUD_NORMAL;
+       this.event_damage = PlayerDamage;
+       if(!this.bot_attack)
+               IL_PUSH(g_bot_targets, this);
+       this.bot_attack = true;
+       if(!this.monster_attack)
+               IL_PUSH(g_monster_targets, this);
+       this.monster_attack = true;
+       navigation_dynamicgoal_init(this, false);
+       PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
+       if (CS(this).killcount == FRAGS_SPECTATOR) {
+               PlayerScore_Clear(this);
+               CS(this).killcount = 0;
+       }
+       for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               entity oldwep = this.(weaponentity);
+               CL_SpawnWeaponentity(this, weaponentity);
+               if(oldwep && oldwep.owner == this)
+                       this.(weaponentity).m_gunalign = oldwep.m_gunalign;
+       }
+       this.alpha = default_player_alpha;
+       this.colormod = '1 1 1' * autocvar_g_player_brightness;
+       this.exteriorweaponentity.alpha = default_weapon_alpha;
  
-               this.speedrunning = false;
+       this.speedrunning = false;
  
-               target_voicescript_clear(this);
+       target_voicescript_clear(this);
  
-               // reset fields the weapons may use
-               FOREACH(Weapons, true, LAMBDA(
-                       it.wr_resetplayer(it, this);
+       // reset fields the weapons may use
+       FOREACH(Weapons, true, LAMBDA(
+               it.wr_resetplayer(it, this);
                        // reload all reloadable weapons
-                       if (it.spawnflags & WEP_FLAG_RELOADABLE) {
-                               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-                               {
-                                       .entity weaponentity = weaponentities[slot];
-                                       this.(weaponentity).weapon_load[it.m_id] = it.reloading_ammo;
-                               }
+               if (it.spawnflags & WEP_FLAG_RELOADABLE) {
+                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+                       {
+                               .entity weaponentity = weaponentities[slot];
+                               this.(weaponentity).weapon_load[it.m_id] = it.reloading_ammo;
                        }
-               ));
-               {
-                       string s = spot.target;
-                       spot.target = string_null;
-                       SUB_UseTargets(spot, this, NULL);
-                       spot.target = s;
                }
+       ));
  
-               Unfreeze(this);
+       {
+               string s = spot.target;
+               spot.target = string_null;
+               SUB_UseTargets(spot, this, NULL);
+               spot.target = s;
+       }
  
-               MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
+       Unfreeze(this);
  
-               if (autocvar_spawn_debug)
-               {
-                       sprint(this, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
-                       delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
-               }
+       MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
  
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       .entity weaponentity = weaponentities[slot];
-                       if(slot == 0 || autocvar_g_weaponswitch_debug == 1)
-                               this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity);
-                       else
-                               this.(weaponentity).m_switchweapon = WEP_Null;
-                       this.(weaponentity).m_weapon = WEP_Null;
-                       this.(weaponentity).weaponname = "";
-                       this.(weaponentity).m_switchingweapon = WEP_Null;
-                       this.(weaponentity).cnt = -1;
-               }
+       if (autocvar_spawn_debug)
+       {
+               sprint(this, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
+               delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
+       }
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               if(slot == 0 || autocvar_g_weaponswitch_debug == 1)
+                       this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity);
+               else
+                       this.(weaponentity).m_switchweapon = WEP_Null;
+               this.(weaponentity).m_weapon = WEP_Null;
+               this.(weaponentity).weaponname = "";
+               this.(weaponentity).m_switchingweapon = WEP_Null;
+               this.(weaponentity).cnt = -1;
+       }
+       MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
+       if (!warmup_stage && !this.alivetime)
+               this.alivetime = time;
+       antilag_clear(this, CS(this));
+ }
+ /** Called when a client spawns in the server */
+ void PutClientInServer(entity this)
+ {
+       if (IS_BOT_CLIENT(this)) {
+               TRANSMUTE(Player, this);
+       } else if (IS_REAL_CLIENT(this)) {
+               msg_entity = this;
+               WriteByte(MSG_ONE, SVC_SETVIEW);
+               WriteEntity(MSG_ONE, this);
+       }
+       if (game_stopped)
+               TRANSMUTE(Observer, this);
  
-               MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
+       SetSpectatee(this, NULL);
  
-               if (!warmup_stage && !this.alivetime)
-                       this.alivetime = time;
+       // reset player keys
+       if(PS(this))
+               PS(this).itemkeys = 0;
+       MUTATOR_CALLHOOK(PutClientInServer, this);
  
-               antilag_clear(this, CS(this));
+       if (IS_OBSERVER(this)) {
+               PutObserverInServer(this);
+       } else if (IS_PLAYER(this)) {
+               PutPlayerInServer(this);
        }
  }
  
@@@ -775,6 -772,7 +774,7 @@@ void ClientInit_misc(entity this
        else
                WriteString(channel, "");
        WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
+       WriteByte(channel, this.cnt * 255.0); // g_balance_damagepush_speedfactor
        WriteByte(channel, serverflags);
        WriteCoord(channel, autocvar_g_trueaim_minrange);
  }
@@@ -787,6 -785,11 +787,11 @@@ void ClientInit_CheckUpdate(entity this
                this.count = autocvar_g_balance_armor_blockpercent;
                this.SendFlags |= 1;
        }
+       if(this.cnt != autocvar_g_balance_damagepush_speedfactor)
+       {
+               this.cnt = autocvar_g_balance_damagepush_speedfactor;
+               this.SendFlags |= 1;
+       }
  }
  
  void ClientInit_Spawn()
@@@ -819,7 -822,7 +824,7 @@@ SetChangeParm
  void SetChangeParms (entity this)
  {
        // save parms for level change
-       parm1 = this.parm_idlesince - time;
+       parm1 = CS(this).parm_idlesince - time;
  
        MUTATOR_CALLHOOK(SetChangeParms);
  }
@@@ -832,12 -835,12 +837,12 @@@ DecodeLevelParm
  void DecodeLevelParms(entity this)
  {
        // load parms
-       this.parm_idlesince = parm1;
-       if (this.parm_idlesince == -(86400 * 366))
-               this.parm_idlesince = time;
+       CS(this).parm_idlesince = parm1;
+       if (CS(this).parm_idlesince == -(86400 * 366))
+               CS(this).parm_idlesince = time;
  
        // whatever happens, allow 60 seconds of idling directly after connect for map loading
-       this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60);
+       CS(this).parm_idlesince = max(CS(this).parm_idlesince, time - sv_maxidle + 60);
  
        MUTATOR_CALLHOOK(DecodeLevelParms);
  }
@@@ -853,19 -856,19 +858,19 @@@ Called when a client types 'kill' in th
  .float clientkill_nexttime;
  void ClientKill_Now_TeamChange(entity this)
  {
-       if(this.killindicator_teamchange == -1)
+       if(CS(this).killindicator_teamchange == -1)
        {
                JoinBestTeam( this, false, true );
        }
-       else if(this.killindicator_teamchange == -2)
+       else if(CS(this).killindicator_teamchange == -2)
        {
                if(blockSpectators)
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
                PutObserverInServer(this);
        }
        else
-               SV_ChangeTeam(this, this.killindicator_teamchange - 1);
-       this.killindicator_teamchange = 0;
+               SV_ChangeTeam(this, CS(this).killindicator_teamchange - 1);
+       CS(this).killindicator_teamchange = 0;
  }
  
  void ClientKill_Now(entity this)
        if(this.vehicle)
        {
            vehicles_exit(this.vehicle, VHEF_RELEASE);
-           if(!this.killindicator_teamchange)
+           if(!CS(this).killindicator_teamchange)
            {
              this.vehicle_health = -1;
              Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0');
  
        this.killindicator = NULL;
  
-       if(this.killindicator_teamchange)
+       if(CS(this).killindicator_teamchange)
                ClientKill_Now_TeamChange(this);
  
        if(!IS_SPEC(this) && !IS_OBSERVER(this))
@@@ -948,7 -951,7 +953,7 @@@ void ClientKill_TeamChange (entity this
        return;
      killtime = M_ARGV(1, float);
  
-       this.killindicator_teamchange = targetteam;
+       CS(this).killindicator_teamchange = targetteam;
  
      if(!this.killindicator)
        {
@@@ -1051,25 -1054,41 +1056,41 @@@ void FixClientCvars(entity e
        MUTATOR_CALLHOOK(FixClientCvars, e);
  }
  
float PlayerInIDList(entity p, string idlist)
bool findinlist_abbrev(string tofind, string list)
  {
-       float n, i;
-       string s;
+       if(list == "" || tofind == "")
+               return false; // empty list or search, just return
  
+       // this function allows abbreviated strings!
+       FOREACH_WORD(list, it == substring(tofind, 0, strlen(it)),
+       {
+               return true;
+       });
+       return false;
+ }
+ bool PlayerInIPList(entity p, string iplist)
+ {
+       // some safety checks (never allow local?)
+       if(p.netaddress == "local" || p.netaddress == "" || !IS_REAL_CLIENT(p))
+               return false;
+       return findinlist_abbrev(p.netaddress, iplist);
+ }
+ bool PlayerInIDList(entity p, string idlist)
+ {
        // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this
-       if (!p.crypto_idfp)
-               return 0;
+       if(!p.crypto_idfp)
+               return false;
  
-       // this function allows abbreviated player IDs too!
-       n = tokenize_console(idlist);
-       for(i = 0; i < n; ++i)
-       {
-               s = argv(i);
-               if(s == substring(p.crypto_idfp, 0, strlen(s)))
-                       return 1;
-       }
+       return findinlist_abbrev(p.crypto_idfp, idlist);
+ }
  
-       return 0;
+ bool PlayerInList(entity player, string list)
+ {
+       return boolean(PlayerInIDList(player, list) || PlayerInIPList(player, list));
  }
  
  #ifdef DP_EXT_PRECONNECT
@@@ -1110,8 -1129,8 +1131,8 @@@ void ClientConnect(entity this
  #ifdef WATERMARK
        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK);
  #endif
-       this.version_nagtime = time + 10 + random() * 10;
        TRANSMUTE(Client, this);
+       CS(this).version_nagtime = time + 10 + random() * 10;
  
        // identify the right forced team
        if (autocvar_g_campaign)
                        }
                }
        }
-       else if (PlayerInIDList(this, autocvar_g_forced_team_red))    this.team_forced = NUM_TEAM_1;
-       else if (PlayerInIDList(this, autocvar_g_forced_team_blue))   this.team_forced = NUM_TEAM_2;
-       else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3;
-       else if (PlayerInIDList(this, autocvar_g_forced_team_pink))   this.team_forced = NUM_TEAM_4;
+       else if (PlayerInList(this, autocvar_g_forced_team_red))    this.team_forced = NUM_TEAM_1;
+       else if (PlayerInList(this, autocvar_g_forced_team_blue))   this.team_forced = NUM_TEAM_2;
+       else if (PlayerInList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3;
+       else if (PlayerInList(this, autocvar_g_forced_team_pink))   this.team_forced = NUM_TEAM_4;
        else switch (autocvar_g_forced_team_otherwise)
        {
                default: this.team_forced = 0; break;
  
        LogTeamchange(this.playerid, this.team, 1);
  
-       this.just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
+       CS(this).just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
  
-       this.netname_previous = strzone(this.netname);
+       CS(this).netname_previous = strzone(this.netname);
  
        if(teamplay && IS_PLAYER(this))
                Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_CONNECT_TEAM), this.netname);
  
        bot_relinkplayerlist();
  
-       this.spectatortime = time;
+       CS(this).spectatortime = time;
        if (blockSpectators)
        {
                Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
        }
  
-       this.jointime = time;
-       this.allowed_timeouts = autocvar_sv_timeout_number;
+       CS(this).jointime = time;
+       CS(this).allowed_timeouts = autocvar_sv_timeout_number;
  
        if (IS_REAL_CLIENT(this))
        {
  
        CSQCMODEL_AUTOINIT(this);
  
-       this.model_randomizer = random();
+       CS(this).model_randomizer = random();
  
        if (IS_REAL_CLIENT(this))
                sv_notice_join(this);
  
        // update physics stats (players can spawn before physics runs)
-       STAT(MOVEVARS_HIGHSPEED, this) = autocvar_g_movement_highspeed;
-       MUTATOR_CALLHOOK(PlayerPhysics_UpdateStats, this); // do it BEFORE the function so we can modify highspeed!
-       Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
+       Physics_UpdateStats(this);
  
        IL_EACH(g_initforplayer, it.init_for_player, {
                it.init_for_player(it, this);
        {
                if (!autocvar_g_campaign && !IS_PLAYER(this))
                {
-                       this.motd_actived_time = -1;
+                       CS(this).motd_actived_time = -1;
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
                }
        }
@@@ -1273,7 -1290,7 +1292,7 @@@ void ClientDisconnect(entity this
  
        PlayerStats_GameReport_FinalizePlayer(this);
        if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
-       if (this.active_minigame) part_minigame(this);
+       if (CS(this).active_minigame) part_minigame(this);
        if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
  
        if (autocvar_sv_eventlog)
  
      MUTATOR_CALLHOOK(ClientDisconnect, this);
  
+       if (CS(this).netname_previous) strunzone(CS(this).netname_previous); // needs to be before the CS entity is removed!
        ClientState_detach(this);
  
        Portal_ClearAll(this);
  
        bot_relinkplayerlist();
  
-       if (this.netname_previous) strunzone(this.netname_previous);
        if (this.clientstatus) strunzone(this.clientstatus);
        if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse);
        if (this.personal) delete(this.personal);
@@@ -1332,7 -1349,7 +1351,7 @@@ void ChatBubbleThink(entity this
  
        if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) )
        {
-               if ( this.owner.active_minigame )
+               if ( CS(this.owner).active_minigame )
                        this.mdl = "models/sprites/minigame_busy.iqm";
                else if (PHYS_INPUT_BUTTON_CHAT(this.owner))
                        this.mdl = "models/misc/chatbubble.spr";
@@@ -1646,9 -1663,9 +1665,9 @@@ void player_regen(entity this
  bool zoomstate_set;
  void SetZoomState(entity this, float newzoom)
  {
-       if(newzoom != this.zoomstate)
+       if(newzoom != CS(this).zoomstate)
        {
-               this.zoomstate = newzoom;
+               CS(this).zoomstate = newzoom;
                ClientData_Touch(this);
        }
        zoomstate_set = true;
@@@ -1658,16 -1675,16 +1677,16 @@@ void GetPressedKeys(entity this
  {
        MUTATOR_CALLHOOK(GetPressedKeys, this);
        int keys = STAT(PRESSED_KEYS, this);
-       keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
-       keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
-       keys = BITSET(keys, KEY_RIGHT,          this.movement.y > 0);
-       keys = BITSET(keys, KEY_LEFT,           this.movement.y < 0);
+       keys = BITSET(keys, KEY_FORWARD,        CS(this).movement.x > 0);
+       keys = BITSET(keys, KEY_BACKWARD,       CS(this).movement.x < 0);
+       keys = BITSET(keys, KEY_RIGHT,          CS(this).movement.y > 0);
+       keys = BITSET(keys, KEY_LEFT,           CS(this).movement.y < 0);
  
        keys = BITSET(keys, KEY_JUMP,           PHYS_INPUT_BUTTON_JUMP(this));
        keys = BITSET(keys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(this));
        keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
        keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
-       this.pressedkeys = keys; // store for other users
+       CS(this).pressedkeys = keys; // store for other users
  
        STAT(PRESSED_KEYS, this) = keys;
  }
@@@ -1696,7 -1713,7 +1715,7 @@@ void SpectateCopy(entity this, entity s
        this.clip_size = spectatee.clip_size;
        this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
        this.health = spectatee.health;
-       this.impulse = 0;
+       CS(this).impulse = 0;
        this.items = spectatee.items;
        this.last_pickup = spectatee.last_pickup;
        this.hit_time = spectatee.hit_time;
        this.superweapons_finished = spectatee.superweapons_finished;
        STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
        this.weapons = spectatee.weapons;
-       this.dual_weapons = spectatee.dual_weapons;
        this.vortex_charge = spectatee.vortex_charge;
        this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
 +      this.okvortex_charge = spectatee.okvortex_charge;
 +      this.okvortex_chargepool_ammo = spectatee.okvortex_chargepool_ammo;
        this.hagar_load = spectatee.hagar_load;
        this.arc_heat_percent = spectatee.arc_heat_percent;
        this.minelayer_mines = spectatee.minelayer_mines;
                this.fixangle = true;
        setorigin(this, spectatee.origin);
        setsize(this, spectatee.mins, spectatee.maxs);
-       SetZoomState(this, spectatee.zoomstate);
+       SetZoomState(this, CS(spectatee).zoomstate);
  
        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
@@@ -1808,10 -1822,10 +1826,10 @@@ bool SpectateSet(entity this
  
  void SetSpectatee_status(entity this, int spectatee_num)
  {
-       int oldspectatee_status = this.spectatee_status;
-       this.spectatee_status = spectatee_num;
+       int oldspectatee_status = CS(this).spectatee_status;
+       CS(this).spectatee_status = spectatee_num;
  
-       if (this.spectatee_status != oldspectatee_status)
+       if (CS(this).spectatee_status != oldspectatee_status)
        {
                ClientData_Touch(this);
                if (g_race || g_cts) race_InitSpectator();
@@@ -1943,7 -1957,7 +1961,7 @@@ void ShowRespawnCountdown(entity this
  .bool team_selected;
  bool ShowTeamSelection(entity this)
  {
-       if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
+       if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (CS(this).wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
                return false;
        stuffcmd(this, "menu_showteamselect\n");
        return true;
@@@ -2029,7 -2043,7 +2047,7 @@@ void checkSpectatorBlock(entity this
        if(!this.caplayer)
        if(IS_REAL_CLIENT(this))
        {
-               if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
+               if( time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
                        dropclient(this);
                }
  
  void PrintWelcomeMessage(entity this)
  {
-       if(this.motd_actived_time == 0)
+       if(CS(this).motd_actived_time == 0)
        {
                if (autocvar_g_campaign) {
                        if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) {
-                               this.motd_actived_time = time;
+                               CS(this).motd_actived_time = time;
                                Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message);
                        }
                } else {
                        if (PHYS_INPUT_BUTTON_INFO(this)) {
-                               this.motd_actived_time = time;
+                               CS(this).motd_actived_time = time;
                                Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
                        }
                }
        }
-       else if(this.motd_actived_time > 0) // showing MOTD or campaign message
+       else if(CS(this).motd_actived_time > 0) // showing MOTD or campaign message
        {
                if (autocvar_g_campaign) {
                        if (PHYS_INPUT_BUTTON_INFO(this))
-                               this.motd_actived_time = time;
-                       else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released
-                               this.motd_actived_time = 0;
+                               CS(this).motd_actived_time = time;
+                       else if ((time - CS(this).motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released
+                               CS(this).motd_actived_time = 0;
                                Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                        }
                } else {
                        if (PHYS_INPUT_BUTTON_INFO(this))
-                               this.motd_actived_time = time;
-                       else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
-                               this.motd_actived_time = 0;
+                               CS(this).motd_actived_time = time;
+                       else if (time - CS(this).motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
+                               CS(this).motd_actived_time = 0;
                                Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                        }
                }
        }
-       else //if(this.motd_actived_time < 0) // just connected, motd is active
+       else //if(CS(this).motd_actived_time < 0) // just connected, motd is active
        {
                if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD
-                       this.motd_actived_time = -2; // wait until BUTTON_INFO gets released
-               else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this))
+                       CS(this).motd_actived_time = -2; // wait until BUTTON_INFO gets released
+               else if(CS(this).motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this))
                {
                        // instanctly hide MOTD
-                       this.motd_actived_time = 0;
+                       CS(this).motd_actived_time = 0;
                        Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                }
        }
  
  bool joinAllowed(entity this)
  {
-       if (this.version_mismatch) return false;
+       if (CS(this).version_mismatch) return false;
        if (!nJoinAllowed(this, this)) return false;
        if (teamplay && lockteams) return false;
        if (ShowTeamSelection(this)) return false;
        return true;
  }
  
+ .int items_added;
+ bool PlayerThink(entity this)
+ {
+       if (game_stopped || intermission_running) {
+               this.modelflags &= ~MF_ROCKET;
+               if(intermission_running)
+                       IntermissionThink(this);
+               return false;
+       }
+       if (timeout_status == TIMEOUT_ACTIVE) {
+         // don't allow the player to turn around while game is paused
+               // FIXME turn this into CSQC stuff
+               this.v_angle = this.lastV_angle;
+               this.angles = this.lastV_angle;
+               this.fixangle = true;
+       }
+       if (frametime) player_powerups(this);
+       if (IS_DEAD(this)) {
+               if (this.personal && g_race_qualifying) {
+                       if (time > this.respawn_time) {
+                               STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
+                               respawn(this);
+                               CS(this).impulse = CHIMPULSE_SPEEDRUN.impulse;
+                       }
+               } else {
+                       if (frametime) player_anim(this);
+                       if (this.respawn_flags & RESPAWN_DENY)
+                       {
+                               STAT(RESPAWN_TIME, this) = 0;
+                               return false;
+                       }
+                       bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this));
+                       switch(this.deadflag)
+                       {
+                               case DEAD_DYING:
+                               {
+                                       if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
+                                               this.deadflag = DEAD_RESPAWNING;
+                                       else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
+                                               this.deadflag = DEAD_DEAD;
+                                       break;
+                               }
+                               case DEAD_DEAD:
+                               {
+                                       if (button_pressed)
+                                               this.deadflag = DEAD_RESPAWNABLE;
+                                       else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))
+                                               this.deadflag = DEAD_RESPAWNING;
+                                       break;
+                               }
+                               case DEAD_RESPAWNABLE:
+                               {
+                                       if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
+                                               this.deadflag = DEAD_RESPAWNING;
+                                       break;
+                               }
+                               case DEAD_RESPAWNING:
+                               {
+                                       if (time > this.respawn_time)
+                                       {
+                                               this.respawn_time = time + 1; // only retry once a second
+                                               this.respawn_time_max = this.respawn_time;
+                                               respawn(this);
+                                       }
+                                       break;
+                               }
+                       }
+                       ShowRespawnCountdown(this);
+                       if (this.respawn_flags & RESPAWN_SILENT)
+                               STAT(RESPAWN_TIME, this) = 0;
+                       else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
+                       {
+                               if (time < this.respawn_time)
+                                       STAT(RESPAWN_TIME, this) = this.respawn_time;
+                               else if (this.deadflag != DEAD_RESPAWNING)
+                                       STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
+                       }
+                       else
+                               STAT(RESPAWN_TIME, this) = this.respawn_time;
+               }
+               // if respawning, invert stat_respawn_time to indicate this, the client translates it
+               if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
+                       STAT(RESPAWN_TIME, this) *= -1;
+               return false;
+       }
+       bool have_hook = false;
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               if(this.(weaponentity).hook.state)
+               {
+                       have_hook = true;
+                       break;
+               }
+       }
+       bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
+       if (have_hook) {
+               do_crouch = false;
+       } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
+               do_crouch = false;
+       } else if (this.vehicle) {
+               do_crouch = false;
+       } else if (STAT(FROZEN, this)) {
+               do_crouch = false;
+     }
+       if (do_crouch) {
+               if (!this.crouch) {
+                       this.crouch = true;
+                       this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
+                       setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
+                       // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway
+               }
+       } else if (this.crouch) {
+         tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this);
+         if (!trace_startsolid) {
+             this.crouch = false;
+             this.view_ofs = STAT(PL_VIEW_OFS, this);
+             setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
+         }
+       }
+       FixPlayermodel(this);
+       // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers
+       //if(frametime)
+       {
+               this.items &= ~this.items_added;
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       W_WeaponFrame(this, weaponentity);
+                       if(slot == 0)
+                       {
+                               this.clip_load = this.(weaponentity).clip_load;
+                               this.clip_size = this.(weaponentity).clip_size;
+                       }
+               }
+               this.items_added = 0;
+               if ((this.items & ITEM_Jetpack.m_itemid) && ((this.items & ITEM_JetpackRegen.m_itemid) || this.ammo_fuel >= 0.01))
+             this.items_added |= IT_FUEL;
+               this.items |= this.items_added;
+       }
+       player_regen(this);
+       // WEAPONTODO: Add a weapon request for this
+       // rot vortex charge to the charge limit
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               if (WEP_CVAR(vortex, charge_rot_rate) && this.(weaponentity).vortex_charge > WEP_CVAR(vortex, charge_limit) && this.(weaponentity).vortex_charge_rottime < time)
+                       this.(weaponentity).vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.(weaponentity).vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
+       }
+       if (frametime) player_anim(this);
+       // secret status
+       secrets_setstatus(this);
+       // monsters status
+       monsters_setstatus(this);
+       this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
+       return true;
+ }
  void ObserverThink(entity this)
  {
-       if ( this.impulse )
+       if ( CS(this).impulse )
        {
-               MinigameImpulse(this, this.impulse);
-               this.impulse = 0;
+               MinigameImpulse(this, CS(this).impulse);
+               CS(this).impulse = 0;
        }
  
        if (this.flags & FL_JUMPRELEASED) {
                if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        this.flags |= FL_SPAWNING;
-               } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) {
+               } else if(PHYS_INPUT_BUTTON_ATCK(this) && !CS(this).version_mismatch) {
                        this.flags &= ~FL_JUMPRELEASED;
                        if(SpectateNext(this)) {
                                TRANSMUTE(Spectator, this);
  
  void SpectatorThink(entity this)
  {
-       if ( this.impulse )
+       if ( CS(this).impulse )
        {
-               if(MinigameImpulse(this, this.impulse))
-                       this.impulse = 0;
+               if(MinigameImpulse(this, CS(this).impulse))
+                       CS(this).impulse = 0;
  
-               if (this.impulse == IMP_weapon_drop.impulse)
+               if (CS(this).impulse == IMP_weapon_drop.impulse)
                {
                        STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3;
-                       this.impulse = 0;
+                       CS(this).impulse = 0;
                        return;
                }
        }
                if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        this.flags |= FL_SPAWNING;
-               } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) {
+               } else if(PHYS_INPUT_BUTTON_ATCK(this) || CS(this).impulse == 10 || CS(this).impulse == 15 || CS(this).impulse == 18 || (CS(this).impulse >= 200 && CS(this).impulse <= 209)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        if(SpectateNext(this)) {
                                TRANSMUTE(Spectator, this);
                                TRANSMUTE(Observer, this);
                                PutClientInServer(this);
                        }
-                       this.impulse = 0;
-               } else if(this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) {
+                       CS(this).impulse = 0;
+               } else if(CS(this).impulse == 12 || CS(this).impulse == 16  || CS(this).impulse == 19 || (CS(this).impulse >= 220 && CS(this).impulse <= 229)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        if(SpectatePrev(this)) {
                                TRANSMUTE(Spectator, this);
                                TRANSMUTE(Observer, this);
                                PutClientInServer(this);
                        }
-                       this.impulse = 0;
+                       CS(this).impulse = 0;
                } else if (PHYS_INPUT_BUTTON_ATCK2(this)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        TRANSMUTE(Observer, this);
@@@ -2246,20 -2443,11 +2447,11 @@@ PlayerPreThin
  Called every frame for each client before the physics are run
  =============
  */
- .float usekeypressed;
  .float last_vehiclecheck;
- .int items_added;
  void PlayerPreThink (entity this)
  {
        WarpZone_PlayerPhysics_FixVAngle(this);
  
-     STAT(GAMESTARTTIME, this) = game_starttime;
-       STAT(ROUNDSTARTTIME, this) = round_starttime;
-       STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam;
-       STAT(LEADLIMIT, this) = autocvar_leadlimit;
-       STAT(WEAPONSINMAP, this) = weaponsInMap;
        if (frametime) {
                // physics frames: update anticheat stuff
                anticheat_prethink(this);
                this.netname = strzone(sprintf("Player#%d", this.playerid));
                // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
        }
-       if (this.netname != this.netname_previous) {
+       if (this.netname != CS(this).netname_previous) {
                if (autocvar_sv_eventlog) {
                        GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this, false)));
          }
-               if (this.netname_previous) strunzone(this.netname_previous);
-               this.netname_previous = strzone(this.netname);
+               if (CS(this).netname_previous) strunzone(CS(this).netname_previous);
+               CS(this).netname_previous = strzone(this.netname);
        }
  
        // version nagging
-       if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) {
-         this.version_nagtime = 0;
+       if (CS(this).version_nagtime && this.cvar_g_xonoticversion && time > CS(this).version_nagtime) {
+         CS(this).version_nagtime = 0;
          if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) {
              // git client
          } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) {
                this.max_armorvalue = 0;
        }
  
-       if (STAT(FROZEN, this) == 2)
-       {
-               this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1);
-               this.health = max(1, this.revive_progress * start_health);
-               this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1);
-               if (this.revive_progress >= 1)
-                       Unfreeze(this);
-       }
-       else if (STAT(FROZEN, this) == 3)
+       if(IS_PLAYER(this))
        {
-               this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1);
-               this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress );
+               if (STAT(FROZEN, this) == 2)
+               {
+                       this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1);
+                       this.health = max(1, this.revive_progress * start_health);
+                       this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1);
  
-               if (this.health < 1)
+                       if (this.revive_progress >= 1)
+                               Unfreeze(this);
+               }
+               else if (STAT(FROZEN, this) == 3)
                {
-                       if (this.vehicle)
-                               vehicles_exit(this.vehicle, VHEF_RELEASE);
-                       if(this.event_damage)
-                               this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
+                       this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1);
+                       this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress );
+                       if (this.health < 1)
+                       {
+                               if (this.vehicle)
+                                       vehicles_exit(this.vehicle, VHEF_RELEASE);
+                               if(this.event_damage)
+                                       this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
+                       }
+                       else if (this.revive_progress <= 0)
+                               Unfreeze(this);
                }
-               else if (this.revive_progress <= 0)
-                       Unfreeze(this);
        }
  
        MUTATOR_CALLHOOK(PlayerPreThink, this);
  
        if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
        {
-               if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed)
+               if(PHYS_INPUT_BUTTON_USE(this) && !CS(this).usekeypressed)
                        PlayerUseKey(this);
-               this.usekeypressed = PHYS_INPUT_BUTTON_USE(this);
+               CS(this).usekeypressed = PHYS_INPUT_BUTTON_USE(this);
        }
  
        if (IS_REAL_CLIENT(this))
                PrintWelcomeMessage(this);
  
        if (IS_PLAYER(this)) {
-               CheckRules_Player(this);
-               if (game_stopped || intermission_running) {
-                       this.modelflags &= ~MF_ROCKET;
-                       if(intermission_running)
-                               IntermissionThink(this);
-                       return;
-               }
-               if (timeout_status == TIMEOUT_ACTIVE) {
-             // don't allow the player to turn around while game is paused
-                       // FIXME turn this into CSQC stuff
-                       this.v_angle = this.lastV_angle;
-                       this.angles = this.lastV_angle;
-                       this.fixangle = true;
-               }
-               if (frametime) player_powerups(this);
-               if (IS_DEAD(this)) {
-                       if (this.personal && g_race_qualifying) {
-                               if (time > this.respawn_time) {
-                                       STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
-                                       respawn(this);
-                                       this.impulse = CHIMPULSE_SPEEDRUN.impulse;
-                               }
-                       } else {
-                               if (frametime) player_anim(this);
-                               if (this.respawn_flags & RESPAWN_DENY)
-                               {
-                                       STAT(RESPAWN_TIME, this) = 0;
-                                       return;
-                               }
-                               bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this));
-                               switch(this.deadflag)
-                               {
-                                       case DEAD_DYING:
-                                       {
-                                               if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
-                                                       this.deadflag = DEAD_RESPAWNING;
-                                               else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
-                                                       this.deadflag = DEAD_DEAD;
-                                               break;
-                                       }
-                                       case DEAD_DEAD:
-                                       {
-                                               if (button_pressed)
-                                                       this.deadflag = DEAD_RESPAWNABLE;
-                                               else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))
-                                                       this.deadflag = DEAD_RESPAWNING;
-                                               break;
-                                       }
-                                       case DEAD_RESPAWNABLE:
-                                       {
-                                               if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
-                                                       this.deadflag = DEAD_RESPAWNING;
-                                               break;
-                                       }
-                                       case DEAD_RESPAWNING:
-                                       {
-                                               if (time > this.respawn_time)
-                                               {
-                                                       this.respawn_time = time + 1; // only retry once a second
-                                                       this.respawn_time_max = this.respawn_time;
-                                                       respawn(this);
-                                               }
-                                               break;
-                                       }
-                               }
-                               ShowRespawnCountdown(this);
-                               if (this.respawn_flags & RESPAWN_SILENT)
-                                       STAT(RESPAWN_TIME, this) = 0;
-                               else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
-                               {
-                                       if (time < this.respawn_time)
-                                               STAT(RESPAWN_TIME, this) = this.respawn_time;
-                                       else if (this.deadflag != DEAD_RESPAWNING)
-                                               STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
-                               }
-                               else
-                                       STAT(RESPAWN_TIME, this) = this.respawn_time;
-                       }
-                       // if respawning, invert stat_respawn_time to indicate this, the client translates it
-                       if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
-                               STAT(RESPAWN_TIME, this) *= -1;
+               if(!PlayerThink(this))
                        return;
-               }
-               this.prevorigin = this.origin;
-               bool have_hook = false;
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       .entity weaponentity = weaponentities[slot];
-                       if(this.(weaponentity).hook.state)
-                       {
-                               have_hook = true;
-                               break;
-                       }
-               }
-               bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
-               if (have_hook) {
-                       do_crouch = false;
-               } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
-                       do_crouch = false;
-               } else if (this.vehicle) {
-                       do_crouch = false;
-               } else if (STAT(FROZEN, this)) {
-                       do_crouch = false;
-         }
-               if (do_crouch) {
-                       if (!this.crouch) {
-                               this.crouch = true;
-                               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
-                               setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
-                               // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway
-                       }
-               } else if (this.crouch) {
-             tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this);
-             if (!trace_startsolid) {
-                 this.crouch = false;
-                 this.view_ofs = STAT(PL_VIEW_OFS, this);
-                 setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
-             }
-               }
-               FixPlayermodel(this);
-               // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers
-               //if(frametime)
-               {
-                       this.items &= ~this.items_added;
-                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-                       {
-                               .entity weaponentity = weaponentities[slot];
-                               W_WeaponFrame(this, weaponentity);
-                               if(slot == 0)
-                               {
-                                       this.clip_load = this.(weaponentity).clip_load;
-                                       this.clip_size = this.(weaponentity).clip_size;
-                               }
-                       }
-                       this.items_added = 0;
-                       if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01))
-                 this.items_added |= IT_FUEL;
-                       this.items |= this.items_added;
-               }
-               player_regen(this);
-               // WEAPONTODO: Add a weapon request for this
-               // rot vortex charge to the charge limit
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       .entity weaponentity = weaponentities[slot];
-                       if (WEP_CVAR(vortex, charge_rot_rate) && this.(weaponentity).vortex_charge > WEP_CVAR(vortex, charge_limit) && this.(weaponentity).vortex_charge_rottime < time)
-                       {
-                               this.(weaponentity).vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.(weaponentity).vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
-                       }
-                       if (WEP_CVAR(okvortex, charge_rot_rate) && this.(weaponentity).okvortex_charge > WEP_CVAR(okvortex, charge_limit) && this.(weaponentity).okvortex_charge_rottime < time)
-                       {
-                               this.(weaponentity).okvortex_charge = bound(WEP_CVAR(okvortex, charge_limit), this.(weaponentity).okvortex_charge - WEP_CVAR(okvortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
-                       }
-               }
-               if (frametime) player_anim(this);
-               // secret status
-               secrets_setstatus(this);
-               // monsters status
-               monsters_setstatus(this);
-               this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
        }
        else if (game_stopped || intermission_running) {
                if(intermission_running)
                SetZoomState(this, PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) || wep_zoomed);
      }
  
-       if (this.teamkill_soundtime && time > this.teamkill_soundtime)
+       if (CS(this).teamkill_soundtime && time > CS(this).teamkill_soundtime)
        {
-               this.teamkill_soundtime = 0;
+               CS(this).teamkill_soundtime = 0;
  
-               entity e = this.teamkill_soundsource;
+               entity e = CS(this).teamkill_soundsource;
                entity oldpusher = e.pusher;
                e.pusher = this;
                PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY);
                e.pusher = oldpusher;
        }
  
-       if (this.taunt_soundtime && time > this.taunt_soundtime) {
-               this.taunt_soundtime = 0;
+       if (CS(this).taunt_soundtime && time > CS(this).taunt_soundtime) {
+               CS(this).taunt_soundtime = 0;
                PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT);
        }
  
@@@ -2642,12 -2649,12 +2653,12 @@@ void Player_Physics(entity this
        if(!this.move_qcphysics)
                return;
  
-       if(!frametime && !this.pm_frametime)
+       if(!frametime && !CS(this).pm_frametime)
                return;
  
-       Movetype_Physics_NoMatchTicrate(this, this.pm_frametime, true);
+       Movetype_Physics_NoMatchTicrate(this, CS(this).pm_frametime, true);
  
-       this.pm_frametime = 0;
+       CS(this).pm_frametime = 0;
  }
  
  /*
@@@ -2657,7 -2664,6 +2668,6 @@@ PlayerPostThin
  Called every frame for each client after the physics are run
  =============
  */
- .float idlekick_lasttimeleft;
  void PlayerPostThink (entity this)
  {
        Player_Physics(this);
  
                if (sv_maxidle_slots > 0 && (maxclients - totalClients) > sv_maxidle_slots)
                { /* do nothing */ }
-               else if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
+               else if (time - CS(this).parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
                {
-                       if (this.idlekick_lasttimeleft)
+                       if (CS(this).idlekick_lasttimeleft)
                        {
-                               this.idlekick_lasttimeleft = 0;
+                               CS(this).idlekick_lasttimeleft = 0;
                                Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING);
                        }
                }
                else
                {
-                       float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince));
+                       float timeleft = ceil(sv_maxidle - (time - CS(this).parm_idlesince));
                        if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10
-                               if (!this.idlekick_lasttimeleft)
+                               if (!CS(this).idlekick_lasttimeleft)
                                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
                        }
                        if (timeleft <= 0) {
                                return;
                        }
                        else if (timeleft <= 10) {
-                               if (timeleft != this.idlekick_lasttimeleft) {
+                               if (timeleft != CS(this).idlekick_lasttimeleft) {
                                    Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft));
                  }
-                               this.idlekick_lasttimeleft = timeleft;
+                               CS(this).idlekick_lasttimeleft = timeleft;
                        }
                }
        }
  
        CheatFrame(this);
  
-       //CheckPlayerJump();
        if (game_stopped)
        {
                this.solid = SOLID_NOT;
  
        if (IS_PLAYER(this)) {
                DrownPlayer(this);
-               CheckRules_Player(this);
                UpdateChatBubble(this);
-               if (this.impulse) ImpulseCommands(this);
+               if (CS(this).impulse) ImpulseCommands(this);
                if (game_stopped)
                {
                        CSQCMODEL_AUTOUPDATE(this);
  
        CSQCMODEL_AUTOUPDATE(this);
  }
+ // hack to copy the button fields from the client entity to the Client State
+ void PM_UpdateButtons(entity this, entity store)
+ {
+       if(this.impulse)
+               store.impulse = this.impulse;
+       this.impulse = 0;
+       store.button0 = this.button0;
+       store.button2 = this.button2;
+       store.button3 = this.button3;
+       store.button4 = this.button4;
+       store.button5 = this.button5;
+       store.button6 = this.button6;
+       store.button7 = this.button7;
+       store.button8 = this.button8;
+       store.button9 = this.button9;
+       store.button10 = this.button10;
+       store.button11 = this.button11;
+       store.button12 = this.button12;
+       store.button13 = this.button13;
+       store.button14 = this.button14;
+       store.button15 = this.button15;
+       store.button16 = this.button16;
+       store.buttonuse = this.buttonuse;
+       store.buttonchat = this.buttonchat;
+       store.cursor_active = this.cursor_active;
+       store.cursor_screen = this.cursor_screen;
+       store.cursor_trace_start = this.cursor_trace_start;
+       store.cursor_trace_endpos = this.cursor_trace_endpos;
+       store.cursor_trace_ent = this.cursor_trace_ent;
+       store.ping = this.ping;
+       store.ping_packetloss = this.ping_packetloss;
+       store.ping_movementloss = this.ping_movementloss;
+       store.v_angle = this.v_angle;
+       store.movement = this.movement;
+ }
diff --combined qcsrc/server/defs.qh
index 8cdb562f5b140cd52c471ef34ff6706743ccc63e,7a268e31c573bd2fbf8fde5e00b4f5d206bbdc16..3f561a193cb01ca0c3f9ab5186d703a8c00d0e5c
@@@ -53,7 -53,6 +53,6 @@@ float server_is_dedicated
  .float count;
  //.float cnt2;
  
- .float play_time;
  .int respawn_flags;
  .float respawn_time;
  .float respawn_time_max;
@@@ -131,8 -130,6 +130,6 @@@ float intermission_running
  float intermission_exittime;
  float alreadychangedlevel;
  
- .float version;
  // footstep interval
  .float nextstep;
  
@@@ -179,8 -176,6 +176,6 @@@ float default_weapon_alpha
  .float cvar_cl_allow_uidtracking;
  .string stored_netname;
  
- .float version_nagtime;
  string gamemode_name;
  
  float startitem_failed;
@@@ -191,7 -186,7 +186,7 @@@ void FixIntermissionClient(entity e)
  void FixClientCvars(entity e);
  
  // WEAPONTODO: remove this
- WepSet weaponsInMap;
//WepSet weaponsInMap;
  
  #define weapons _STAT(WEAPONS)
  
@@@ -226,13 -221,11 +221,11 @@@ int have_team_spawns_forteams; // if Xt
  
  // set when showing a kill countdown
  .entity killindicator;
- .float killindicator_teamchange;
  
  void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
  
  float lockteams;
  
- .float parm_idlesince;
  float sv_maxidle;
  float sv_maxidle_spectatorsareidle;
  int sv_maxidle_slots;
@@@ -247,8 -240,6 +240,6 @@@ float next_pingtime
  .float cvar_cl_voice_directional;
  .float cvar_cl_voice_directional_taunt_attenuation;
  
- .float version_mismatch;
  int autocvar__independent_players;
  bool independent_players;
  #define INDEPENDENT_PLAYERS (autocvar__independent_players ? (autocvar__independent_players > 0) : independent_players)
@@@ -265,8 -256,8 +256,8 @@@ string cvar_changes
  string cvar_purechanges;
  float cvar_purechanges_count;
  
- float game_starttime; //point in time when the countdown to game start is over
- float round_starttime; //point in time when the countdown to round start is over
//float game_starttime; //point in time when the countdown to game start is over
//float round_starttime; //point in time when the countdown to round start is over
  
  void W_Porto_Remove (entity p);
  
@@@ -292,14 -283,12 +283,12 @@@ void ClientData_Touch(entity e)
  
  //vector debug_shotorg; // if non-zero, overrides the shot origin of all weapons
  
- .float wasplayer;
+ .bool wasplayer;
  
  float servertime, serverprevtime, serverframetime;
  
  .float ammo_fuel;
  
- .vector prevorigin;
  //flood fields
  .float nickspamtime; // time of last nick change
  .float nickspamcount;
@@@ -338,9 -327,6 +327,9 @@@ float client_cefc_accumulatortime
  .float vortex_charge = _STAT(VORTEX_CHARGE);
  .float vortex_charge_rottime;
  .float vortex_chargepool_ammo = _STAT(VORTEX_CHARGEPOOL);
 +.float okvortex_charge = _STAT(OVERKILL_VORTEX_CHARGE);
 +.float okvortex_charge_rottime;
 +.float okvortex_chargepool_ammo = _STAT(OVERKILL_VORTEX_CHARGEPOOL);
  .float hagar_load = _STAT(HAGAR_LOAD);
  
  .int grab; // 0 = can't grab, 1 = owner can grab, 2 = owner and team mates can grab, 3 = anyone can grab