]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/wepent_experimental
authorMario <mario@smbclan.net>
Sat, 5 Nov 2016 02:43:35 +0000 (12:43 +1000)
committerMario <mario@smbclan.net>
Sat, 5 Nov 2016 02:43:35 +0000 (12:43 +1000)
# Conflicts:
# qcsrc/client/view.qc
# qcsrc/common/weapons/weapon/tuba.qc

13 files changed:
1  2 
qcsrc/client/defs.qh
qcsrc/client/main.qc
qcsrc/client/view.qc
qcsrc/common/weapons/weapon/hagar.qc
qcsrc/common/weapons/weapon/tuba.qc
qcsrc/server/bot/default/havocbot/havocbot.qc
qcsrc/server/client.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/weapons/selection.qc
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponsystem.qc

diff --combined qcsrc/client/defs.qh
index 7068340d6ef8d646040e2884ad74f0e5422ca200,437e1dd75bab5538dc80c311102adcb7719a8ebd..b89ecf55ac1de5dce663f1b7b4fa42991126e262
@@@ -27,7 -27,7 +27,7 @@@ float         dmg_take
  .int team;
  .int team_size;
  
- float vid_conwidth, vid_conheight;
+ float vid_conheight;
  int binddb;
  
  // QUALIFYING
@@@ -88,7 -88,7 +88,7 @@@ vector lightning_shotorigin[4]
  float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power;
  #endif
  
- float servertime, serverprevtime, serverdeltatime;
+ float serverprevtime, serverdeltatime;
  
  float ticrate;
  
@@@ -98,6 -98,10 +98,6 @@@ const float MAX_DAMAGEEXTRARADIUS = 16
  .float damageextraradius;
  .void(entity this, float thisdmg, int hittype, vector org, vector thisforce) event_damage;
  
 -// only for Porto
 -float angles_held_status;
 -vector angles_held;
 -
  // weapons
  .bool silent;
  
diff --combined qcsrc/client/main.qc
index 701d40a0aa4bf2b3b1b6b43fe40f339e209d8e35,36ca217240f0ba0e8a6966fcb25b06829df43491..f48c0c530a40cf631c10c1e54c696d80db6dc741
@@@ -15,6 -15,7 +15,7 @@@
  #include <common/mapinfo.qh>
  #include <common/minigames/cl_minigames.qh>
  #include <common/minigames/cl_minigames_hud.qh>
+ #include <common/net_linked.qh>
  #include <common/net_notice.qh>
  #include <common/triggers/include.qh>
  #include <common/vehicles/all.qh>
@@@ -493,6 -494,16 +494,6 @@@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool 
  
        spectatorbutton_zoom = (f & 4);
  
 -      if(f & 8)
 -      {
 -              angles_held_status = 1;
 -              angles_held.x = ReadAngle();
 -              angles_held.y = ReadAngle();
 -              angles_held.z = 0;
 -      }
 -      else
 -              angles_held_status = 0;
 -
        if(f & 16)
        {
                num_spectators = ReadByte();
diff --combined qcsrc/client/view.qc
index d4e4d6093292e92b6d05eabcd6e03b2b98989eb5,12f4c6a1d0e68e5b72ebb0a475d31c5dee265045..9c19493dced8d4a72e34053237c816449e44335e
@@@ -13,6 -13,7 +13,7 @@@
  #include <common/ent_cs.qh>
  #include <common/anim.qh>
  #include <common/constants.qh>
+ #include <common/net_linked.qh>
  #include <common/debug.qh>
  #include <common/mapinfo.qh>
  #include <common/gamemodes/_mod.qh>
@@@ -21,6 -22,8 +22,8 @@@
  #include <common/triggers/target/music.qh>
  #include <common/teams.qh>
  
+ #include <common/weapons/weapon/tuba.qh>
  #include <common/vehicles/all.qh>
  #include <common/weapons/_all.qh>
  #include <common/viewloc.qh>
@@@ -281,11 -284,9 +284,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;
                if(wep == WEP_TUBA)
                {
                           (this.tuba_instrument == 1) ? "akordeon" :
                                                     "kleinbottle";
                }
 -              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;
        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);
@@@ -375,70 -377,65 +378,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);
        }
  }
  
@@@ -460,17 -457,10 +463,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)
@@@ -637,7 -627,7 +640,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
@@@ -733,6 -723,7 +736,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;
@@@ -824,18 -815,9 +827,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)
@@@ -999,9 -981,8 +1002,9 @@@ void HUD_Crosshair(entity this
                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 +
@@@ -1718,8 -1708,7 +1721,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);
  
        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 = "";
 +              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(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_image != "") { reticle_type = 2; }
                        else { reticle_type = 0; }
                }
                else if(button_zoom || zoomscript_caught)
index aa564565bb2c7cf591bdb298a2bdbeab66ad23a9,684ee75d377540a15de2911269dbf6ccf078d982..eff656a6e1dce2ae5f271c4de27c454a6a654dc7
@@@ -139,7 -139,7 +139,7 @@@ void W_Hagar_Attack(Weapon thiswep, ent
  {
        entity missile;
  
 -      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(hagar, ammo));
 +      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(hagar, ammo), weaponentity);
  
        W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage));
  
@@@ -184,7 -184,7 +184,7 @@@ void W_Hagar_Attack2(Weapon thiswep, en
  {
        entity missile;
  
 -      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo));
 +      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo), weaponentity);
  
        W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
  
@@@ -236,7 -236,7 +236,7 @@@ void W_Hagar_Attack2_Load_Release(entit
        vector s;
        vector forward, right, up;
  
 -      if(!actor.hagar_load)
 +      if(!actor.(weaponentity).hagar_load)
                return;
  
        weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire));
        right = v_right;
        up = v_up;
  
 -      shots = actor.hagar_load;
 +      shots = actor.(weaponentity).hagar_load;
        missile = NULL;
        for(counter = 0; counter < shots; ++counter)
        {
        }
  
        weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready);
 -      actor.hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor(actor);
 -      actor.hagar_load = 0;
 +      actor.(weaponentity).hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor(actor);
 +      actor.(weaponentity).hagar_load = 0;
 +
 +      if(weaponslot(weaponentity) == 0)
 +              actor.hagar_load = 0;
  }
  
  void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity)
  {
        // loadable hagar secondary attack, must always run each frame
  
-       if(time < game_starttime || PS(actor).m_switchweapon != WEP_HAGAR)
+       if(time < game_starttime)
                return;
  
 -      bool loaded = actor.hagar_load >= WEP_CVAR_SEC(hagar, load_max);
 +      bool loaded = actor.(weaponentity).hagar_load >= WEP_CVAR_SEC(hagar, load_max);
  
        // this is different than WR_CHECKAMMO when it comes to reloading
        bool enough_ammo;
        if(actor.items & IT_UNLIMITED_WEAPON_AMMO)
                enough_ammo = true;
        else if(autocvar_g_balance_hagar_reload_ammo)
 -              enough_ammo = actor.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
 +              enough_ammo = actor.(weaponentity).(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
        else
                enough_ammo = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(hagar, ammo);
  
        {
                if(PHYS_INPUT_BUTTON_ATCK(actor) && WEP_CVAR_SEC(hagar, load_abort))
                {
 -                      if(actor.hagar_load)
 +                      if(actor.(weaponentity).hagar_load)
                        {
                                // if we pressed primary fire while loading, unload all rockets and abort
                                actor.(weaponentity).state = WS_READY;
 -                              W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo) * actor.hagar_load * -1); // give back ammo
 -                              actor.hagar_load = 0;
 +                              W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo) * actor.(weaponentity).hagar_load * -1, weaponentity); // give back ammo
 +                              actor.(weaponentity).hagar_load = 0;
 +                              if(weaponslot(weaponentity) == 0)
 +                                      actor.hagar_load = 0;
                                sound(actor, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
  
                                // pause until we can load rockets again, once we re-press the alt fire button
 -                              actor.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor(actor);
 +                              actor.(weaponentity).hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor(actor);
  
                                // require letting go of the alt fire button before we can load again
 -                              actor.hagar_loadblock = true;
 +                              actor.(weaponentity).hagar_loadblock = true;
                        }
                }
                else
                        // check if we can attempt to load another rocket
                        if(!stopped)
                        {
 -                              if(!actor.hagar_loadblock && actor.hagar_loadstep < time)
 +                              if(!actor.(weaponentity).hagar_loadblock && actor.(weaponentity).hagar_loadstep < time)
                                {
 -                                      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo));
 +                                      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo), weaponentity);
                                        actor.(weaponentity).state = WS_INUSE;
 -                                      actor.hagar_load += 1;
 +                                      actor.(weaponentity).hagar_load += 1;
                                        sound(actor, CH_WEAPON_B, SND_HAGAR_LOAD, VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most
  
 -                                      if(actor.hagar_load >= WEP_CVAR_SEC(hagar, load_max))
 +                                      if(actor.(weaponentity).hagar_load >= WEP_CVAR_SEC(hagar, load_max))
                                                stopped = true;
                                        else
 -                                              actor.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor(actor);
 +                                              actor.(weaponentity).hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor(actor);
                                }
                        }
 -                      if(stopped && !actor.hagar_loadbeep && actor.hagar_load) // prevents the beep from playing each frame
 +                      if(stopped && !actor.(weaponentity).hagar_loadbeep && actor.(weaponentity).hagar_load) // prevents the beep from playing each frame
                        {
                                // if this is the last rocket we can load, play a beep sound to notify the player
                                sound(actor, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
 -                              actor.hagar_loadbeep = true;
 -                              actor.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_hold) * W_WeaponRateFactor(actor);
 +                              actor.(weaponentity).hagar_loadbeep = true;
 +                              actor.(weaponentity).hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_hold) * W_WeaponRateFactor(actor);
                        }
                }
        }
 -      else if(actor.hagar_loadblock)
 +      else if(actor.(weaponentity).hagar_loadblock)
        {
                // the alt fire button has been released, so re-enable loading if blocked
 -              actor.hagar_loadblock = false;
 +              actor.(weaponentity).hagar_loadblock = false;
        }
  
 -      if(actor.hagar_load)
 +      if(actor.(weaponentity).hagar_load)
        {
                // play warning sound if we're about to release
 -              if(stopped && actor.hagar_loadstep - 0.5 < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)
 +              if(stopped && actor.(weaponentity).hagar_loadstep - 0.5 < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)
                {
 -                      if(!actor.hagar_warning) // prevents the beep from playing each frame
 +                      if(!actor.(weaponentity).hagar_warning) // prevents the beep from playing each frame
                        {
                                // we're about to automatically release after holding time, play a beep sound to notify the player
                                sound(actor, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
 -                              actor.hagar_warning = true;
 +                              actor.(weaponentity).hagar_warning = true;
                        }
                }
  
                // release if player let go of button or if they've held it in too long
 -              if(!PHYS_INPUT_BUTTON_ATCK2(actor) || (stopped && actor.hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0))
 +              if(!PHYS_INPUT_BUTTON_ATCK2(actor) || (stopped && actor.(weaponentity).hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0))
                {
                        actor.(weaponentity).state = WS_READY;
                        W_Hagar_Attack2_Load_Release(actor, weaponentity);
        }
        else
        {
 -              actor.hagar_loadbeep = false;
 -              actor.hagar_warning = false;
 +              actor.(weaponentity).hagar_loadbeep = false;
 +              actor.(weaponentity).hagar_warning = false;
  
                // we aren't checking ammo during an attack, so we must do it here
 -              if(!(thiswep.wr_checkammo1(thiswep, actor) + thiswep.wr_checkammo2(thiswep, actor)))
 +              if(!(thiswep.wr_checkammo1(thiswep, actor, weaponentity) + thiswep.wr_checkammo2(thiswep, actor, weaponentity)))
                if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                {
                        // note: this doesn't force the switch
 -                      W_SwitchToOtherWeapon(actor);
 +                      W_SwitchToOtherWeapon(actor, weaponentity);
                        return;
                }
        }
  
  void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
  {
 -      if(!(fire & 1) || actor.hagar_load || actor.hagar_loadblock || PS(actor).m_switchweapon != WEP_HAGAR)
 +      if(!(fire & 1) || actor.(weaponentity).hagar_load || actor.(weaponentity).hagar_loadblock || actor.(weaponentity).m_switchweapon != WEP_HAGAR)
        {
                w_ready(thiswep, actor, weaponentity, fire);
                return;
        }
  
 -      if(!thiswep.wr_checkammo1(thiswep, actor))
 +      if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity))
        if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
        {
 -              W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +              W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
                w_ready(thiswep, actor, weaponentity, fire);
                return;
        }
        weapon_thinkf(actor, weaponentity, theframe, WEP_CVAR_PRI(hagar, refire), W_Hagar_Attack_Auto);
  }
  
 -METHOD(Hagar, wr_aim, void(entity thiswep, entity actor))
 +METHOD(Hagar, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
  {
      if(random()>0.15)
 -        PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
 +        PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
      else // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming
 -        PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
 +        PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
  }
  METHOD(Hagar, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
  {
      float loadable_secondary;
      loadable_secondary = (WEP_CVAR_SEC(hagar, load) && WEP_CVAR(hagar, secondary));
 +    if(weaponslot(weaponentity) == 0)
 +      actor.hagar_load = actor.(weaponentity).hagar_load;
  
      if(loadable_secondary)
          W_Hagar_Attack2_Load(thiswep, actor, weaponentity); // must always run each frame
 -    if(autocvar_g_balance_hagar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) { // forced reload
 +    if(autocvar_g_balance_hagar_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) { // forced reload
          thiswep.wr_reload(thiswep, actor, weaponentity);
      }
 -    else if((fire & 1) && !actor.hagar_load && !actor.hagar_loadblock) // not while secondary is loaded or awaiting reset
 +    else if((fire & 1) && !actor.(weaponentity).hagar_load && !actor.(weaponentity).hagar_loadblock) // not while secondary is loaded or awaiting reset
        {
                if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
                        W_Hagar_Attack_Auto(thiswep, actor, weaponentity, fire);
  METHOD(Hagar, wr_gonethink, void(entity thiswep, entity actor, .entity weaponentity))
  {
      // we lost the weapon and want to prepare switching away
 -    if(actor.hagar_load)
 +    if(actor.(weaponentity).hagar_load)
      {
        actor.(weaponentity).state = WS_READY;
        W_Hagar_Attack2_Load_Release(actor, weaponentity);
      }
  }
 -METHOD(Hagar, wr_setup, void(entity thiswep, entity actor))
 +METHOD(Hagar, wr_setup, void(entity thiswep, entity actor, .entity weaponentity))
  {
 -    actor.hagar_loadblock = false;
 -
 -    if(actor.hagar_load)
 +      actor.hagar_load = 0;
 +    actor.(weaponentity).hagar_loadblock = false;
 +    if(actor.(weaponentity).hagar_load)
      {
 -        W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo) * actor.hagar_load * -1); // give back ammo if necessary
 -        actor.hagar_load = 0;
 +      W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo) * actor.(weaponentity).hagar_load * -1, weaponentity); // give back ammo if necessary
 +      actor.(weaponentity).hagar_load = 0;
      }
  }
 -METHOD(Hagar, wr_checkammo1, bool(entity thiswep, entity actor))
 +METHOD(Hagar, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
  {
      float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(hagar, ammo);
 -    ammo_amount += actor.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_PRI(hagar, ammo);
 +    ammo_amount += actor.(weaponentity).(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_PRI(hagar, ammo);
      return ammo_amount;
  }
 -METHOD(Hagar, wr_checkammo2, bool(entity thiswep, entity actor))
 +METHOD(Hagar, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
  {
      float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(hagar, ammo);
 -    ammo_amount += actor.(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
 +    ammo_amount += actor.(weaponentity).(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
      return ammo_amount;
  }
  METHOD(Hagar, wr_resetplayer, void(entity thiswep, entity actor))
  {
      actor.hagar_load = 0;
 +    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +    {
 +      .entity weaponentity = weaponentities[slot];
 +      actor.(weaponentity).hagar_load = 0;
 +    }
  }
  METHOD(Hagar, wr_playerdeath, void(entity thiswep, entity actor, .entity weaponentity))
  {
      // if we have any rockets loaded when we die, release them
 -    if(actor.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath))
 +    if(actor.(weaponentity).hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath))
        W_Hagar_Attack2_Load_Release(actor, weaponentity);
  }
  METHOD(Hagar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
  {
 -    if(!actor.hagar_load) // require releasing loaded rockets first
 +    if(!actor.(weaponentity).hagar_load) // require releasing loaded rockets first
          W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND_RELOAD);
  }
  METHOD(Hagar, wr_suicidemessage, Notification(entity thiswep))
index d68de4a353cc8d24c0a95508e6a14bc6fa80676c,1f01ef0413c237e262752c59bd5daee816131968..786c96016911ad25e1164d9c12643ec74cbcdce7
@@@ -47,6 -47,7 +47,6 @@@ REGISTER_WEAPON(TUBA, tuba, NEW(Tuba))
  
  .entity tuba_note;
  .float tuba_smoketime;
 -.float tuba_instrument;
  
  #define MAX_TUBANOTES 32
  .float tuba_lastnotes_last;
  
  spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(this, WEP_TUBA); }
  
 -bool W_Tuba_HasPlayed(entity pl, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo)
 +bool W_Tuba_HasPlayed(entity pl, .entity weaponentity, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo)
  {
        float i, j, mmin, mmax, nolength;
        float n = tokenize_console(melody);
 -      if(n > pl.tuba_lastnotes_cnt)
 +      if(n > pl.(weaponentity).tuba_lastnotes_cnt)
                return false;
        float pitchshift = 0;
  
        if(instrument >= 0)
 -              if(pl.tuba_instrument != instrument)
 +              if(pl.(weaponentity).tuba_instrument != instrument)
                        return false;
  
        // verify notes...
        nolength = false;
        for(i = 0; i < n; ++i)
        {
 -              vector v = pl.(tuba_lastnotes[(pl.tuba_lastnotes_last - i + MAX_TUBANOTES) % MAX_TUBANOTES]);
 +              vector v = pl.(weaponentity).(tuba_lastnotes[(pl.(weaponentity).tuba_lastnotes_last - i + MAX_TUBANOTES) % MAX_TUBANOTES]);
                float ai = stof(argv(n - i - 1));
                float np = floor(ai);
                if(ai == np)
  
                for(i = 0; i < n; ++i)
                {
 -                      vector vi = pl.(tuba_lastnotes[(pl.tuba_lastnotes_last - i + MAX_TUBANOTES) % MAX_TUBANOTES]);
 +                      vector vi = pl.(weaponentity).(tuba_lastnotes[(pl.(weaponentity).tuba_lastnotes_last - i + MAX_TUBANOTES) % MAX_TUBANOTES]);
                        float ai = stof(argv(n - i - 1));
                        ti -= 1 / (ai - floor(ai));
                        float tj = ti;
                        for(j = i+1; j < n; ++j)
                        {
 -                              vector vj = pl.(tuba_lastnotes[(pl.tuba_lastnotes_last - j + MAX_TUBANOTES) % MAX_TUBANOTES]);
 +                              vector vj = pl.(weaponentity).(tuba_lastnotes[(pl.(weaponentity).tuba_lastnotes_last - j + MAX_TUBANOTES) % MAX_TUBANOTES]);
                                float aj = stof(argv(n - j - 1));
                                tj -= (aj - floor(aj));
  
                        return false;
        }
  
 -      pl.tuba_lastnotes_cnt = 0;
 +      pl.(weaponentity).tuba_lastnotes_cnt = 0;
  
        return true;
  }
@@@ -153,13 -154,12 +153,13 @@@ void W_Tuba_NoteOff(entity this
        //   on: this.spawnshieldtime
        //   off: time
        //   note: this.cnt
 -      if (actor.tuba_note == this)
 +      .entity weaponentity = this.weaponentity_fld;
 +      if (actor.(weaponentity).tuba_note == this)
        {
 -              actor.tuba_lastnotes_last = (actor.tuba_lastnotes_last + 1) % MAX_TUBANOTES;
 -              actor.(tuba_lastnotes[actor.tuba_lastnotes_last]) = eX * this.spawnshieldtime + eY * time + eZ * this.cnt;
 -              actor.tuba_note = NULL;
 -              actor.tuba_lastnotes_cnt = bound(0, actor.tuba_lastnotes_cnt + 1, MAX_TUBANOTES);
 +              actor.(weaponentity).tuba_lastnotes_last = (actor.(weaponentity).tuba_lastnotes_last + 1) % MAX_TUBANOTES;
 +              actor.(weaponentity).(tuba_lastnotes[actor.(weaponentity).tuba_lastnotes_last]) = eX * this.spawnshieldtime + eY * time + eZ * this.cnt;
 +              actor.(weaponentity).tuba_note = NULL;
 +              actor.(weaponentity).tuba_lastnotes_cnt = bound(0, actor.(weaponentity).tuba_lastnotes_cnt + 1, MAX_TUBANOTES);
  
                string s = trigger_magicear_processmessage_forallears(actor, 0, NULL, string_null);
                if (s != "")
@@@ -320,49 -320,47 +320,49 @@@ void W_Tuba_NoteOn(entity actor, .entit
        n = W_Tuba_GetNote(actor, hittype);
  
        hittype = 0;
 -      if(actor.tuba_instrument & 1)
 +      if(actor.(weaponentity).tuba_instrument & 1)
                hittype |= HITTYPE_SECONDARY;
 -      if(actor.tuba_instrument & 2)
 +      if(actor.(weaponentity).tuba_instrument & 2)
                hittype |= HITTYPE_BOUNCE;
  
 -      if(actor.tuba_note)
 +      if(actor.(weaponentity).tuba_note)
        {
 -              if(actor.tuba_note.cnt != n || actor.tuba_note.tuba_instrument != actor.tuba_instrument)
 +              if(actor.(weaponentity).tuba_note.cnt != n || actor.(weaponentity).tuba_note.tuba_instrument != actor.(weaponentity).tuba_instrument)
                {
 -                      W_Tuba_NoteOff(actor.tuba_note);
 +                      W_Tuba_NoteOff(actor.(weaponentity).tuba_note);
                }
        }
  
 -      if(!actor.tuba_note)
 +      if(!actor.(weaponentity).tuba_note)
        {
 -              actor.tuba_note = new(tuba_note);
 -              actor.tuba_note.owner = actor.tuba_note.realowner = actor;
 -              actor.tuba_note.cnt = n;
 -              actor.tuba_note.tuba_instrument = actor.tuba_instrument;
 -              setthink(actor.tuba_note, W_Tuba_NoteThink);
 -              actor.tuba_note.nextthink = time;
 -              actor.tuba_note.spawnshieldtime = time;
 -              Net_LinkEntity(actor.tuba_note, false, 0, W_Tuba_NoteSendEntity);
 +              entity note = new(tuba_note);
 +              note.weaponentity_fld = weaponentity;
 +              actor.(weaponentity).tuba_note = note;
 +              note.owner = note.realowner = actor;
 +              note.cnt = n;
 +              note.tuba_instrument = actor.(weaponentity).tuba_instrument;
 +              setthink(note, W_Tuba_NoteThink);
 +              note.nextthink = time;
 +              note.spawnshieldtime = time;
 +              Net_LinkEntity(note, false, 0, W_Tuba_NoteSendEntity);
        }
  
 -      actor.tuba_note.teleport_time = time + WEP_CVAR(tuba, refire) * 2 * W_WeaponRateFactor(actor); // so it can get prolonged safely
 +      actor.(weaponentity).tuba_note.teleport_time = time + WEP_CVAR(tuba, refire) * 2 * W_WeaponRateFactor(actor); // so it can get prolonged safely
  
        //sound(actor, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation);
        RadiusDamage(actor, actor, WEP_CVAR(tuba, damage), WEP_CVAR(tuba, edgedamage), WEP_CVAR(tuba, radius), NULL, NULL, WEP_CVAR(tuba, force), hittype | WEP_TUBA.m_id, NULL);
  
        o = gettaginfo(actor.exteriorweaponentity, 0);
 -      if(time > actor.tuba_smoketime)
 +      if(time > actor.(weaponentity).tuba_smoketime)
        {
                Send_Effect(EFFECT_SMOKE_RING, o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
 -              actor.tuba_smoketime = time + 0.25;
 +              actor.(weaponentity).tuba_smoketime = time + 0.25;
        }
  }
  #endif
  
  #ifdef SVQC
 -METHOD(Tuba, wr_aim, void(Tuba this, entity actor))
 +METHOD(Tuba, wr_aim, void(Tuba this, entity actor, .entity weaponentity))
  {
        // bots cannot play the Tuba well yet
        // I think they should start with the recorder first
@@@ -389,40 -387,73 +389,40 @@@ METHOD(Tuba, wr_think, void(Tuba this, 
                W_Tuba_NoteOn(actor, weaponentity, HITTYPE_SECONDARY);
                weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
        }
 -      if (actor.tuba_note)
 +      if (actor.(weaponentity).tuba_note)
        {
                if (!(fire & 1) && !(fire & 2))
                {
 -                      W_Tuba_NoteOff(actor.tuba_note);
 +                      W_Tuba_NoteOff(actor.(weaponentity).tuba_note);
                }
        }
  }
  
 -void tuba_instrument_send(entity this, int instr);
 -METHOD(Tuba, wr_setup, void(Tuba this, entity actor))
 +METHOD(Tuba, wr_setup, void(Tuba this, entity actor, .entity weaponentity))
  {
 -      actor.ammo_field = ammo_none;
 -      actor.tuba_instrument = 0;
 -      tuba_instrument_send(actor, actor.tuba_instrument);
 +      actor.(weaponentity).tuba_instrument = 0;
  }
 -#endif
  
 -REGISTER_NET_S2C(tuba_instrument)
 -#ifdef CSQC
 -NET_HANDLE(tuba_instrument, bool)
 -{
 -      int i = ReadByte();
 -      return = true;
 -      string s = (i == 0) ? "tuba" :
 -                 (i == 1) ? "akordeon" :
 -                            "kleinbottle" ;
 -    viewmodel.tuba_instrument = i;
 -      CL_WeaponEntity_SetModel(viewmodel, s, true);
 -}
 -#endif
 -#ifdef SVQC
 -void tuba_instrument_send(entity this, int instr)
 -{
 -      msg_entity = this;
 -      if (!IS_REAL_CLIENT(this))
 -              return;
 -      int chan = MSG_ONE;
 -      WriteHeader(chan, tuba_instrument);
 -      WriteByte(chan, instr);
 -}
 -SPECTATE_COPY()
 -{
 -      if (this.tuba_instrument != spectatee.tuba_instrument)
 -              tuba_instrument_send(this, this.tuba_instrument = spectatee.tuba_instrument);
 -}
  METHOD(Tuba, wr_reload, void(Tuba this, entity actor, .entity weaponentity))
  {
        // switch to alternate instruments :)
        if (actor.(weaponentity).state == WS_READY)
        {
 -              switch (actor.tuba_instrument)
 +              switch (actor.(weaponentity).tuba_instrument)
                {
                        case 0:
 -                              actor.tuba_instrument = 1;
 -                              actor.weaponname = "akordeon";
 +                              actor.(weaponentity).tuba_instrument = 1;
 +                              actor.(weaponentity).weaponname = "akordeon";
                                break;
                        case 1:
 -                              actor.tuba_instrument = 2;
 -                              actor.weaponname = "kleinbottle";
 +                              actor.(weaponentity).tuba_instrument = 2;
 +                              actor.(weaponentity).weaponname = "kleinbottle";
                                break;
                        case 2:
 -                              actor.tuba_instrument = 0;
 -                              actor.weaponname = "tuba";
 +                              actor.(weaponentity).tuba_instrument = 0;
 +                              actor.(weaponentity).weaponname = "tuba";
                                break;
                }
 -              tuba_instrument_send(actor, actor.tuba_instrument);
                W_SetupShot(actor, weaponentity, false, 0, SND_Null, 0, 0);
                Send_Effect(EFFECT_TELEPORT, w_shotorg, '0 0 0', 1);
                actor.(weaponentity).state = WS_INUSE;
  #ifdef SVQC
  
  // infinite ammo
 -METHOD(Tuba, wr_checkammo1, bool(Tuba this, entity actor)) { return true; }
 -METHOD(Tuba, wr_checkammo2, bool(Tuba this, entity actor)) { return true; }
 +METHOD(Tuba, wr_checkammo1, bool(Tuba this, entity actor, .entity weaponentity)) { return true; }
 +METHOD(Tuba, wr_checkammo2, bool(Tuba this, entity actor, .entity weaponentity)) { return true; }
  
  METHOD(Tuba, wr_suicidemessage, Notification(Tuba this))
  {
@@@ -466,12 -497,6 +466,6 @@@ const int TUBA_MIN = -18
  const int TUBA_MAX = 27;
  const int TUBA_INSTRUMENTS = 3;
  
- entityclass(Tuba);
- class(Tuba) .int note;
- class(Tuba) .bool tuba_attenuate;
- class(Tuba) .float tuba_volume;
- class(Tuba) .float tuba_volume_initial;
  int Tuba_PitchStep;
  
  void tubasound(entity e, bool restart)
index 806ffc9c4a91dbadff158d6ca40d274114c39be7,56c36c0745a759e4236f3893ce08e327c12ee50f..0613ab403d9f73e7c9a2ba03aa42580668d740d0
@@@ -9,10 -9,10 +9,11 @@@
  #include "../waypoints.qh"
  
  #include <common/constants.qh>
+ #include <common/net_linked.qh>
  #include <common/physics/player.qh>
  #include <common/state.qh>
  #include <common/items/_mod.qh>
 +#include <common/wepent.qh>
  
  #include <common/triggers/teleporters.qh>
  #include <common/triggers/trigger/jumppads.qh>
@@@ -85,16 -85,10 +86,16 @@@ void havocbot_ai(entity this
                return;
  
        havocbot_chooseenemy(this);
 -      if (this.bot_chooseweapontime < time )
 +
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
 -              this.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
 -              havocbot_chooseweapon(this);
 +              .entity weaponentity = weaponentities[slot];
 +              if(this.(weaponentity).m_weapon != WEP_Null || slot == 0)
 +              if(this.(weaponentity).bot_chooseweapontime < time)
 +              {
 +                      this.(weaponentity).bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval;
 +                      havocbot_chooseweapon(this, weaponentity);
 +              }
        }
        havocbot_aim(this);
        lag_update(this);
  
                if(this.weapons)
                {
 -                      Weapon w = PS(this).m_weapon;
 -                      w.wr_aim(w, this);
                        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this))
                        {
                                PHYS_INPUT_BUTTON_ATCK(this) = false;
                        }
                        else
                        {
 -                              if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))
 -                                      this.lastfiredweapon = PS(this).m_weapon.m_id;
 +                              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +                              {
 +                                      .entity weaponentity = weaponentities[slot];
 +                                      Weapon w = this.(weaponentity).m_weapon;
 +                                      if(w == WEP_Null && slot != 0)
 +                                              continue;
 +                                      w.wr_aim(w, this, weaponentity);
 +                                      if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) // TODO: what if we didn't fire this weapon, but the previous?
 +                                              this.(weaponentity).lastfiredweapon = this.(weaponentity).m_weapon.m_id;
 +                              }
                        }
                }
                else
        // if the bot is not attacking, consider reloading weapons
        if (!(this.aistatus & AI_STATUS_ATTACKING))
        {
 -              // we are currently holding a weapon that's not fully loaded, reload it
 -              if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
 -              if(this.clip_load < this.clip_size)
 -                      this.impulse = 20; // "press" the reload button, not sure if this is done right
 -
 -              // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
 -              // the code above executes next frame, starting the reloading then
 -              if(skill >= 5) // bots can only look for unloaded weapons past this skill
 -              if(this.clip_load >= 0) // only if we're not reloading a weapon already
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
 -                      FOREACH(Weapons, it != WEP_Null, LAMBDA(
 -                              if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.weapon_load[it.m_id] < it.reloading_ammo))
 -                                      PS(this).m_switchweapon = it;
 -                      ));
 +                      .entity weaponentity = weaponentities[slot];
 +
 +                      if(this.(weaponentity).m_weapon == WEP_Null && slot != 0)
 +                              continue;
 +
 +                      // we are currently holding a weapon that's not fully loaded, reload it
 +                      if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
 +                      if(this.(weaponentity).clip_load < this.(weaponentity).clip_size)
 +                              this.impulse = 20; // "press" the reload button, not sure if this is done right
 +
 +                      // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
 +                      // the code above executes next frame, starting the reloading then
 +                      if(skill >= 5) // bots can only look for unloaded weapons past this skill
 +                      if(this.(weaponentity).clip_load >= 0) // only if we're not reloading a weapon already
 +                      {
 +                              FOREACH(Weapons, it != WEP_Null, LAMBDA(
 +                                      if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.(weaponentity).weapon_load[it.m_id] < it.reloading_ammo))
 +                                              this.(weaponentity).m_switchweapon = it;
 +                              ));
 +                      }
                }
        }
  }
@@@ -605,35 -585,25 +606,35 @@@ void havocbot_movetogoal(entity this
                else if(this.health>WEP_CVAR(devastator, damage)*0.5)
                {
                        if(this.velocity.z < 0)
 -                      if(client_hasweapon(this, WEP_DEVASTATOR, true, false))
                        {
 -                              this.movement_x = maxspeed;
 -
 -                              if(this.rocketjumptime)
 +                              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                                {
 -                                      if(time > this.rocketjumptime)
 +                                      .entity weaponentity = weaponentities[slot];
 +
 +                                      if(this.(weaponentity).m_weapon == WEP_Null && slot != 0)
 +                                              continue;
 +
 +                                      if(client_hasweapon(this, WEP_DEVASTATOR, weaponentity, true, false))
                                        {
 -                                              PHYS_INPUT_BUTTON_ATCK2(this) = true;
 -                                              this.rocketjumptime = 0;
 +                                              this.movement_x = maxspeed;
 +
 +                                              if(this.rocketjumptime)
 +                                              {
 +                                                      if(time > this.rocketjumptime)
 +                                                      {
 +                                                              PHYS_INPUT_BUTTON_ATCK2(this) = true;
 +                                                              this.rocketjumptime = 0;
 +                                                      }
 +                                                      return;
 +                                              }
 +
 +                                              this.(weaponentity).m_switchweapon = WEP_DEVASTATOR;
 +                                              this.v_angle_x = 90;
 +                                              PHYS_INPUT_BUTTON_ATCK(this) = true;
 +                                              this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
 +                                              return;
                                        }
 -                                      return;
                                }
 -
 -                              PS(this).m_switchweapon = WEP_DEVASTATOR;
 -                              this.v_angle_x = 90;
 -                              PHYS_INPUT_BUTTON_ATCK(this) = true;
 -                              this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
 -                              return;
                        }
                }
                else
@@@ -1013,7 -983,7 +1014,7 @@@ LABEL(scan_targets
                this.havocbot_stickenemy = false;
  }
  
 -float havocbot_chooseweapon_checkreload(entity this, int new_weapon)
 +float havocbot_chooseweapon_checkreload(entity this, .entity weaponentity, int new_weapon)
  {
        // bots under this skill cannot find unloaded weapons to reload idly when not in combat,
        // so skip this for them, or they'll never get to reload their weapons at all.
                return false;
  
        // if this weapon is scheduled for reloading, don't switch to it during combat
 -      if (this.weapon_load[new_weapon] < 0)
 +      if (this.(weaponentity).weapon_load[new_weapon] < 0)
        {
                bool other_weapon_available = false;
                FOREACH(Weapons, it != WEP_Null, LAMBDA(
 -                      if(it.wr_checkammo1(it, this) + it.wr_checkammo2(it, this))
 +                      if(it.wr_checkammo1(it, this, weaponentity) + it.wr_checkammo2(it, this, weaponentity))
                                other_weapon_available = true;
                ));
                if(other_weapon_available)
        return false;
  }
  
 -void havocbot_chooseweapon(entity this)
 +void havocbot_chooseweapon(entity this, .entity weaponentity)
  {
        int i;
  
        // ;)
        if(g_weaponarena_weapons == WEPSET(TUBA))
        {
 -              PS(this).m_switchweapon = WEP_TUBA;
 +              this.(weaponentity).m_switchweapon = WEP_TUBA;
                return;
        }
  
        if(this.enemy==NULL)
        {
                // If no weapon was chosen get the first available weapon
 -              if(PS(this).m_weapon==WEP_Null)
 +              if(this.(weaponentity).m_weapon==WEP_Null)
                FOREACH(Weapons, it != WEP_Null, LAMBDA(
 -                      if(client_hasweapon(this, it, true, false))
 +                      if(client_hasweapon(this, it, weaponentity, true, false))
                        {
 -                              PS(this).m_switchweapon = it;
 +                              this.(weaponentity).m_switchweapon = it;
                                return;
                        }
                ));
        combo = false;
  
        if(autocvar_bot_ai_weapon_combo)
 -      if(PS(this).m_weapon.m_id == this.lastfiredweapon)
 +      if(this.(weaponentity).m_weapon.m_id == this.(weaponentity).lastfiredweapon)
        if(af > combo_time)
        {
                combo = true;
                if ( distance > bot_distance_far ) {
                        for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){
                                w = bot_weapons_far[i];
 -                              if ( client_hasweapon(this, Weapons_from(w), true, false) )
 +                              if ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
                                {
 -                                      if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
 +                                      if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
                                                continue;
 -                                      PS(this).m_switchweapon = Weapons_from(w);
 +                                      this.(weaponentity).m_switchweapon = Weapons_from(w);
                                        return;
                                }
                        }
                if ( distance > bot_distance_close) {
                        for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){
                                w = bot_weapons_mid[i];
 -                              if ( client_hasweapon(this, Weapons_from(w), true, false) )
 +                              if ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
                                {
 -                                      if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
 +                                      if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
                                                continue;
 -                                      PS(this).m_switchweapon = Weapons_from(w);
 +                                      this.(weaponentity).m_switchweapon = Weapons_from(w);
                                        return;
                                }
                        }
                // Choose weapons for close distance
                for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){
                        w = bot_weapons_close[i];
 -                      if ( client_hasweapon(this, Weapons_from(w), true, false) )
 +                      if ( client_hasweapon(this, Weapons_from(w), weaponentity, true, false) )
                        {
 -                              if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w))
 +                              if ((this.(weaponentity).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, weaponentity, w))
                                        continue;
 -                              PS(this).m_switchweapon = Weapons_from(w);
 +                              this.(weaponentity).m_switchweapon = Weapons_from(w);
                                return;
                        }
                }
diff --combined qcsrc/server/client.qc
index 2a1340408652430f9d734179b03cc6bfde26a734,da66ece3bb813fa6776d5c9ad183311ba63218af..6ce1cd62f9234f5fcc91aed550c75863215b66b3
@@@ -23,7 -23,6 +23,7 @@@
  #include "bot/api.qh"
  
  #include "../common/ent_cs.qh"
 +#include "../common/wepent.qh"
  #include <common/state.qh>
  
  #include <common/effects/qc/globalsound.qh>
@@@ -37,6 -36,7 +37,7 @@@
  #include "weapons/weaponsystem.qh"
  
  #include "../common/net_notice.qh"
+ #include "../common/net_linked.qh"
  #include "../common/physics/player.qh"
  
  #include "../common/items/_mod.qh"
@@@ -112,6 -112,7 +113,6 @@@ bool ClientData_Send(entity this, entit
        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 (e.porto_v_angle_held)   sf |= 8; // angles held
        if (autocvar_sv_showspectators) sf |= 16; // show spectators
  
        WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
        {
                WriteByte(MSG_ENTITY, to.spectatee_status);
        }
 -      if (sf & 8)
 -      {
 -              WriteAngle(MSG_ENTITY, e.v_angle.x);
 -              WriteAngle(MSG_ENTITY, e.v_angle.y);
 -      }
  
        if(sf & 16)
        {
@@@ -251,7 -257,7 +252,7 @@@ void PutObserverInServer(entity this
          this.view_ofs = '0 0 0';
      }
  
 -    RemoveGrapplingHook(this);
 +    RemoveGrapplingHooks(this);
        Portal_ClearAll(this);
        Unfreeze(this);
        SetSpectatee(this, NULL);
        this.istypefrag = 0;
        setthink(this, func_null);
        this.nextthink = 0;
 -      this.hook_time = 0;
        this.deadflag = DEAD_NO;
        this.crouch = false;
        this.revival_time = 0;
        this.weapons = '0 0 0';
        this.drawonlytoclient = this;
  
 -      this.weaponname = "";
        this.weaponmodel = "";
        for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
 +              if(!this.weaponentities[slot])
 +                      continue; // first load
 +              this.weaponentities[slot].hook_time = 0;
 +              this.weaponentities[slot].weaponname = "";
                this.weaponentities[slot] = NULL;
        }
        this.exteriorweaponentity = NULL;
        this.oldvelocity = this.velocity;
        this.fire_endtime = -1;
        this.event_damage = func_null;
 -
 -      STAT(ACTIVEWEAPON, this) = WEP_Null.m_id;
 -      STAT(SWITCHINGWEAPON, this) = WEP_Null.m_id;
 -      STAT(SWITCHWEAPON, this) = WEP_Null.m_id;
  }
  
  int player_getspecies(entity this)
@@@ -654,8 -662,7 +655,8 @@@ void PutClientInServer(entity this
  
                for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
 -                      CL_SpawnWeaponentity(this, weaponentities[slot]);
 +                      .entity weaponentity = weaponentities[slot];
 +                      CL_SpawnWeaponentity(this, weaponentity);
                }
                this.alpha = default_player_alpha;
                this.colormod = '1 1 1' * autocvar_g_player_brightness;
                        it.wr_resetplayer(it, this);
                        // reload all reloadable weapons
                        if (it.spawnflags & WEP_FLAG_RELOADABLE) {
 -                              this.weapon_load[it.m_id] = it.reloading_ammo;
 +                              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +                              {
 +                                      .entity weaponentity = weaponentities[slot];
 +                                      this.(weaponentity).weapon_load[it.m_id] = it.reloading_ammo;
 +                              }
                        }
                ));
  
                        delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
                }
  
 -              PS(this).m_switchweapon = w_getbestweapon(this);
 -              this.cnt = -1; // W_LastWeapon will not complain
 -              PS(this).m_weapon = WEP_Null;
 -              this.weaponname = "";
 -              PS(this).m_switchingweapon = WEP_Null;
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +              {
 +                      .entity weaponentity = weaponentities[slot];
 +                      if(slot == 0)
 +                              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 (!warmup_stage && !this.alivetime)
                        this.alivetime = time;
@@@ -1260,7 -1256,7 +1261,7 @@@ void ClientDisconnect(entity this
  
        Unfreeze(this);
  
 -      RemoveGrapplingHook(this);
 +      RemoveGrapplingHooks(this);
  
        // Here, everything has been done that requires this player to be a client.
  
        this.playerid = 0;
        ReadyCount();
        if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
+       ONREMOVE(this);
  }
  
  void ChatBubbleThink(entity this)
@@@ -1689,12 -1687,6 +1692,12 @@@ void SpectateCopy(entity this, entity s
        setsize(this, spectatee.mins, spectatee.maxs);
        SetZoomState(this, spectatee.zoomstate);
  
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +      {
 +              .entity weaponentity = weaponentities[slot];
 +              this.(weaponentity) = spectatee.(weaponentity);
 +      }
 +
      anticheat_spectatecopy(this, spectatee);
        this.hud = spectatee.hud;
        if(spectatee.vehicle)
@@@ -2443,17 -2435,13 +2446,17 @@@ void PlayerPreThink (entity this
                {
                        this.items &= ~this.items_added;
  
 -                      //for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 -                      //{
 -                              //.entity weaponentity = weaponentities[slot];
 -                              //W_WeaponFrame(this, weaponentity);
 -                      //}
 -                      .entity weaponentity = weaponentities[0]; // TODO
 -                      W_WeaponFrame(this, weaponentity);
 +                      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))
  
                // WEAPONTODO: Add a weapon request for this
                // rot vortex charge to the charge limit
 -              if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time)
 -                      this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
 +              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);
  
  
        // WEAPONTODO: Add weapon request for this
        if (!zoomstate_set) {
 -              SetZoomState(this,
 -                      PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this)
 -                      || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX)
 -                      || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)
 -              );
 +              bool wep_zoomed = false;
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +              {
 +                      .entity weaponentity = weaponentities[slot];
 +                      Weapon thiswep = this.(weaponentity).m_weapon;
 +                      if(thiswep != WEP_Null && thiswep.wr_zoom)
 +                              wep_zoomed += thiswep.wr_zoom(thiswep, this);
 +              }
 +              SetZoomState(this, PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) || wep_zoomed);
      }
  
        if (this.teamkill_soundtime && time > this.teamkill_soundtime)
  
        // WEAPONTODO: Move into weaponsystem somehow
        // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
 -      if (PS(this).m_weapon == WEP_Null)
 -              this.clip_load = this.clip_size = 0;
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +      {
 +              .entity weaponentity = weaponentities[slot];
 +              if(this.(weaponentity).m_weapon == WEP_Null)
 +                      this.(weaponentity).clip_load = this.(weaponentity).clip_size = 0;
 +      }
  }
  
  void DrownPlayer(entity this)
diff --combined qcsrc/server/g_hook.qc
index e50a066618f91784c0e74aa0fb0acc0649382e7c,fd725ad35e8b128a3e78521e0d46913e64f6134e..36e1e4e52ba2458cab620ac962d1b9af6fc634ae
@@@ -13,6 -13,7 +13,7 @@@
  #include "../common/vehicles/all.qh"
  #include "../common/constants.qh"
  #include "../common/util.qh"
+ #include <common/net_linked.qh>
  #include <common/weapons/_all.qh>
  #include "../lib/warpzone/common.qh"
  #include "../lib/warpzone/server.qh"
@@@ -70,41 -71,24 +71,41 @@@ And you should be done
  
  .float hook_length;
  
 -void RemoveGrapplingHook(entity pl)
 +void RemoveGrapplingHooks(entity pl)
  {
 -      if(pl.hook == NULL)
 -              return;
 -      delete(pl.hook);
 -      pl.hook = NULL;
        if(pl.move_movetype == MOVETYPE_FLY)
                set_movetype(pl, MOVETYPE_WALK);
  
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +    {
 +      .entity weaponentity = weaponentities[slot];
 +      if(pl.(weaponentity).hook)
 +      {
 +              delete(pl.(weaponentity).hook);
 +              pl.(weaponentity).hook = NULL;
 +      }
 +    }
 +
        //pl.disableclientprediction = false;
  }
  
 +void RemoveHook(entity this)
 +{
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +    {
 +      .entity weaponentity = weaponentities[slot];
 +      if(this.realowner.(weaponentity).hook == this)
 +              this.realowner.(weaponentity).hook = NULL;
 +    }
 +
 +    if(this.realowner.move_movetype == MOVETYPE_FLY)
 +      set_movetype(this.realowner, MOVETYPE_WALK);
 +    delete(this);
 +}
 +
  void GrapplingHookReset(entity this)
  {
 -      if(this.realowner.hook == this)
 -              RemoveGrapplingHook(this.owner);
 -      else // in any case:
 -              delete(this);
 +      RemoveHook(this);
  }
  
  void GrapplingHookThink(entity this);
@@@ -133,7 -117,6 +134,7 @@@ bool GrapplingHookSend(entity this, ent
        if(sf & 1)
        {
                WriteByte(MSG_ENTITY, etof(this.realowner));
 +              WriteByte(MSG_ENTITY, weaponslot(this.weaponentity_fld));
        }
        if(sf & 2)
        {
@@@ -156,15 -139,14 +157,15 @@@ void GrapplingHookThink(entity this
  {
        float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch;
        vector dir, org, end, v0, dv, v, myorg, vs;
 -      if(this.realowner.hook != this) // how did that happen?
 +      .entity weaponentity = this.weaponentity_fld;
 +      if(this.realowner.(weaponentity).hook != this)  // how did that happen?
        {
                error("Owner lost the hook!\n");
                return;
        }
        if(LostMovetypeFollow(this) || intermission_running || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
        {
 -              RemoveGrapplingHook(this.realowner);
 +              RemoveHook(this);
                return;
        }
        if(this.aiment)
  
        this.nextthink = time;
  
 -      int s = W_GetGunAlignment(this.realowner);
 +      int s = W_GunAlign(this.realowner.(weaponentity), STAT(GUNALIGN, this.realowner)) - 1;
        vs = hook_shotorigin[s];
  
        makevectors(this.realowner.v_angle);
                        v = v0 = WarpZone_RefSys_TransformVelocity(pull_entity, this, pull_entity.velocity);
  
                        // first pull the rope...
 -                      if(this.realowner.hook_state & HOOK_PULLING)
 +                      if(this.realowner.(weaponentity).hook_state & HOOK_PULLING)
                        {
                                newlength = this.hook_length;
                                newlength = max(newlength - pullspeed * frametime, minlength);
                        if(pull_entity.move_movetype == MOVETYPE_FLY)
                                set_movetype(pull_entity, MOVETYPE_WALK);
  
 -                      if(this.realowner.hook_state & HOOK_RELEASING)
 +                      if(this.realowner.(weaponentity).hook_state & HOOK_RELEASING)
                        {
                                newlength = dist;
                                this.hook_length = newlength;
  
                        if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !STAT(FROZEN, this.aiment))
                        {
 -                              RemoveGrapplingHook(this.realowner);
 +                              RemoveHook(this);
                                return;
                        }
                }
@@@ -356,33 -338,36 +357,33 @@@ void GrapplingHook_Damage(entity this, 
                        this.realowner.pushltime = time + autocvar_g_maxpushtime;
                        this.realowner.istypefrag = PHYS_INPUT_BUTTON_CHAT(this.realowner);
                }
 -              RemoveGrapplingHook(this.realowner);
 +              RemoveHook(this);
        }
  }
  
 -void FireGrapplingHook(entity actor)
 +void FireGrapplingHook(entity actor, .entity weaponentity)
  {
 -      entity missile;
 -      vector org;
 -      vector vs;
 -
        if(forbidWeaponUse(actor)) return;
        if(actor.vehicle) return;
  
        makevectors(actor.v_angle);
  
 -      int s = W_GetGunAlignment(actor);
 -      vs = hook_shotorigin[s];
 +      int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1;
 +      vector vs = hook_shotorigin[s];
  
        // UGLY WORKAROUND: play this on CH_WEAPON_B so it can't cut off fire sounds
        sound (actor, CH_WEAPON_B, SND_HOOK_FIRE, VOL_BASE, ATTEN_NORM);
 -      org = actor.origin + actor.view_ofs + v_forward * vs.x + v_right * -vs.y + v_up * vs.z;
 +      vector org = actor.origin + actor.view_ofs + v_forward * vs.x + v_right * -vs.y + v_up * vs.z;
  
        tracebox(actor.origin + actor.view_ofs, '-3 -3 -3', '3 3 3', org, MOVE_NORMAL, actor);
        org = trace_endpos;
  
        Send_Effect(EFFECT_HOOK_MUZZLEFLASH, org, '0 0 0', 1);
  
 -      missile = WarpZone_RefSys_SpawnSameRefSys(actor);
 +      entity missile = WarpZone_RefSys_SpawnSameRefSys(actor);
        missile.owner = missile.realowner = actor;
 -      actor.hook = missile;
 +      actor.(weaponentity).hook = missile;
 +      missile.weaponentity_fld = weaponentity;
        missile.reset = GrapplingHookReset;
        missile.classname = "grapplinghook";
        missile.flags = FL_PROJECTILE;
diff --combined qcsrc/server/g_world.qc
index 8e8dce9b12080abd58a7ff9ac91fd9bd2de9f317,8edcf3b8f58f7b0010efffabd1279ab4c6d9ab9f..4a9656d26ad40853dc511609309f79dca8fcbbcc
@@@ -19,6 -19,7 +19,7 @@@
  #include "teamplay.qh"
  #include "weapons/weaponstats.qh"
  #include "../common/constants.qh"
+ #include <common/net_linked.qh>
  #include "../common/deathtypes/all.qh"
  #include "../common/mapinfo.qh"
  #include "../common/monsters/_mod.qh"
@@@ -2091,6 -2092,10 +2092,6 @@@ void EndFrame(
        {
                antilag_record(it, it, altime);
        });
 -      FOREACH_CLIENT(PS(it), {
 -              PlayerState s = PS(it);
 -              s.ps_push(s, it);
 -      });
        systems_update();
        IL_ENDFRAME();
  }
index 35f0f227e77ee4c18859d3c10ef55c658aa78d21,e90dbeb9669a81e00cc94cb6651094e015081c4d..220ebc3af1c800006a7c34efd9641b12e5f8b758
@@@ -11,6 -11,7 +11,7 @@@
  #include "weapons/selection.qh"
  #include "../common/command/_mod.qh"
  #include "../common/constants.qh"
+ #include <common/net_linked.qh>
  #include "../common/deathtypes/all.qh"
  #include "../common/mapinfo.qh"
  #include "../common/notifications/all.qh"
@@@ -25,7 -26,6 +26,7 @@@
  #include "../common/items/_mod.qh"
  #include "../common/state.qh"
  #include "../common/effects/qc/globalsound.qh"
 +#include "../common/wepent.qh"
  #include "../lib/csqcmodel/sv_model.qh"
  #include "../lib/warpzone/anglestransform.qh"
  #include "../lib/warpzone/server.qh"
@@@ -260,8 -260,6 +261,8 @@@ string formatmessage(entity this, strin
                replacement = substring(msg, p, 2);
                escape = substring(msg, p + 1, 1);
  
 +              .entity weaponentity = weaponentities[0]; // TODO: unhardcode
 +
                switch(escape)
                {
                        case "%": replacement = "%"; break;
                        case "l": replacement = NearestLocation(this.origin); break;
                        case "y": replacement = NearestLocation(cursor); break;
                        case "d": replacement = NearestLocation(this.death_origin); break;
 -                      case "w": replacement = ((PS(this).m_weapon == WEP_Null) ? ((PS(this).m_switchweapon == WEP_Null) ? Weapons_from(this.cnt) : PS(this).m_switchweapon) : PS(this).m_weapon).m_name; break;
 +                      case "w": replacement = ((this.(weaponentity).m_weapon == WEP_Null) ? ((this.(weaponentity).m_switchweapon == WEP_Null) ? Weapons_from(this.(weaponentity).cnt) : this.(weaponentity).m_switchweapon) : this.(weaponentity).m_weapon).m_name; break;
                        case "W": replacement = ammoitems; break;
                        case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
                        case "s": replacement = ftos(vlen(this.velocity - this.velocity_z * '0 0 1')); break;
@@@ -450,14 -448,7 +451,14 @@@ void GetCvars(entity this, int f
        if (f > 0)
        {
                if (s == "cl_weaponpriority")
 -                      if (PS(this)) PS(this).m_switchweapon = w_getbestweapon(this);
 +              {
 +                      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +                      {
 +                              .entity weaponentity = weaponentities[slot];
 +                              if (this.(weaponentity) && (this.(weaponentity).m_weapon != WEP_Null || slot == 0))
 +                                      this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity);
 +                      }
 +              }
                if (s == "cl_allow_uidtracking")
                        PlayerStats_GameReport_AddPlayer(this);
        }
@@@ -1083,7 -1074,7 +1084,7 @@@ bool WarpZone_Projectile_Touch_ImpactFi
                if(this.classname == "nade")
                        return false; // no checks here
                else if(this.classname == "grapplinghook")
 -                      RemoveGrapplingHook(this.realowner);
 +                      RemoveHook(this);
                else if(this.classname == "spike")
                {
                        W_Crylink_Dequeue(this);
index b3075928cb4433a83e120214bd8e0f4d80e78b6f,539371a53cbe9c94b87e388deac95b290d1850cd..d7d8fc9c1fbd6b5a854751aed15452ec00585234
@@@ -3,12 -3,12 +3,13 @@@
  #include "weaponsystem.qh"
  #include <common/t_items.qh>
  #include <common/constants.qh>
+ #include <common/net_linked.qh>
  #include <common/util.qh>
  #include <common/items/item.qh>
  #include <common/weapons/_all.qh>
  #include <common/state.qh>
  #include <common/mutators/mutator/waypoints/waypointsprites.qh>
 +#include <common/wepent.qh>
  
  // switch between weapons
  void Send_WeaponComplain(entity e, float wpn, float type)
@@@ -39,7 -39,7 +40,7 @@@ void Weapon_whereis(Weapon this, entit
        });
  }
  
 -bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain)
 +bool client_hasweapon(entity this, Weapon wpn, .entity weaponentity, float andammo, bool complain)
  {
        float f = 0;
  
                        }
                        else
                        {
 -                              f = wpn.wr_checkammo1(wpn, this) + wpn.wr_checkammo2(wpn, this);
 +                              f = wpn.wr_checkammo1(wpn, this, weaponentity) + wpn.wr_checkammo2(wpn, this, weaponentity);
  
                                // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
                                if(wpn == WEP_MINE_LAYER)
 -                                      IL_EACH(g_mines, it.owner == this,
 +                                      IL_EACH(g_mines, it.owner == this && it.weaponentity_fld == weaponentity,
                                        {
                                                f = 1;
                                                break; // no need to continue
        return false;
  }
  
 -float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, float complain, float skipmissing)
 +float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, float complain, float skipmissing, .entity weaponentity)
  {
        // We cannot tokenize in this function, as GiveItems calls this
        // function. Thus we must use car/cdr.
        float weaponcur;
        entity wep;
  
 -      if(skipmissing || this.selectweapon == 0)
 -              weaponcur = PS(this).m_switchweapon.m_id;
 +      if(skipmissing || this.(weaponentity).selectweapon == 0)
 +              weaponcur = this.(weaponentity).m_switchweapon.m_id;
        else
 -              weaponcur = this.selectweapon;
 +              weaponcur = this.(weaponentity).selectweapon;
  
        if(dir == 0)
                switchtonext = 1;
  
                ++c;
  
 -              if(!skipmissing || client_hasweapon(this, wep, true, false))
 +              if(!skipmissing || client_hasweapon(this, wep, weaponentity, true, false))
                {
                        if(switchtonext)
                                return weaponwant;
                        --c;
                        if(c == 0)
                        {
 -                              client_hasweapon(this, wep, true, true);
 +                              client_hasweapon(this, wep, weaponentity, true, true);
                                break;
                        }
                }
        return 0;
  }
  
 -void W_SwitchWeapon_Force(Player this, Weapon wep)
 +void W_SwitchWeapon_Force(Player this, Weapon wep, .entity weaponentity)
  {
 -    TC(Player, this); TC(Weapon, wep);
 -      this.cnt = PS(this).m_switchweapon.m_id;
 -      PS(this).m_switchweapon = wep;
 -      this.selectweapon = wep.m_id;
 +    TC(Weapon, wep);
 +      this.(weaponentity).cnt = this.(weaponentity).m_switchweapon.m_id;
 +      this.(weaponentity).m_switchweapon = wep;
 +      this.(weaponentity).selectweapon = wep.m_id;
  }
  
  // perform weapon to attack (weaponstate and attack_finished check is here)
 -void W_SwitchToOtherWeapon(entity this)
 +void W_SwitchToOtherWeapon(entity this, .entity weaponentity)
  {
        // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway)
        Weapon ww;
 -      WepSet set = WepSet_FromWeapon(PS(this).m_weapon);
 +      WepSet set = WepSet_FromWeapon(this.(weaponentity).m_weapon);
        if (this.weapons & set)
        {
                this.weapons &= ~set;
 -              ww = w_getbestweapon(this);
 +              ww = w_getbestweapon(this, weaponentity);
                this.weapons |= set;
        }
        else
        {
 -              ww = w_getbestweapon(this);
 +              ww = w_getbestweapon(this, weaponentity);
        }
        if (ww == WEP_Null) return;
 -      W_SwitchWeapon_Force(this, ww);
 +      W_SwitchWeapon_Force(this, ww, weaponentity);
  }
  
 -void W_SwitchWeapon(entity this, Weapon w)
 +void W_SwitchWeapon(entity this, Weapon w, .entity weaponentity)
  {
 -      if (PS(this).m_switchweapon != w)
 +      if(this.(weaponentity).m_switchweapon != w)
        {
 -              if (client_hasweapon(this, w, true, true))
 -                      W_SwitchWeapon_Force(this, w);
 +              if(client_hasweapon(this, w, weaponentity, true, true))
 +                      W_SwitchWeapon_Force(this, w, weaponentity);
                else
 -                      this.selectweapon = w.m_id; // update selectweapon ANYWAY
 +                      this.(weaponentity).selectweapon = w.m_id; // update selectweapon anyway
        }
 -      else if(!forbidWeaponUse(this)) {
 +      else if(!forbidWeaponUse(this))
 +      {
                entity actor = this;
 -              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 -              {
 -                      .entity weaponentity = weaponentities[slot];
 -                      w.wr_reload(w, actor, weaponentity);
 -              }
 +              w.wr_reload(w, actor, weaponentity);
        }
  }
  
 -void W_CycleWeapon(entity this, string weaponorder, float dir)
 +void W_CycleWeapon(entity this, string weaponorder, float dir, .entity weaponentity)
  {
        float w;
 -      w = W_GetCycleWeapon(this, weaponorder, dir, -1, 1, true);
 +      w = W_GetCycleWeapon(this, weaponorder, dir, -1, 1, true, weaponentity);
        if(w > 0)
 -              W_SwitchWeapon(this, Weapons_from(w));
 +              W_SwitchWeapon(this, Weapons_from(w), weaponentity);
  }
  
 -void W_NextWeaponOnImpulse(entity this, float imp)
 +void W_NextWeaponOnImpulse(entity this, float imp, .entity weaponentity)
  {
        float w;
 -      w = W_GetCycleWeapon(this, this.cvar_cl_weaponpriority, +1, imp, 1, (this.cvar_cl_weaponimpulsemode == 0));
 +      w = W_GetCycleWeapon(this, this.cvar_cl_weaponpriority, +1, imp, 1, (this.cvar_cl_weaponimpulsemode == 0), weaponentity);
        if(w > 0)
 -              W_SwitchWeapon(this, Weapons_from(w));
 +              W_SwitchWeapon(this, Weapons_from(w), weaponentity);
  }
  
  // next weapon
 -void W_NextWeapon(entity this, int list)
 +void W_NextWeapon(entity this, int list, .entity weaponentity)
  {
        if(list == 0)
 -              W_CycleWeapon(this, weaponorder_byid, -1);
 +              W_CycleWeapon(this, weaponorder_byid, -1, weaponentity);
        else if(list == 1)
 -              W_CycleWeapon(this, this.weaponorder_byimpulse, -1);
 +              W_CycleWeapon(this, this.weaponorder_byimpulse, -1, weaponentity);
        else if(list == 2)
 -              W_CycleWeapon(this, this.cvar_cl_weaponpriority, -1);
 +              W_CycleWeapon(this, this.cvar_cl_weaponpriority, -1, weaponentity);
  }
  
  // prev weapon
 -void W_PreviousWeapon(entity this, float list)
 +void W_PreviousWeapon(entity this, float list, .entity weaponentity)
  {
        if(list == 0)
 -              W_CycleWeapon(this, weaponorder_byid, +1);
 +              W_CycleWeapon(this, weaponorder_byid, +1, weaponentity);
        else if(list == 1)
 -              W_CycleWeapon(this, this.weaponorder_byimpulse, +1);
 +              W_CycleWeapon(this, this.weaponorder_byimpulse, +1, weaponentity);
        else if(list == 2)
 -              W_CycleWeapon(this, this.cvar_cl_weaponpriority, +1);
 +              W_CycleWeapon(this, this.cvar_cl_weaponpriority, +1, weaponentity);
  }
  
  // previously used if exists and has ammo, (second) best otherwise
 -void W_LastWeapon(entity this)
 +void W_LastWeapon(entity this, .entity weaponentity)
  {
 -      Weapon wep = Weapons_from(this.cnt);
 -      if (client_hasweapon(this, wep, true, false))
 -              W_SwitchWeapon(this, wep);
 +      Weapon wep = Weapons_from(this.(weaponentity).cnt);
 +      if (client_hasweapon(this, wep, weaponentity, true, false))
 +              W_SwitchWeapon(this, wep, weaponentity);
        else
 -              W_SwitchToOtherWeapon(this);
 +              W_SwitchToOtherWeapon(this, weaponentity);
  }
index 8e6d2eabf1e2aca1ba608bcc198cce4be5d1b712,b49ed88f596ca00a7ef4e6b3467b7e5a3f36c2e2..1417717e834e33270ed355d891a450bb73767938
@@@ -10,6 -10,7 +10,7 @@@
  #include "../antilag.qh"
  
  #include <common/constants.qh>
+ #include <common/net_linked.qh>
  #include <common/util.qh>
  
  #include <common/weapons/_all.qh>
@@@ -27,7 -28,7 +28,7 @@@ void W_SetupShot_Dir_ProjectileSize_Ran
        float oldsolid;
        vector vecs, dv;
        oldsolid = ent.dphitcontentsmask;
 -      if (IS_PLAYER(ent) && PS(ent).m_weapon == WEP_RIFLE)
 +      if (IS_PLAYER(ent) && ent.(weaponentity).m_weapon == WEP_RIFLE)
                ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
        else
                ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
  
        // track max damage
        if (IS_PLAYER(ent) && accuracy_canbegooddamage(ent))
 -              accuracy_add(ent, PS(ent).m_weapon.m_id, maxdamage, 0);
 +              accuracy_add(ent, ent.(weaponentity).m_weapon.m_id, maxdamage, 0);
  
        if(IS_PLAYER(ent))
 -              W_HitPlotAnalysis(ent, v_forward, v_right, v_up);
 +              W_HitPlotAnalysis(ent, weaponentity, v_forward, v_right, v_up);
  
        vector md = ent.(weaponentity).movedir;
        if(md.x > 0)
@@@ -196,7 -197,7 +197,7 @@@ void W_SetupProjVelocity_Explicit(entit
  //  Ballistics Tracing
  // ====================
  
 -void FireRailgunBullet (entity this, vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, int deathtype)
 +void FireRailgunBullet (entity this, .entity weaponentity, vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, int deathtype)
  {
        vector hitloc, force, endpoint, dir;
        entity ent, endent;
        }
  
        // calculate hits and fired shots for hitscan
 -      accuracy_add(this, PS(this).m_weapon.m_id, 0, min(bdamage, totaldmg));
 +      accuracy_add(this, this.(weaponentity).m_weapon.m_id, 0, min(bdamage, totaldmg));
  
        trace_endpos = endpoint;
        trace_ent = endent;
@@@ -348,7 -349,7 +349,7 @@@ void fireBullet_trace_callback(vector s
        fireBullet_last_hit = NULL;
  }
  
 -void fireBullet(entity this, vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, int tracereffects)
 +void fireBullet(entity this, .entity weaponentity, vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, int tracereffects)
  {
        vector  end;
  
                                // do not exceed 100%
                                float added_damage = min(damage - total_damage, damage * solid_penetration_left);
                                total_damage += damage * solid_penetration_left;
 -                              accuracy_add(this, PS(this).m_weapon.m_id, 0, added_damage);
 +                              accuracy_add(this, this.(weaponentity).m_weapon.m_id, 0, added_damage);
                        }
                }
  
index 0d26aaa403ca4c3d4b6a394846deb6b0a71c3944,c0d302e2232123e9e3c049eed1965f27d750cf7b..6139f2879cbf8e04718e18f7fc7848710c654532
@@@ -8,13 -8,13 +8,14 @@@
  #include <common/t_items.qh>
  #include <common/animdecide.qh>
  #include <common/constants.qh>
+ #include <common/net_linked.qh>
  #include <common/monsters/_mod.qh>
  #include <common/notifications/all.qh>
  #include <common/util.qh>
  #include <common/weapons/_all.qh>
  #include <common/state.qh>
  #include <lib/csqcmodel/sv_model.qh>
 +#include <common/wepent.qh>
  
  .int state;
  
@@@ -61,10 -61,8 +62,10 @@@ vector CL_Weapon_GetShotOrg(int wpn
        return ret;
  }
  
 -..entity weaponentity_fld;
  .float m_alpha;
 +.string w_weaponname;
 +.int w_dmg;
 +.int w_deadflag;
  
  void CL_Weaponentity_Think(entity this)
  {
                if (this.weaponchild) this.weaponchild.model = "";
                return;
        }
 -      if (this.weaponname != this.owner.weaponname
 -          || this.dmg != this.owner.modelindex
 -          || this.deadflag != this.owner.deadflag)
 +      if (this.w_weaponname != this.weaponname
 +          || this.w_dmg != this.modelindex
 +          || this.w_deadflag != this.deadflag)
        {
                // owner changed weapons; update appearance
 -              this.weaponname = this.owner.weaponname;
 -              this.dmg = this.owner.modelindex;
 -              this.deadflag = this.owner.deadflag;
 +              this.w_weaponname = this.weaponname;
 +              this.w_dmg = this.modelindex;
 +              this.w_deadflag = this.deadflag;
  
 -              CL_WeaponEntity_SetModel(this, this.owner.weaponname, true);
 +              CL_WeaponEntity_SetModel(this, this.weaponname, true);
        }
  
        this.alpha = -1;  // TODO: don't render this entity at all
  void CL_ExteriorWeaponentity_Think(entity this)
  {
        this.nextthink = time;
 +      .entity weaponentity = this.weaponentity_fld;
 +      entity w_ent = this.owner.(weaponentity);
        if (this.owner.exteriorweaponentity != this)
        {
                delete(this);
                this.model = "";
                return;
        }
 -      if (this.weaponname != this.owner.weaponname || this.dmg != this.owner.modelindex
 -          || this.deadflag != this.owner.deadflag)
 +      if (this.weaponname != w_ent.weaponname || this.dmg != w_ent.modelindex
 +          || this.deadflag != w_ent.deadflag)
        {
 -              this.weaponname = this.owner.weaponname;
 -              this.dmg = this.owner.modelindex;
 -              this.deadflag = this.owner.deadflag;
 -              if (this.owner.weaponname != "")
 +              this.weaponname = w_ent.weaponname;
 +              this.dmg = w_ent.modelindex;
 +              this.deadflag = w_ent.deadflag;
 +              if (w_ent.weaponname != "")
                {
 -                      _setmodel(this, W_Model(strcat("v_", this.owner.weaponname, ".md3")));
 +                      _setmodel(this, W_Model(strcat("v_", w_ent.weaponname, ".md3")));
                        setsize(this, '0 0 0', '0 0 0');
                }
                else this.model = "";
        else if (this.owner.alpha != 0) this.alpha = this.owner.alpha;
        else this.alpha = 1;
  
 -    Weapon wep = PS(this.owner).m_weapon;
 -      if (wep) this.glowmod = weaponentity_glowmod(wep, this.owner, this.owner.clientcolors);
 +    Weapon wep = this.owner.(weaponentity).m_weapon;
 +      if (wep) this.glowmod = weaponentity_glowmod(wep, this.owner, this.owner.clientcolors, this.owner.(weaponentity));
        this.colormap = this.owner.colormap;
  
        CSQCMODEL_AUTOUPDATE(this);
@@@ -177,14 -173,11 +178,14 @@@ void CL_SpawnWeaponentity(entity actor
        view.viewmodelforclient = actor;
        setcefc(view, CL_Weaponentity_CustomizeEntityForClient);
  
 -      if (weaponentity == weaponentities[0])
 +      wepent_link(view);
 +
 +      if (weaponentity == weaponentities[0]) // only one exterior model, thank you very much
        {
                entity exterior = actor.exteriorweaponentity = new(exteriorweaponentity);
                exterior.solid = SOLID_NOT;
                exterior.owner = actor;
 +              exterior.weaponentity_fld = weaponentity;
                setorigin(exterior, '0 0 0');
                setthink(exterior, CL_ExteriorWeaponentity_Think);
                exterior.nextthink = time;
  // Weapon subs
  void w_clear(Weapon thiswep, entity actor, .entity weaponentity, int fire)
  {
 -      PS(actor).m_weapon = WEP_Null;
 -      PS(actor).m_switchingweapon = WEP_Null;
 +      actor.(weaponentity).m_weapon = WEP_Null;
 +      actor.(weaponentity).m_switchingweapon = WEP_Null;
        entity this = actor.(weaponentity);
        if (this)
        {
@@@ -215,17 -208,17 +216,17 @@@ void w_ready(Weapon thiswep, entity act
  
  .float prevdryfire;
  .float prevwarntime;
 -bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, bool secondary)
 +bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, bool secondary, .entity weaponentity)
  {
        if ((actor.items & IT_UNLIMITED_WEAPON_AMMO)) return true;
        bool ammo = false;
 -      if (secondary) ammo = thiswep.wr_checkammo2(thiswep, actor);
 -      else ammo = thiswep.wr_checkammo1(thiswep, actor);
 +      if (secondary) ammo = thiswep.wr_checkammo2(thiswep, actor, weaponentity);
 +      else ammo = thiswep.wr_checkammo1(thiswep, actor, weaponentity);
        if (ammo) return true;
        // always keep the Mine Layer if we placed mines, so that we can detonate them
        if (thiswep == WEP_MINE_LAYER)
        {
 -              IL_EACH(g_mines, it.owner == actor,
 +              IL_EACH(g_mines, it.owner == actor && it.weaponentity_fld == weaponentity,
                {
                        return false;
                });
        if (thiswep == WEP_SHOTGUN)
                if (!secondary && WEP_CVAR(shotgun, secondary) == 1) return false;           // no clicking, just allow
  
 -      if (thiswep == PS(actor).m_switchweapon && time - actor.prevdryfire > 1) // only play once BEFORE starting to switch weapons
 +      if (thiswep == actor.(weaponentity).m_switchweapon && time - actor.prevdryfire > 1) // only play once BEFORE starting to switch weapons
        {
                sound(actor, CH_WEAPON_A, SND_DRYFIRE, VOL_BASE, ATTEN_NORM);
                actor.prevdryfire = time;
  
        // check if the other firing mode has enough ammo
        bool ammo_other = false;
 -      if (secondary) ammo_other = thiswep.wr_checkammo1(thiswep, actor);
 -      else ammo_other = thiswep.wr_checkammo2(thiswep, actor);
 +      if (secondary) ammo_other = thiswep.wr_checkammo1(thiswep, actor, weaponentity);
 +      else ammo_other = thiswep.wr_checkammo2(thiswep, actor, weaponentity);
        if (ammo_other)
        {
                if (time - actor.prevwarntime > 1)
        }
        else  // this weapon is totally unable to fire, switch to another one
        {
 -              W_SwitchToOtherWeapon(actor);
 +              W_SwitchToOtherWeapon(actor, weaponentity);
        }
  
        return false;
  bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime)
  {
        if (actor.weaponentity == NULL) return true;
 -      if (!weapon_prepareattack_checkammo(thiswep, actor, secondary)) return false;
 +      if (!weapon_prepareattack_checkammo(thiswep, actor, secondary, weaponentity)) return false;
  
        // if sv_ready_restart_after_countdown is set, don't allow the player to shoot
        // if all players readied up and the countdown is running
                return false;
  
        // do not even think about shooting if switching
 -      if (PS(actor).m_switchweapon != PS(actor).m_weapon) return false;
 +      if (actor.(weaponentity).m_switchweapon != actor.(weaponentity).m_weapon) return false;
  
        if (attacktime >= 0)
        {
                int slot = weaponslot(weaponentity);
                // don't fire if previous attack is not finished
 -              if (ATTACK_FINISHED(actor, slot) > time + actor.weapon_frametime * 0.5) return false;
 +              if (ATTACK_FINISHED(actor, slot) > time + actor.(weaponentity).weapon_frametime * 0.5) return false;
                entity this = actor.(weaponentity);
                // don't fire while changing weapon
                if (this.state != WS_READY) return false;
@@@ -308,14 -301,14 +309,14 @@@ void weapon_prepareattack_do(entity act
        if (attacktime >= 0)
        {
                int slot = weaponslot(weaponentity);
 -              if (ATTACK_FINISHED(actor, slot) < time - actor.weapon_frametime * 1.5)
 +              if (ATTACK_FINISHED(actor, slot) < time - this.weapon_frametime * 1.5)
                {
                        ATTACK_FINISHED(actor, slot) = time;
                        // dprint("resetting attack finished to ", ftos(time), "\n");
                }
                ATTACK_FINISHED(actor, slot) = ATTACK_FINISHED(actor, slot) + attacktime * W_WeaponRateFactor(actor);
        }
 -      actor.bulletcounter += 1;
 +      this.bulletcounter += 1;
        // dprint("attack finished ", ftos(ATTACK_FINISHED(actor, slot)), "\n");
  }
  
@@@ -379,13 -372,14 +380,13 @@@ void weapon_thinkf(entity actor, .entit
                this.weapon_nextthink = time;
                // dprint("started firing at ", ftos(time), "\n");
        }
 -      if (this.weapon_nextthink < time - actor.weapon_frametime * 1.5
 -          || this.weapon_nextthink > time + actor.weapon_frametime * 1.5)
 +      if (this.weapon_nextthink < time - this.weapon_frametime * 1.5
 +          || this.weapon_nextthink > time + this.weapon_frametime * 1.5)
        {
                this.weapon_nextthink = time;
                // dprint("reset weapon animation timer at ", ftos(time), "\n");
        }
        this.weapon_nextthink += t;
 -      if (weaponentity == weaponentities[0]) STAT(WEAPON_NEXTTHINK, actor) = this.weapon_nextthink;
        this.weapon_think = func;
        // dprint("next ", ftos(this.weapon_nextthink), "\n");
  
  
        if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
        {
 -              bool primary_melee = boolean(fr == WFRAME_FIRE1 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI));
 -              bool secondary_melee = boolean(fr == WFRAME_FIRE2 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC));
 +              bool primary_melee = boolean(fr == WFRAME_FIRE1 && (this.m_weapon.spawnflags & WEP_TYPE_MELEE_PRI));
 +              bool secondary_melee = boolean(fr == WFRAME_FIRE2 && (this.m_weapon.spawnflags & WEP_TYPE_MELEE_SEC));
                int act = (primary_melee || secondary_melee)
                        ? ANIMACTION_MELEE
                        : ANIMACTION_SHOOT
@@@ -432,27 -426,27 +433,27 @@@ void W_WeaponFrame(Player actor, .entit
      TC(Player, actor);
      TC(PlayerState, PS(actor));
        entity this = actor.(weaponentity);
 -      if (frametime) actor.weapon_frametime = frametime;
 +      if (frametime) this.weapon_frametime = frametime;
  
        if (!this || actor.health < 1) return;  // Dead player can't use weapons and injure impulse commands
  
  
        if (forbidWeaponUse(actor))
        {
 -              if (actor.(weaponentity).state != WS_CLEAR)
 +              if (this.state != WS_CLEAR)
                {
 -                      Weapon wpn = PS(actor).m_weapon;
 +                      Weapon wpn = this.m_weapon;
                        w_ready(wpn, actor, weaponentity, PHYS_INPUT_BUTTON_ATCK(actor) | (PHYS_INPUT_BUTTON_ATCK2(actor) << 1));
                        return;
                }
        }
  
 -      if (PS(actor).m_switchweapon == WEP_Null)
 +      if (this.m_switchweapon == WEP_Null)
        {
 -              PS(actor).m_weapon = WEP_Null;
 -              PS(actor).m_switchingweapon = WEP_Null;
 +              this.m_weapon = WEP_Null;
 +              this.m_switchingweapon = WEP_Null;
                this.state = WS_CLEAR;
 -              actor.weaponname = "";
 +              this.weaponname = "";
                // actor.items &= ~IT_AMMO;
                return;
        }
        vector up = v_up;
  
        // Change weapon
 -      if (PS(actor).m_weapon != PS(actor).m_switchweapon)
 +      if (this.m_weapon != this.m_switchweapon)
        {
                switch (this.state)
                {
                        case WS_CLEAR:
                        {
                                // end switching!
 -                              Weapon newwep = PS(actor).m_switchweapon;
 -                              PS(actor).m_switchingweapon = newwep;
 +                              Weapon newwep = this.m_switchweapon;
 +                              this.m_switchingweapon = newwep;
  
                                // the two weapon entities will notice this has changed and update their models
 -                              PS(actor).m_weapon = newwep;
 -                              actor.weaponname = newwep.mdl;
 -                              actor.bulletcounter = 0;
 -                              actor.ammo_field = newwep.ammo_field;
 -                              newwep.wr_setup(newwep, actor);
 +                              this.m_weapon = newwep;
 +                              this.weaponname = newwep.mdl;
 +                              this.bulletcounter = 0;
 +                              newwep.wr_setup(newwep, actor, weaponentity);
                                this.state = WS_RAISE;
  
                                // set our clip load to the load of the weapon we switched to, if it's reloadable
                                if ((newwep.spawnflags & WEP_FLAG_RELOADABLE) && newwep.reloading_ammo)  // prevent accessing undefined cvars
                                {
 -                                      actor.clip_load = actor.(weapon_load[PS(actor).m_switchweapon.m_id]);
 -                                      actor.clip_size = newwep.reloading_ammo;
 +                                      this.clip_load = this.(weapon_load[this.m_switchweapon.m_id]);
 +                                      this.clip_size = newwep.reloading_ammo;
                                }
                                else
                                {
 -                                      actor.clip_load = actor.clip_size = 0;
 +                                      this.clip_load = this.clip_size = 0;
                                }
  
                                weapon_thinkf(actor, weaponentity, WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
                        case WS_DROP:
                        {
                                // in dropping phase we can switch at any time
 -                              PS(actor).m_switchingweapon = PS(actor).m_switchweapon;
 +                              this.m_switchingweapon = this.m_switchweapon;
                                break;
                        }
                        case WS_READY:
                        {
                                // start switching!
 -                              PS(actor).m_switchingweapon = PS(actor).m_switchweapon;
 -                              entity oldwep = PS(actor).m_weapon;
 +                              this.m_switchingweapon = this.m_switchweapon;
 +                              entity oldwep = this.m_weapon;
  
                                // set up weapon switch think in the future, and start drop anim
 -                              if (INDEPENDENT_ATTACK_FINISHED || ATTACK_FINISHED(actor, weaponslot(weaponentity)) <= time + actor.weapon_frametime * 0.5)
 +                              if (INDEPENDENT_ATTACK_FINISHED || ATTACK_FINISHED(actor, weaponslot(weaponentity)) <= time + this.weapon_frametime * 0.5)
                                {
                                        sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM);
                                        this.state = WS_DROP;
        // if (actor.button0)
        //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor, slot)), " >= ", ftos(this.weapon_nextthink), "\n");
  
 -      Weapon w = PS(actor).m_weapon;
 +      Weapon w = this.m_weapon;
  
        // call the think code which may fire the weapon
        // and do so multiple times to resolve framerate dependency issues if the
        {
                if (w != WEP_Null && !(actor.weapons & WepSet_FromWeapon(w)))
                {
 -                      if (PS(actor).m_weapon == PS(actor).m_switchweapon) W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
 +                      if (this.m_weapon == this.m_switchweapon) W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
                        w = WEP_Null;
                }
  
                        }
                        else
                        {
 -                              if (key_pressed && PS(actor).m_switchweapon != WEP_HOOK && !actor.hook_switchweapon)
 -                                      W_SwitchWeapon(actor, WEP_HOOK);
 +                              if (key_pressed && this.m_switchweapon != WEP_HOOK && !actor.hook_switchweapon)
 +                                      W_SwitchWeapon(actor, WEP_HOOK, weaponentity);
                                actor.hook_switchweapon = key_pressed;
                                Weapon h = WEP_HOOK;
 -                              block_weapon = (PS(actor).m_weapon == h && (PHYS_INPUT_BUTTON_ATCK(actor) || key_pressed));
 +                              block_weapon = (this.m_weapon == h && (PHYS_INPUT_BUTTON_ATCK(actor) || key_pressed));
                                h.wr_think(h, actor, weaponentity, block_weapon ? 1 : 0);
                        }
                }
  
                if (!block_weapon)
                {
 -            Weapon e = PS(actor).m_weapon;
 +            Weapon e = this.m_weapon;
              TC(Weapon, e);
                        if (w != WEP_Null)
                        {
                        }
                }
  
 -              if (time + actor.weapon_frametime * 0.5 >= this.weapon_nextthink)
 +              if (time + this.weapon_frametime * 0.5 >= this.weapon_nextthink)
                {
                        if (this.weapon_think)
                        {
                                v_forward = fo;
                                v_right = ri;
                                v_up = up;
 -                              Weapon wpn = PS(actor).m_weapon;
 +                              Weapon wpn = this.m_weapon;
                                this.weapon_think(wpn, actor, weaponentity,
                                        PHYS_INPUT_BUTTON_ATCK(actor) | (PHYS_INPUT_BUTTON_ATCK2(actor) << 1));
                        }
@@@ -631,17 -626,17 +632,17 @@@ void W_AttachToShotorg(entity actor, .e
        }
  }
  
 -void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use)
 +void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use, .entity weaponentity)
  {
 -      if (MUTATOR_CALLHOOK(W_DecreaseAmmo, actor)) return;
 +      if (MUTATOR_CALLHOOK(W_DecreaseAmmo, actor, actor.(weaponentity))) return;
  
        if ((actor.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo) return;
  
        // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
        if (wep.reloading_ammo)
        {
 -              actor.clip_load -= ammo_use;
 -              actor.(weapon_load[PS(actor).m_weapon.m_id]) = actor.clip_load;
 +              actor.(weaponentity).clip_load -= ammo_use;
 +              actor.(weaponentity).(weapon_load[actor.(weaponentity).m_weapon.m_id]) = actor.(weaponentity).clip_load;
        }
        else if (wep.ammo_field != ammo_none)
        {
@@@ -671,30 -666,29 +672,30 @@@ void W_ReloadedAndReady(Weapon thiswep
  {
        // finish the reloading process, and do the ammo transfer
  
 -      actor.clip_load = actor.old_clip_load;  // restore the ammo counter, in case we still had ammo in the weapon before reloading
 +      Weapon wpn = actor.(weaponentity).m_weapon;
 +
 +      actor.(weaponentity).clip_load = actor.(weaponentity).old_clip_load;  // restore the ammo counter, in case we still had ammo in the weapon before reloading
  
        // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
 -      if (!actor.reload_ammo_min || actor.items & IT_UNLIMITED_WEAPON_AMMO || actor.ammo_field == ammo_none)
 +      if (!actor.(weaponentity).reload_ammo_min || (actor.items & IT_UNLIMITED_WEAPON_AMMO) || wpn.ammo_field == ammo_none)
        {
 -              actor.clip_load = actor.reload_ammo_amount;
 +              actor.(weaponentity).clip_load = actor.(weaponentity).reload_ammo_amount;
        }
        else
        {
                // make sure we don't add more ammo than we have
 -              float load = min(actor.reload_ammo_amount - actor.clip_load, actor.(actor.ammo_field));
 -              actor.clip_load += load;
 -              actor.(actor.ammo_field) -= load;
 +              float load = min(actor.(weaponentity).reload_ammo_amount - actor.(weaponentity).clip_load, actor.(wpn.ammo_field));
 +              actor.(weaponentity).clip_load += load;
 +              actor.(wpn.ammo_field) -= load;
        }
 -      actor.(weapon_load[PS(actor).m_weapon.m_id]) = actor.clip_load;
 +      actor.(weaponentity).(weapon_load[actor.(weaponentity).m_weapon.m_id]) = actor.(weaponentity).clip_load;
  
        // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
        // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
        // so your weapon is disabled for a few seconds without reason
  
 -      // ATTACK_FINISHED(actor, slot) -= actor.reload_time - 1;
 +      // ATTACK_FINISHED(actor, slot) -= actor.(weaponentity).reload_time - 1;
  
 -      Weapon wpn = Weapons_from(PS(actor).m_weapon.m_id);
        w_ready(wpn, actor, weaponentity, PHYS_INPUT_BUTTON_ATCK(actor) | (PHYS_INPUT_BUTTON_ATCK2(actor) << 1));
  }
  
@@@ -702,13 -696,13 +703,13 @@@ void W_Reload(entity actor, .entity wea
  {
      TC(Sound, sent_sound);
        // set global values to work with
 -      Weapon e = PS(actor).m_weapon;
 +      Weapon e = actor.(weaponentity).m_weapon;
  
        if (MUTATOR_CALLHOOK(W_Reload, actor)) return;
  
 -      actor.reload_ammo_min = sent_ammo_min;
 -      actor.reload_ammo_amount = e.reloading_ammo;
 -      actor.reload_time = e.reloading_time;
 +      actor.(weaponentity).reload_ammo_min = sent_ammo_min;
 +      actor.(weaponentity).reload_ammo_amount = e.reloading_ammo;
 +      actor.(weaponentity).reload_time = e.reloading_time;
        if (actor.reload_sound) strunzone(actor.reload_sound);
        actor.reload_sound = strzone(Sound_fixpath(sent_sound));
  
        }
  
        // return if reloading is disabled for this weapon
 -      if (!actor.reload_ammo_amount) return;
 +      if (!actor.(weaponentity).reload_ammo_amount) return;
  
        // our weapon is fully loaded, no need to reload
 -      if (actor.clip_load >= actor.reload_ammo_amount) return;
 +      if (actor.(weaponentity).clip_load >= actor.(weaponentity).reload_ammo_amount) return;
  
        // no ammo, so nothing to load
 -      if (actor.ammo_field != ammo_none)
 +      if (e.ammo_field != ammo_none)
        {
 -              if (!actor.(actor.ammo_field) && actor.reload_ammo_min)
 +              if (!actor.(e.ammo_field) && actor.(weaponentity).reload_ammo_min)
                {
                        if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
                        {
                                if (IS_REAL_CLIENT(actor) && actor.reload_complain < time)
                                {
                                        play2(actor, SND(UNAVAILABLE));
 -                                      sprint(actor, strcat("You don't have enough ammo to reload the ^2", PS(actor).m_weapon.m_name, "\n"));
 +                                      sprint(actor, strcat("You don't have enough ammo to reload the ^2", actor.(weaponentity).m_weapon.m_name, "\n"));
                                        actor.reload_complain = time + 1;
                                }
                                // switch away if the amount of ammo is not enough to keep using this weapon
 -                              Weapon w = PS(actor).m_weapon;
 -                              if (!(w.wr_checkammo1(w, actor) + w.wr_checkammo2(w, actor)))
 +                              if (!(e.wr_checkammo1(e, actor, weaponentity) + e.wr_checkammo2(e, actor, weaponentity)))
                                {
 -                                      actor.clip_load = -1;  // reload later
 -                                      W_SwitchToOtherWeapon(actor);
 +                                      actor.(weaponentity).clip_load = -1;  // reload later
 +                                      W_SwitchToOtherWeapon(actor, weaponentity);
                                }
                                return;
                        }
        // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
        // so your weapon is disabled for a few seconds without reason
  
 -      // ATTACK_FINISHED(actor, slot) = max(time, ATTACK_FINISHED(actor, slot)) + actor.reload_time + 1;
 +      // ATTACK_FINISHED(actor, slot) = max(time, ATTACK_FINISHED(actor, slot)) + actor.(weaponentity).reload_time + 1;
  
 -      weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, actor.reload_time, W_ReloadedAndReady);
 +      weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, this.reload_time, W_ReloadedAndReady);
  
 -      if (actor.clip_load < 0) actor.clip_load = 0;
 -      actor.old_clip_load = actor.clip_load;
 -      actor.clip_load = actor.(weapon_load[PS(actor).m_weapon.m_id]) = -1;
 +      if (this.clip_load < 0) this.clip_load = 0;
 +      this.old_clip_load = this.clip_load;
 +      this.clip_load = this.(weapon_load[this.m_weapon.m_id]) = -1;
  }
  
 -void W_DropEvent(.void(Weapon, entity actor) event, entity player, float weapon_type, entity weapon_item)
 +void W_DropEvent(.void(Weapon, entity actor, .entity) event, entity player, float weapon_type, entity weapon_item, .entity weaponentity)
  {
        Weapon w = Weapons_from(weapon_type);
        weapon_dropevent_item = weapon_item;
 -      w.event(w, player);
 +      w.event(w, player, weaponentity);
  }