Merge branch 'master' into Mario/killsound
authorMario <mario@smbclan.net>
Sun, 12 Mar 2017 03:13:00 +0000 (13:13 +1000)
committerMario <mario@smbclan.net>
Sun, 12 Mar 2017 03:13:00 +0000 (13:13 +1000)
1  2 
qcsrc/client/view.qc
qcsrc/common/stats.qh
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc

diff --combined qcsrc/client/view.qc
@@@ -283,9 -283,11 +283,11 @@@ void viewmodel_animate(entity this
  .float weapon_eta_last;
  .float weapon_switchdelay;
  
+ .string name_last;
  void viewmodel_draw(entity this)
  {
-       if(!activeweapon || !autocvar_r_drawviewmodel)
+       if(!this.activeweapon || !autocvar_r_drawviewmodel)
                return;
        int mask = (intermission || (STAT(HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL;
        float a = this.alpha;
        if (invehicle) a = -1;
        else if (wasinvehicle) a = 1;
        wasinvehicle = invehicle;
-       Weapon wep = activeweapon;
+       Weapon wep = this.activeweapon;
        int c = entcs_GetClientColors(current_player);
-       vector g = weaponentity_glowmod(wep, NULL, c);
+       vector g = weaponentity_glowmod(wep, NULL, c, this);
        entity me = CSQCModel_server2csqc(player_localentnum - 1);
        int fx = ((me.csqcmodel_effects & EFMASK_CHEAP)
                | EF_NODEPTHTEST)
                CSQCModel_Effects_Apply(e);
        }
        {
-               static string name_last;
                string name = wep.mdl;
                string newname = wep.wr_viewmodel(wep, this);
                if(newname)
                        name = newname;
-               bool swap = name != name_last;
+               bool swap = name != this.name_last;
                // if (swap)
                {
-                       name_last = name;
+                       this.name_last = name;
                        CL_WeaponEntity_SetModel(this, name, swap);
                        this.viewmodel_origin = this.origin;
                        this.viewmodel_angles = this.angles;
                }
                anim_update(this);
-               if (!this.animstate_override && !this.animstate_looping)
+               if ((!this.animstate_override && !this.animstate_looping) || time > this.animstate_endtime)
                        anim_set(this, this.anim_idle, true, false, false);
        }
        float f = 0; // 0..1; 0: fully active
        setorigin(this, this.origin);
  }
  
- entity viewmodel;
  STATIC_INIT(viewmodel) {
-     viewmodel = new(viewmodel);
+     for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       viewmodels[slot] = new(viewmodel);
  }
  
  void Porto_Draw(entity this);
@@@ -373,65 -374,70 +374,70 @@@ STATIC_INIT(Porto
  }
  
  const int polyline_length = 16;
- vector polyline[polyline_length];
.vector polyline[polyline_length];
  void Porto_Draw(entity this)
  {
-       if (activeweapon != WEP_PORTO) return;
-       if (spectatee_status) return;
-       if (WEP_CVAR(porto, secondary)) return;
-       if (intermission == 1) return;
-       if (intermission == 2) return;
-       if (STAT(HEALTH) <= 0) return;
-       vector pos = view_origin;
-       vector dir = view_forward;
-       if (angles_held_status)
-       {
-               makevectors(angles_held);
-               dir = v_forward;
-       }
-       polyline[0] = pos;
-       int portal_number = 0, portal1_idx = 1, portal_max = 2;
-       int n = 1 + 2;  // 2 lines == 3 points
-       for (int idx = 0; idx < n && idx < polyline_length - 1; )
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
-               traceline(pos, pos + 65536 * dir, true, this);
-               dir = reflect(dir, trace_plane_normal);
-               pos = trace_endpos;
-               polyline[++idx] = pos;
-               if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
+               entity wepent = viewmodels[slot];
+               if (wepent.activeweapon != WEP_PORTO) continue;
+               if (spectatee_status) continue;
+               if (WEP_CVAR(porto, secondary)) continue;
+               if (intermission == 1) continue;
+               if (intermission == 2) continue;
+               if (STAT(HEALTH) <= 0) continue;
+               vector pos = view_origin;
+               vector dir = view_forward;
+               if (wepent.angles_held_status)
                {
-                       n += 1;
-                       continue;
+                       makevectors(wepent.angles_held);
+                       dir = v_forward;
                }
-               if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
-               {
-                       n = max(2, idx);
-                       break;
-               }
-               // check size
+               wepent.polyline[0] = pos;
+               int portal_number = 0, portal1_idx = 1, portal_max = 2;
+               int n = 1 + 2;  // 2 lines == 3 points
+               for (int idx = 0; idx < n && idx < polyline_length - 1; )
                {
-                       vector ang = vectoangles2(trace_plane_normal, dir);
-                       ang.x = -ang.x;
-                       makevectors(ang);
-                       if (!CheckWireframeBox(this, pos - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))
+                       traceline(pos, pos + 65536 * dir, true, this);
+                       dir = reflect(dir, trace_plane_normal);
+                       pos = trace_endpos;
+                       wepent.polyline[++idx] = pos;
+                       if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
+                       {
+                               n += 1;
+                               continue;
+                       }
+                       if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
                        {
                                n = max(2, idx);
                                break;
                        }
+                       // check size
+                       {
+                               vector ang = vectoangles2(trace_plane_normal, dir);
+                               ang.x = -ang.x;
+                               makevectors(ang);
+                               if (!CheckWireframeBox(this, pos - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))
+                               {
+                                       n = max(2, idx);
+                                       break;
+                               }
+                       }
+                       portal_number += 1;
+                       if (portal_number >= portal_max) break;
+                       if (portal_number == 1) portal1_idx = idx;
+               }
+               for (int idx = 0; idx < n - 1; ++idx)
+               {
+                       vector p = wepent.polyline[idx], q = wepent.polyline[idx + 1];
+                       if (idx == 0) p -= view_up * 16;  // line from player
+                       vector rgb = (idx < portal1_idx) ? '1 0 0' : '0 0 1';
+                       Draw_CylindricLine(p, q, 4, "", 1, 0, rgb, 0.5, DRAWFLAG_NORMAL, view_origin);
                }
-               portal_number += 1;
-               if (portal_number >= portal_max) break;
-               if (portal_number == 1) portal1_idx = idx;
-       }
-       for (int idx = 0; idx < n - 1; ++idx)
-       {
-               vector p = polyline[idx], q = polyline[idx + 1];
-               if (idx == 0) p -= view_up * 16;  // line from player
-               vector rgb = (idx < portal1_idx) ? '1 0 0' : '0 0 1';
-               Draw_CylindricLine(p, q, 4, "", 1, 0, rgb, 0.5, DRAWFLAG_NORMAL, view_origin);
        }
  }
  
@@@ -453,10 -459,17 +459,17 @@@ vector GetCurrentFov(float fov
                        zoomspeed = 3.5;
  
        zoomdir = button_zoom;
        if(hud == HUD_NORMAL && !spectatee_status)
-       if(switchweapon == activeweapon)
-       if((activeweapon == WEP_VORTEX && !WEP_CVAR(vortex, secondary)) || (activeweapon == WEP_RIFLE && !WEP_CVAR(rifle, secondary))) // do NOT use switchweapon here
-               zoomdir += button_attack2;
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       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
+                               zoomdir += button_attack2;
+               }
+       }
        if(spectatee_status > 0 || isdemo())
        {
                if(spectatorbutton_zoom)
                current_zoomfraction = (current_viewzoom - 1) / (1/zoomfactor - 1);
  
        if(zoomsensitivity < 1)
-               setsensitivityscale(pow(current_viewzoom, 1 - zoomsensitivity));
+               setsensitivityscale(current_viewzoom ** (1 - zoomsensitivity));
        else
                setsensitivityscale(1);
  
@@@ -623,7 -636,7 +636,7 @@@ float EnemyHitCheck(
        return SHOTTYPE_HITENEMY;
  }
  
- float TrueAimCheck()
+ float TrueAimCheck(entity wepent)
  {
        float nudge = 1; // added to traceline target and subtracted from result TOOD(divVerent): do we still need this? Doesn't the engine do this now for us?
        vector vecs, trueaimpoint, w_shotorg;
        ta = trueaim;
        mv = MOVE_NOMONSTERS;
  
-       switch(activeweapon) // WEAPONTODO
+       switch(wepent.activeweapon) // WEAPONTODO
        {
                case WEP_TUBA: // no aim
                case WEP_PORTO: // shoots from eye
                        mv = MOVE_NORMAL;
                        if(zoomscript_caught)
                        {
-                               tracebox(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * MAX_SHOT_DISTANCE, mv, ta);
+                               tracebox(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * max_shot_distance, mv, ta);
                                return EnemyHitCheck();
                        }
                        break;
  
        vecs = decompressShotOrigin(STAT(SHOTORG));
  
-       traceline(traceorigin, traceorigin + view_forward * MAX_SHOT_DISTANCE, mv, ta);
+       traceline(traceorigin, traceorigin + view_forward * max_shot_distance, mv, ta);
        trueaimpoint = trace_endpos;
  
        if(vdist((trueaimpoint - traceorigin), <, g_trueaim_minrange))
@@@ -719,7 -732,6 +732,6 @@@ float camera_mode
  const float CAMERA_FREE = 1;
  const float CAMERA_CHASE = 2;
  float reticle_type;
- string reticle_image;
  string NextFrameCommand;
  
  vector freeze_org, freeze_ang;
@@@ -747,7 -759,7 +759,7 @@@ bool WantEventchase(entity this
  {
        if(autocvar_cl_orthoview)
                return false;
-       if(STAT(GAMEOVER) || intermission)
+       if(STAT(GAME_STOPPED) || intermission)
                return true;
        if(this.viewloc)
                return true;
@@@ -811,9 -823,18 +823,18 @@@ void HitSound(
  {
        // varying sound pitch
  
+       bool have_arc = false;
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               entity wepent = viewmodels[slot];
+               if(wepent.activeweapon == WEP_ARC)
+                       have_arc = true;
+       }
        static float hitsound_time_prev = 0;
        // HACK: the only way to get the arc to sound consistent with pitch shift is to ignore cl_hitsound_antispam_time
-       float arc_hack = activeweapon == WEP_ARC && autocvar_cl_hitsound >= 2;
+       bool arc_hack = have_arc && autocvar_cl_hitsound >= 2;
        if (arc_hack || COMPARE_INCREASING(time, hitsound_time_prev) > autocvar_cl_hitsound_antispam_time)
        {
                if (autocvar_cl_hitsound && unaccounted_damage)
                sound(NULL, CH_INFO, SND_TYPEHIT, VOL_BASE, ATTN_NONE);
                typehit_time_prev = typehit_time;
        }
 +
 +      static float kill_time_prev = 0;
 +      float kill_time = STAT(KILL_TIME);
 +      if (COMPARE_INCREASING(kill_time, kill_time_prev) > autocvar_cl_hitsound_antispam_time)
 +      {
 +              sound(NULL, CH_INFO, SND_KILL, VOL_BASE, ATTN_NONE);
 +              kill_time_prev = kill_time;
 +      }
  }
  
  vector crosshair_getcolor(entity this, float health_stat)
@@@ -948,7 -961,7 +969,7 @@@ void HUD_Crosshair(entity this
  {
        float f, i, j;
        vector v;
-       if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAMEOVER) &&
+       if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAME_STOPPED) &&
                spectatee_status != -1 && !csqcplayer.viewloc && !MUTATOR_CALLHOOK(DrawCrosshair) &&
                !HUD_MinigameMenu_IsOpened() )
        {
                float shottype;
  
                // wcross_origin = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
-               wcross_origin = project_3d_to_2d(view_origin + MAX_SHOT_DISTANCE * view_forward);
+               wcross_origin = project_3d_to_2d(view_origin + max_shot_distance * view_forward);
                wcross_origin.z = 0;
                if(autocvar_crosshair_hittest)
                {
                        vector wcross_oldorigin;
+                       entity thiswep = viewmodels[0]; // TODO: unhardcode
                        wcross_oldorigin = wcross_origin;
-                       shottype = TrueAimCheck();
+                       shottype = TrueAimCheck(thiswep);
                        if(shottype == SHOTTYPE_HITWORLD)
                        {
                                v = wcross_origin - wcross_oldorigin;
          entity e = WEP_Null;
                if(autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1))
                {
-                       e = switchingweapon;
+                       entity wepent = viewmodels[0]; // TODO: unhardcode
+                       e = wepent.switchingweapon;
                        if(e)
                        {
                                if(autocvar_crosshair_per_weapon)
                                weapon_clipload = STAT(WEAPON_CLIPLOAD);
                                weapon_clipsize = STAT(WEAPON_CLIPSIZE);
  
-                               float ok_ammo_charge, ok_ammo_chargepool;
-                               ok_ammo_charge = STAT(OK_AMMO_CHARGE);
-                               ok_ammo_chargepool = STAT(OK_AMMO_CHARGEPOOL);
                                float vortex_charge, vortex_chargepool;
                                vortex_charge = STAT(VORTEX_CHARGE);
                                vortex_chargepool = STAT(VORTEX_CHARGEPOOL);
                                if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game
                                        vortex_charge_movingavg = vortex_charge;
  
+                               entity wepent = viewmodels[0]; // TODO: unhardcode
  
                                // handle the values
-                               if (autocvar_crosshair_ring && activeweapon == WEP_VORTEX && vortex_charge && autocvar_crosshair_ring_vortex) // ring around crosshair representing velocity-dependent damage for the vortex
+                               if (autocvar_crosshair_ring && wepent.activeweapon == WEP_VORTEX && vortex_charge && autocvar_crosshair_ring_vortex) // ring around crosshair representing velocity-dependent damage for the vortex
                                {
                                        if (vortex_chargepool || use_vortex_chargepool) {
                                                use_vortex_chargepool = 1;
                                        ring_rgb = wcross_color;
                                        ring_image = "gfx/crosshair_ring_nexgun.tga";
                                }
-                               else if (autocvar_crosshair_ring && activeweapon == WEP_MINE_LAYER && WEP_CVAR(minelayer, limit) && autocvar_crosshair_ring_minelayer)
+                               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.
                                        ring_alpha = autocvar_crosshair_ring_minelayer_alpha;
                                        ring_rgb = wcross_color;
                                        ring_image = "gfx/crosshair_ring.tga";
                                }
-                               else if (activeweapon == WEP_HAGAR && STAT(HAGAR_LOAD) && autocvar_crosshair_ring_hagar)
+                               else if (wepent.activeweapon == WEP_HAGAR && STAT(HAGAR_LOAD) && autocvar_crosshair_ring_hagar)
                                {
                                        ring_value = bound(0, STAT(HAGAR_LOAD) / WEP_CVAR_SEC(hagar, load_max), 1);
                                        ring_alpha = autocvar_crosshair_ring_hagar_alpha;
                                        ring_rgb = wcross_color;
                                        ring_image = "gfx/crosshair_ring.tga";
                                }
-                               else if (ok_ammo_charge)
-                               {
-                                       ring_value = ok_ammo_chargepool;
-                                       ring_alpha = autocvar_crosshair_ring_reload_alpha;
-                                       ring_rgb = wcross_color;
-                                       ring_image = "gfx/crosshair_ring.tga";
-                               }
                                else if(autocvar_crosshair_ring_reload && weapon_clipsize) // forces there to be only an ammo ring
                                {
                                        ring_value = bound(0, weapon_clipload / weapon_clipsize, 1);
  
                                        // Note: This is to stop Taoki from complaining that the image doesn't match all potential balances.
                                        // if a new image for another weapon is added, add the code (and its respective file/value) here
-                                       if ((activeweapon == WEP_RIFLE) && (weapon_clipsize == 80))
+                                       if ((wepent.activeweapon == WEP_RIFLE) && (weapon_clipsize == 80))
                                                ring_image = "gfx/crosshair_ring_rifle.tga";
                                        else
                                                ring_image = "gfx/crosshair_ring.tga";
                                }
-                               else if ( autocvar_crosshair_ring && autocvar_crosshair_ring_arc && arc_heat && activeweapon == WEP_ARC )
+                               else if ( autocvar_crosshair_ring && autocvar_crosshair_ring_arc && arc_heat && wepent.activeweapon == WEP_ARC )
                                {
                                        ring_value = arc_heat;
                                        ring_alpha = (1-arc_heat)*autocvar_crosshair_ring_arc_cold_alpha +
@@@ -1712,7 -1717,8 +1725,8 @@@ void CSQC_UpdateView(entity this, floa
  
        // run viewmodel_draw before updating view_angles to the angles calculated by WarpZone_FixView
        // viewmodel_draw needs to use the view_angles set by the engine on every CSQC_UpdateView call
-       viewmodel_draw(viewmodel);
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               viewmodel_draw(viewmodels[slot]);
  
        // Render the Scene
        view_origin = getpropertyvec(VF_ORIGIN);
  
                t = (time - blurtest_time0) / (blurtest_time1 - blurtest_time0);
                r = t * blurtest_radius;
-               f = 1 / pow(t, blurtest_power) - 1;
+               f = 1 / (t ** blurtest_power) - 1;
  
                cvar_set("r_glsl_postprocess", "1");
                cvar_set("r_glsl_postprocess_uservec1", strcat(ftos(r), " ", ftos(f), " 0 0"));
  
        ColorTranslateMode = autocvar_cl_stripcolorcodes;
  
-       // currently switching-to weapon (for crosshair)
-       switchingweapon = Weapons_from(STAT(SWITCHINGWEAPON));
-       // actually active weapon (for zoom)
-       activeweapon = Weapons_from(STAT(ACTIVEWEAPON));
-       switchweapon = Weapons_from(STAT(SWITCHWEAPON));
-       if(last_switchweapon != switchweapon)
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
-               weapontime = time;
-               last_switchweapon = switchweapon;
-               if(button_zoom && autocvar_cl_unpress_zoom_on_weapon_switch)
+               entity wepent = viewmodels[slot];
+               if(wepent.last_switchweapon != wepent.switchweapon)
                {
-                       localcmd("-zoom\n");
-                       button_zoom = false;
+                       weapontime = time;
+                       wepent.last_switchweapon = wepent.switchweapon;
+                       if(slot == 0 && button_zoom && autocvar_cl_unpress_zoom_on_weapon_switch)
+                       {
+                               localcmd("-zoom\n");
+                               button_zoom = false;
+                       }
+                       if(slot == 0 && autocvar_cl_unpress_attack_on_weapon_switch)
+                       {
+                               localcmd("-fire\n");
+                               localcmd("-fire2\n");
+                               button_attack2 = false;
+                       }
                }
-               if(autocvar_cl_unpress_attack_on_weapon_switch)
+               if(wepent.last_activeweapon != wepent.activeweapon)
                {
-                       localcmd("-fire\n");
-                       localcmd("-fire2\n");
-                       button_attack2 = false;
-               }
-       }
-       if(last_activeweapon != activeweapon)
-       {
-               last_activeweapon = activeweapon;
+                       wepent.last_activeweapon = wepent.activeweapon;
  
-               e = activeweapon;
-               if(e.netname != "")
-                       localcmd(strcat("\ncl_hook_activeweapon ", e.netname), "\n");
-               else
-                       localcmd("\ncl_hook_activeweapon none\n");
+                       e = wepent.activeweapon;
+                       if(e.netname != "")
+                               localcmd(strcat("\ncl_hook_activeweapon ", e.netname), "\n");
+                       else if(slot == 0)
+                               localcmd("\ncl_hook_activeweapon none\n");
+               }
        }
  
        // ALWAYS Clear Current Scene First
  
        if(autocvar_cl_reticle)
        {
-               Weapon wep = activeweapon;
+               string reticle_image = string_null;
+               bool wep_zoomed = false;
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       entity wepe = viewmodels[slot];
+                       Weapon wep = wepe.activeweapon;
+                       if(wep != WEP_Null && wep.wr_zoom)
+                       {
+                               bool do_zoom = wep.wr_zoom(wep, NULL);
+                               if(!reticle_image && wep.w_reticle && wep.w_reticle != "")
+                                       reticle_image = wep.w_reticle;
+                               wep_zoomed += do_zoom;
+                       }
+               }
                // Draw the aiming reticle for weapons that use it
                // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use
                // It must be a persisted float for fading out to work properly (you let go of the zoom button for
                        // no zoom reticle while dead
                        reticle_type = 0;
                }
-               else if(wep.wr_zoomreticle(wep) && autocvar_cl_reticle_weapon)
+               else if(wep_zoomed && autocvar_cl_reticle_weapon)
                {
-                       if(reticle_image != "") { reticle_type = 2; }
+                       if(reticle_image) { reticle_type = 2; }
                        else { reticle_type = 0; }
                }
                else if(button_zoom || zoomscript_caught)
                                switch(reticle_type)
                                {
                                        case 1: drawpic(reticle_pos, "gfx/reticle_normal", reticle_size, '1 1 1', f * autocvar_cl_reticle_normal_alpha, DRAWFLAG_NORMAL); break;
-                                       case 2: drawpic(reticle_pos, reticle_image, reticle_size, '1 1 1', f * autocvar_cl_reticle_weapon_alpha, DRAWFLAG_NORMAL); break;
+                                       case 2: if(reticle_image) drawpic(reticle_pos, reticle_image, reticle_size, '1 1 1', f * autocvar_cl_reticle_weapon_alpha, DRAWFLAG_NORMAL); break;
                                }
                        }
                }
diff --combined qcsrc/common/stats.qh
@@@ -55,18 -55,12 +55,12 @@@ REGISTER_STAT(PL_CROUCH_MAX, vector
  
  REGISTER_STAT(KH_KEYS, int)
  
- /** weapon requested to switch to; next WANTED weapon (for HUD) */
- REGISTER_STAT(SWITCHWEAPON, int)
- /** weapon currently being switched to (is copied from switchweapon once switch is possible) */
- REGISTER_STAT(SWITCHINGWEAPON, int)
- REGISTER_STAT(WEAPON_NEXTTHINK, float)
  #ifdef SVQC
- SPECTATE_COPYFIELD(_STAT(WEAPON_NEXTTHINK))
  float W_WeaponRateFactor(entity this);
- float gameover;
+ float game_stopped;
  #endif
  REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
- REGISTER_STAT(GAMEOVER, int, gameover)
+ REGISTER_STAT(GAME_STOPPED, int, game_stopped)
  REGISTER_STAT(GAMESTARTTIME, float)
  REGISTER_STAT(STRENGTH_FINISHED, float)
  REGISTER_STAT(INVINCIBLE_FINISHED, float)
@@@ -115,8 -109,6 +109,6 @@@ REGISTER_STAT(NADE_BONUS_SCORE, float
  REGISTER_STAT(HEALING_ORB, float)
  REGISTER_STAT(HEALING_ORB_ALPHA, float)
  REGISTER_STAT(PLASMA, int)
- REGISTER_STAT(OK_AMMO_CHARGE, float)
- REGISTER_STAT(OK_AMMO_CHARGEPOOL, float)
  REGISTER_STAT(FROZEN, int)
  REGISTER_STAT(REVIVE_PROGRESS, float)
  REGISTER_STAT(ROUNDLOST, int)
@@@ -126,9 -118,13 +118,14 @@@ REGISTER_STAT(CAPTURE_PROGRESS, float
  REGISTER_STAT(ENTRAP_ORB, float)
  REGISTER_STAT(ENTRAP_ORB_ALPHA, float)
  REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime)
 +REGISTER_STAT(KILL_TIME, float)
  
  #ifdef SVQC
+ bool autocvar_g_ctf_leaderboard;
+ #endif
+ REGISTER_STAT(CTF_SHOWLEADERBOARD, bool, autocvar_g_ctf_leaderboard)
+ #ifdef SVQC
  int autocvar_g_multijump;
  float autocvar_g_multijump_add;
  float autocvar_g_multijump_speed;
@@@ -218,22 -214,26 +215,26 @@@ float autocvar_sv_dodging_ramp_time
  float autocvar_sv_dodging_up_speed;
  bool autocvar_sv_dodging_wall_dodging;
  bool autocvar_sv_dodging_air_dodging;
+ float autocvar_sv_dodging_maxspeed = 450;
  #endif
  
+ #if 0
  REGISTER_STAT(DODGING, int, g_dodging)
  REGISTER_STAT(DODGING_DELAY, float, autocvar_sv_dodging_delay)
  REGISTER_STAT(DODGING_DISTANCE_THRESHOLD, float, autocvar_sv_dodging_wall_distance_threshold)
- REGISTER_STAT(DODGING_FROZEN, int, autocvar_sv_dodging_frozen)
  REGISTER_STAT(DODGING_FROZEN_NO_DOUBLETAP, int, autocvar_sv_dodging_frozen_doubletap)
  REGISTER_STAT(DODGING_HEIGHT_THRESHOLD, float, autocvar_sv_dodging_height_threshold)
  REGISTER_STAT(DODGING_HORIZ_SPEED, float, autocvar_sv_dodging_horiz_speed)
  REGISTER_STAT(DODGING_HORIZ_SPEED_FROZEN, float, autocvar_sv_dodging_horiz_speed_frozen)
  REGISTER_STAT(DODGING_RAMP_TIME, float, autocvar_sv_dodging_ramp_time)
- /** cvar loopback */
- REGISTER_STAT(DODGING_TIMEOUT, float)
  REGISTER_STAT(DODGING_UP_SPEED, float, autocvar_sv_dodging_up_speed)
  REGISTER_STAT(DODGING_WALL, bool, autocvar_sv_dodging_wall_dodging)
  REGISTER_STAT(DODGING_AIR, bool, autocvar_sv_dodging_air_dodging)
+ REGISTER_STAT(DODGING_MAXSPEED, float, autocvar_sv_dodging_maxspeed)
+ #endif
+ /** cvar loopback */
+ REGISTER_STAT(DODGING_FROZEN, int, autocvar_sv_dodging_frozen)
+ REGISTER_STAT(DODGING_TIMEOUT, float)
  
  REGISTER_STAT(JETPACK_ACCEL_SIDE, float, autocvar_g_jetpack_acceleration_side)
  REGISTER_STAT(JETPACK_ACCEL_UP, float, autocvar_g_jetpack_acceleration_up)
diff --combined qcsrc/server/defs.qh
@@@ -47,8 -47,6 +47,6 @@@ float server_is_dedicated
  .float        pain_frame;                     //"
  .float  crouch;       // Crouching or not?
  
- .float        strength_finished = _STAT(STRENGTH_FINISHED);
- .float        invincible_finished = _STAT(INVINCIBLE_FINISHED);
  .float        superweapons_finished = _STAT(SUPERWEAPONS_FINISHED);
  
  .float cnt; // used in too many places
@@@ -96,7 -94,7 +94,7 @@@ const float MAX_DAMAGEEXTRARADIUS = 16
  .float                dmgtime;
  
  .float                killcount;
 -.float damage_dealt, typehitsound;
 +.float damage_dealt, typehitsound, killsound;
  
  .float watersound_finished;
  .float iscreature;
  
  // WEAPONTODO
  .float autoswitch;
- bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain);
+ bool client_hasweapon(entity this, Weapon wpn, .entity weaponentity, float andammo, bool complain);
  void w_clear(Weapon thiswep, entity actor, .entity weaponentity, int fire);
  void w_ready(Weapon thiswep, entity actor, .entity weaponentity, int fire);
  // VorteX: standalone think for weapons, so normal think on weaponentity can be reserved by weaponflashes (which needs update even player dies)
@@@ -208,7 -206,7 +206,7 @@@ float bot_waypoints_for_items
  #else
  #define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).attack_finished_single[slot])
  #endif
- #define ATTACK_FINISHED(ent, slot) ATTACK_FINISHED_FOR(ent, PS(ent).m_weapon.m_id, slot)
+ #define ATTACK_FINISHED(ent, slot) ATTACK_FINISHED_FOR(ent, ent.(weaponentity).m_weapon.m_id, slot)
  
  // assault game mode: Which team is attacking in this round?
  float assault_attacker_team;
@@@ -319,7 -317,6 +317,7 @@@ string matchid
  
  .float hit_time = _STAT(HIT_TIME);
  .float typehit_time = _STAT(TYPEHIT_TIME);
 +.float kill_time = _STAT(KILL_TIME);
  
  .float damage_dealt_total = _STAT(DAMAGE_DEALT_TOTAL);
  
@@@ -378,7 -375,6 +376,6 @@@ const float ACTIVE_TOGGLE  = 3
  .float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
  
  .float player_blocked;
- .float weapon_blocked; // weapon use disabled
  
  .float revive_progress = _STAT(REVIVE_PROGRESS);
  .float revival_time; // time at which player was last revived
diff --combined qcsrc/server/g_damage.qc
@@@ -33,7 -33,7 +33,7 @@@ void UpdateFrags(entity player, int f
  void GiveFrags (entity attacker, entity targ, float f, int deathtype)
  {
        // TODO route through PlayerScores instead
-       if(gameover) return;
+       if(game_stopped) return;
  
        if(f < 0)
        {
  
        PlayerScore_Add(targ, SP_DEATHS, 1);
  
+       .entity weaponentity = weaponentities[0]; // TODO: unhardcode
        if(targ != attacker) // not for suicides
        if(g_weaponarena_random)
        {
                // after a frag, exchange the current weapon (or the culprit, if detectable) by a new random weapon
                Weapon culprit = DEATH_WEAPONOF(deathtype);
-               if(!culprit) culprit = PS(attacker).m_weapon;
-               else if(!(attacker.weapons & (culprit.m_wepset))) culprit = PS(attacker).m_weapon;
+               if(!culprit) culprit = attacker.(weaponentity).m_weapon;
+               else if(!(attacker.weapons & (culprit.m_wepset))) culprit = attacker.(weaponentity).m_weapon;
  
                if(g_weaponarena_random_with_blaster && culprit == WEP_BLASTER) // WEAPONTODO: Shouldn't this be in a mutator?
                {
@@@ -97,8 -99,8 +99,8 @@@
                }
  
                // after a frag, choose another random weapon set
-               if (!(attacker.weapons & WepSet_FromWeapon(PS(attacker).m_weapon)))
-                       W_SwitchWeapon_Force(attacker, w_getbestweapon(attacker));
+               if (!(attacker.weapons & WepSet_FromWeapon(attacker.(weaponentity).m_weapon)))
+                       W_SwitchWeapon_Force(attacker, w_getbestweapon(attacker, weaponentity), weaponentity);
        }
  
        // FIXME fix the mess this is (we have REAL points now!)
  
  string AppendItemcodes(string s, entity player)
  {
-       int w = PS(player).m_weapon.m_id;
-       //if(w == 0)
-       //      w = player.switchweapon;
-       if(w == 0)
-               w = player.cnt; // previous weapon!
-       s = strcat(s, ftos(w));
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               .entity weaponentity = weaponentities[slot];
+               int w = player.(weaponentity).m_weapon.m_id;
+               if(w == 0)
+                       w = player.(weaponentity).cnt; // previous weapon
+               if(w != 0 || slot == 0)
+                       s = strcat(s, ftos(w));
+       }
        if(time < player.strength_finished)
                s = strcat(s, "S");
        if(time < player.invincible_finished)
@@@ -370,8 -375,6 +375,8 @@@ void Obituary(entity attacker, entity i
                        attacker.taunt_soundtime = time + 1;
                        attacker.killcount = attacker.killcount + 1;
  
 +                      attacker.killsound += 1;
 +
                        #define SPREE_ITEM(counta,countb,center,normal,gentle) \
                                case counta: \
                                { \
@@@ -558,9 -561,17 +563,17 @@@ void Freeze (entity targ, float freeze_
  
        Ice_Think(ice);
  
-       RemoveGrapplingHook(targ);
+       RemoveGrapplingHooks(targ);
  
-       FOREACH_CLIENT(IS_PLAYER(it) && it.hook.aiment == targ, LAMBDA(RemoveGrapplingHook(it)));
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+           {
+               .entity weaponentity = weaponentities[slot];
+               if(it.(weaponentity).hook.aiment == targ)
+                       RemoveHook(it.(weaponentity).hook);
+           }
+       });
  
        // add waypoint
        if(show_waypoint)
@@@ -587,7 -598,15 +600,15 @@@ void Unfreeze (entity targ
  
        WaypointSprite_Kill(targ.waypointsprite_attached);
  
-       FOREACH_CLIENT(IS_PLAYER(it) && it.hook.aiment == targ, LAMBDA(RemoveGrapplingHook(it)));
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+           {
+               .entity weaponentity = weaponentities[slot];
+               if(it.(weaponentity).hook.aiment == targ)
+                       RemoveHook(it.(weaponentity).hook);
+           }
+       });
  
        // remove the ice block
        if(targ.iceblock)
@@@ -604,7 -623,7 +625,7 @@@ void Damage (entity targ, entity inflic
        mirrordamage = 0;
        mirrorforce = 0;
  
-       if (gameover || targ.killcount == FRAGS_SPECTATOR)
+       if (game_stopped || targ.killcount == FRAGS_SPECTATOR)
                return;
  
      damage_targ = targ;
      damage_attacker = attacker;
        attacker_save = attacker;
  
-       if(IS_PLAYER(targ))
-               if(targ.hook)
-                       if(targ.hook.aiment)
-                               if(targ.hook.aiment == attacker)
-                                       RemoveGrapplingHook(targ); // STOP THAT, you parasite!
        // special rule: gravity bomb does not hit team mates (other than for disconnecting the hook)
        if(DEATH_ISWEAPON(deathtype, WEP_HOOK) || DEATH_ISWEAPON(deathtype, WEP_TUBA))
        {
                }
  
                // should this be changed at all? If so, in what way?
-               MUTATOR_CALLHOOK(PlayerDamage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force);
+               MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force);
                damage = M_ARGV(4, float);
                mirrordamage = M_ARGV(5, float);
                force = M_ARGV(6, vector);
  
+               if(IS_PLAYER(targ) && damage > 0 && attacker)
+               {
+                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+                   {
+                       .entity weaponentity = weaponentities[slot];
+                       if(targ.(weaponentity).hook && targ.(weaponentity).hook.aiment == attacker)
+                               RemoveHook(targ.(weaponentity).hook);
+                   }
+               }
                if(STAT(FROZEN, targ))
                if(deathtype != DEATH_HURTTRIGGER.m_id && deathtype != DEATH_TEAMCHANGE.m_id && deathtype != DEATH_AUTOTEAMCHANGE.m_id)
                {
@@@ -1006,7 -1029,7 +1031,7 @@@ float RadiusDamageForSource (entity inf
                                                        LOG_INFOF("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
  
  
-                                               total = 0.25 * pow(max(mininv_f, mininv_d), 2);
+                                               total = 0.25 * (max(mininv_f, mininv_d) ** 2);
  
                                                if(autocvar_g_throughfloor_debug)
                                                        LOG_INFOF(" steps=%f", total);
diff --combined qcsrc/server/g_world.qc
@@@ -227,9 -227,11 +227,11 @@@ void cvar_changes_init(
                BADCVAR("sys_specialcharactertranslation");
                BADCVAR("timeformat");
                BADCVAR("timestamps");
+               BADCVAR("g_require_stats");
                BADPREFIX("developer_");
                BADPREFIX("g_ban_");
                BADPREFIX("g_banned_list");
+               BADPREFIX("g_require_stats_");
                BADPREFIX("g_chat_flood_");
                BADPREFIX("g_ghost_items");
                BADPREFIX("g_playerstats_");
  
                // mapinfo
                BADCVAR("fraglimit");
+               BADCVAR("g_arena");
                BADCVAR("g_assault");
                BADCVAR("g_ca");
                BADCVAR("g_ca_teams");
+               BADCVAR("g_conquest");
                BADCVAR("g_ctf");
                BADCVAR("g_cts");
+               BADCVAR("g_dotc");
                BADCVAR("g_dm");
                BADCVAR("g_domination");
                BADCVAR("g_domination_default_teams");
                BADCVAR("g_freezetag");
                BADCVAR("g_freezetag_teams");
                BADCVAR("g_invasion_teams");
+               BADCVAR("g_jailbreak");
+               BADCVAR("g_jailbreak_teams");
                BADCVAR("g_keepaway");
                BADCVAR("g_keyhunt");
                BADCVAR("g_keyhunt_teams");
                BADCVAR("g_race_laps_limit");
                BADCVAR("g_race_qualifying_timelimit");
                BADCVAR("g_race_qualifying_timelimit_override");
+               BADCVAR("g_snafu");
                BADCVAR("g_tdm");
                BADCVAR("g_tdm_teams");
+               BADCVAR("g_vip");
                BADCVAR("leadlimit");
                BADCVAR("nextmap");
                BADCVAR("teamplay");
                BADCVAR("timelimit");
+               BADCVAR("g_mapinfo_ignore_warnings");
  
                // long
                BADCVAR("hostname");
                BADCVAR("gameversion");
                BADCVAR("g_allow_oldvortexbeam");
                BADCVAR("g_balance_kill_delay");
+               BADCVAR("g_buffs_pickup_anyway");
+               BADCVAR("g_buffs_randomize");
                BADCVAR("g_campcheck_distance");
                BADCVAR("g_ca_point_leadlimit");
                BADCVAR("g_ca_point_limit");
                BADCVAR("g_freezetag_point_limit");
                BADCVAR("g_hats");
                BADCVAR("g_invasion_point_limit");
+               BADCVAR("g_jump_grunt");
                BADCVAR("g_keyhunt_point_leadlimit");
+               BADCVAR("g_maplist_selectrandom");
                BADCVAR("g_nexball_goalleadlimit");
                BADCVAR("g_new_toys_use_pickupsound");
                BADCVAR("g_physics_predictall");
                BADCVAR("g_piggyback");
+               BADCVAR("g_playerclip_collisions");
                BADCVAR("g_tdm_point_leadlimit");
                BADCVAR("g_tdm_point_limit");
                BADCVAR("leadlimit_and_fraglimit");
                BADCVAR("sv_precacheplayermodels");
                BADCVAR("sv_stepheight");
                BADCVAR("sv_timeout");
+               BADCVAR("sv_weapons_modeloverride");
                BADPREFIX("crypto_");
                BADPREFIX("gameversion_");
                BADPREFIX("g_chat_");
                BADCVAR("g_nexball_goallimit");
                BADCVAR("g_norecoil");
                BADCVAR("g_physics_clientselect");
+               BADCVAR("g_pinata");
                BADCVAR("g_powerups");
                BADCVAR("g_spawnshieldtime");
                BADCVAR("g_start_delay");
@@@ -1137,7 -1154,7 +1154,7 @@@ float(float exponent) MaplistMethod_Shu
                string newlist;
  
                // now reinsert this at another position
-               insertpos = pow(random(), 1 / exponent);       // ]0, 1]
+               insertpos = (random() ** (1 / exponent));       // ]0, 1]
                insertpos = insertpos * (Map_Count - 1);       // ]0, Map_Count - 1]
                insertpos = ceil(insertpos) + 1;               // {2, 3, 4, ..., Map_Count}
                LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos));
@@@ -1447,11 -1464,11 +1464,11 @@@ void DumpStats(float final
                        s = strcat(s, "spectator:");
  
                if(to_console)
-                       LOG_INFO(s, it.netname, "\n");
+                       LOG_INFO(s, playername(it, false), "\n");
                if(to_eventlog)
-                       GameLogEcho(strcat(s, ftos(it.playerid), ":", it.netname));
+                       GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it, false)));
                if(to_file)
-                       fputs(file, strcat(s, it.netname, "\n"));
+                       fputs(file, strcat(s, playername(it, false), "\n"));
        ));
  
        if(teamplay)
@@@ -1528,9 -1545,8 +1545,8 @@@ only called if a time or frag limit ha
  */
  void NextLevel()
  {
-       gameover = true;
-       intermission_running = 1;
+       game_stopped = true;
+       intermission_running = 1; // game over
  
        // enforce a wait time before allowing changelevel
        if(player_count > 0)
        FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
                FixIntermissionClient(it);
                if(it.winning)
-                       bprint(it.netname, " ^7wins.\n");
+                       bprint(playername(it, false), " ^7wins.\n");
        ));
  
        target_music_kill();
@@@ -1587,7 -1603,7 +1603,7 @@@ Exit deathmatch games upon condition
  */
  void CheckRules_Player(entity this)
  {
-       if (gameover)   // someone else quit the game already
+       if (game_stopped) // someone else quit the game already
                return;
  
        if(!IS_DEAD(this))
@@@ -2030,7 -2046,9 +2046,9 @@@ void Physics_Frame(
                if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH || it.move_movetype == MOVETYPE_PHYSICS)
                        continue;
  
-               set_movetype(it, it.move_movetype);
+               //set_movetype(it, it.move_movetype);
+               // inline the set_movetype function, since this is called a lot
+               it.movetype = (it.move_qcphysics) ? MOVETYPE_NONE : it.move_movetype;
  
                if(it.move_movetype == MOVETYPE_NONE)
                        continue;
@@@ -2068,8 -2086,6 +2086,8 @@@ void EndFrame(
                entity e = IS_SPEC(it) ? it.enemy : it;
                if (e.typehitsound) {
                        it.typehit_time = time;
 +              } else if (e.killsound) {
 +                      it.kill_time = time;
                } else if (e.damage_dealt) {
                        it.hit_time = time;
                        it.damage_dealt_total += ceil(e.damage_dealt);
        FOREACH_CLIENT(true, {
                it.typehitsound = false;
                it.damage_dealt = 0;
 +              it.killsound = false;
                antilag_record(it, CS(it), altime);
        });
        IL_EACH(g_monsters, true,
        {
                antilag_record(it, it, altime);
        });
-       FOREACH_CLIENT(PS(it), {
-               PlayerState s = PS(it);
-               s.ps_push(s, it);
-       });
        systems_update();
        IL_ENDFRAME();
  }
@@@ -2167,7 -2178,7 +2181,7 @@@ void RestoreGame(
  
  void Shutdown()
  {
-       gameover = 2;
+       game_stopped = 2;
  
        if(world_initialized > 0)
        {