Merge branch 'master' into Mario/wepent_experimental
authorMario <mario@smbclan.net>
Wed, 12 Oct 2016 22:53:23 +0000 (08:53 +1000)
committerMario <mario@smbclan.net>
Wed, 12 Oct 2016 22:53:23 +0000 (08:53 +1000)
# Conflicts:
# qcsrc/common/mutators/mutator/overkill/sv_overkill.qc

1  2 
mutators.cfg
qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
qcsrc/common/physics/player.qc
qcsrc/common/stats.qh
qcsrc/server/client.qc
qcsrc/server/g_world.qc
qcsrc/server/mutators/events.qh

diff --combined mutators.cfg
index 18ed08cec71ab2dd9018df48d944eaf63c8b95cb,8fd4d2baf66b3a9e74db9b6f28374bcb2c18a5e8..4b8f1a2376ce25874e880eebba7340762a64f93c
@@@ -31,6 -31,10 +31,10 @@@ set g_instagib 0 "enable instagib
  set g_instagib_extralives 1 "how many extra lives you will get per powerup"
  set g_instagib_ammo_start 10 "starting ammo"
  set g_instagib_ammo_drop 5 "how much ammo you'll get for weapons or cells"
+ set g_instagib_ammo_convert_bullets 0 "convert bullet ammo packs to insta cell ammo packs"
+ set g_instagib_ammo_convert_cells 0 "convert normal cell ammo packs to insta cell ammo packs"
+ set g_instagib_ammo_convert_rockets 0 "convert rocket ammo packs to insta cell ammo packs"
+ set g_instagib_ammo_convert_shells 0 "convert shell ammo packs to insta cell ammo packs"
  set g_instagib_invis_alpha 0.15
  set g_instagib_speed_highspeed 1.5 "speed-multiplier that applies while you carry the invincibility powerup"
  set g_instagib_damagedbycontents 1 "allow damage from lava pits in instagib"
@@@ -45,11 -49,28 +49,12 @@@ set g_instagib_friendlypush 1 "allow pu
  // ==========
  set g_overkill 0 "enable overkill"
  
- set g_overkill_100a_anyway 1
- set g_overkill_100h_anyway 1
  set g_overkill_powerups_replace 1
- set g_overkill_superguns_respawn_time 120
+ set g_overkill_filter_healthmega 0
+ set g_overkill_filter_armormedium 0
+ set g_overkill_filter_armorbig 0
+ set g_overkill_filter_armorlarge 0
  
 -set g_overkill_ammo_charge 0
 -set g_overkill_ammo_charge_notice 1
 -set g_overkill_ammo_charge_limit 1
 -set g_overkill_ammo_charge_rate 0.5
 -set g_overkill_ammo_charge_rate_vortex 0.5
 -set g_overkill_ammo_charge_rate_machinegun 0.5
 -set g_overkill_ammo_charge_rate_shotgun 0.5
 -set g_overkill_ammo_charge_rate_hmg 0.25
 -set g_overkill_ammo_charge_rate_rpc 1.5
 -set g_overkill_ammo_decharge 0.1
 -set g_overkill_ammo_decharge_machinegun 0.025
 -set g_overkill_ammo_decharge_shotgun 0.15
 -set g_overkill_ammo_decharge_vortex 0.2
 -set g_overkill_ammo_decharge_rpc 1
 -set g_overkill_ammo_decharge_hmg 0.01
 -
  
  // =========
  //  vampire
index cbc49be16513fa5d1cebbb2d3ed1724368f05910,d47da6c3c38ed43c3949eed8042f94941d8beef9..67512a0a82a4c221a0b5bba2ea2e9750c5011c95
@@@ -4,15 -4,25 +4,19 @@@
  #include "rpc.qh"
  
  bool autocvar_g_overkill_powerups_replace;
- float autocvar_g_overkill_superguns_respawn_time;
- bool autocvar_g_overkill_100h_anyway;
- bool autocvar_g_overkill_100a_anyway;
+ bool autocvar_g_overkill_ammo_charge;
+ float autocvar_g_overkill_ammo_charge_notice;
+ float autocvar_g_overkill_ammo_charge_limit;
+ bool autocvar_g_overkill_filter_healthmega;
+ bool autocvar_g_overkill_filter_armormedium;
+ bool autocvar_g_overkill_filter_armorbig;
+ bool autocvar_g_overkill_filter_armorlarge;
  
- .vector ok_deathloc;
- .float ok_spawnsys_timer;
- .Weapon ok_lastwep[MAX_WEAPONSLOTS];
 -.float ok_lastwep;
  .float ok_item;
  
 -.float ok_notice_time;
 -.float ammo_charge[Weapons_MAX];
 -.float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE);
 -.float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL);
 -
 -void(entity ent, float wep) ok_DecreaseCharge;
++.Weapon ok_lastwep[MAX_WEAPONSLOTS];
  void ok_Initialize();
  
  REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
        }
  }
  
 -MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo)
 -{
 -      entity actor = M_ARGV(0, entity);
 -      if (actor.ok_use_ammocharge)
 -      {
 -              ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id);
 -              return true;
 -      }
 -}
 -
 -MUTATOR_HOOKFUNCTION(ok, W_Reload)
 -{
 -      entity actor = M_ARGV(0, entity);
 -      return actor.ok_use_ammocharge;
 -}
 -
  void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float);
  spawnfunc(weapon_hmg);
  spawnfunc(weapon_rpc);
  
 -void ok_DecreaseCharge(entity ent, int wep)
 -{
 -      if(!ent.ok_use_ammocharge) return;
 -
 -      entity wepent = Weapons_from(wep);
 -
 -      if (wepent == WEP_Null) return;  // dummy
 -
 -      ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
 -}
 -
 -void ok_IncreaseCharge(entity ent, int wep)
 -{
 -      entity wepent = Weapons_from(wep);
 -
 -      if (wepent == WEP_Null) return;  // dummy
 -
 -      if(ent.ok_use_ammocharge)
 -      if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking?
 -              ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME);
 -}
 -
 -float ok_CheckWeaponCharge(entity ent, int wep)
 -{
 -      if(!ent.ok_use_ammocharge) return true;
 -
 -      entity wepent = Weapons_from(wep);
 -
 -      if(wepent == WEP_Null) return false;  // dummy
 -
 -      return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
 -}
 -
  MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST)
  {
        entity frag_attacker = M_ARGV(1, entity);
@@@ -82,12 -141,7 +86,12 @@@ MUTATOR_HOOKFUNCTION(ok, PlayerDies
  
        ok_DropItem(frag_target, targ);
  
 -      frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id;
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +      {
 +              .entity weaponentity = weaponentities[slot];
 +
 +              frag_target.ok_lastwep[slot] = frag_target.(weaponentity).m_switchweapon;
 +      }
  }
  
  MUTATOR_HOOKFUNCTION(ok, MonsterDropItem)
@@@ -118,22 -172,18 +122,22 @@@ MUTATOR_HOOKFUNCTION(ok, PlayerPreThink
        if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player))
                return;
  
 -      if(player.ok_lastwep)
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
 -              Weapon newwep = Weapons_from(player.ok_lastwep);
 -              if(player.ok_lastwep == WEP_HMG.m_id)
 -                      newwep = WEP_MACHINEGUN;
 -              if(player.ok_lastwep == WEP_RPC.m_id)
 -                      newwep = WEP_VORTEX;
 -              PS(player).m_switchweapon = newwep;
 -              player.ok_lastwep = 0;
 -      }
 +              .entity weaponentity = weaponentities[slot];
 +              entity thiswep = player.(weaponentity);
  
 -      ok_IncreaseCharge(player, PS(player).m_weapon.m_id);
 +              if(player.ok_lastwep[slot] && player.ok_lastwep[slot] != WEP_Null)
 +              {
 +                      Weapon newwep = player.ok_lastwep[slot];
 +                      if(player.ok_lastwep[slot] == WEP_HMG)
 +                              newwep = WEP_MACHINEGUN;
 +                      if(player.ok_lastwep[slot] == WEP_RPC)
 +                              newwep = WEP_VORTEX;
 +                      thiswep.m_switchweapon = newwep;
 +                      player.ok_lastwep[slot] = WEP_Null;
 +              }
 +      }
  
        if(PHYS_INPUT_BUTTON_ATCK2(player))
        if( !forbidWeaponUse(player) || player.weapon_blocked // allow if weapon is blocked
                player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player);
                makevectors(player.v_angle);
  
 -              Weapon oldwep = PS(player).m_weapon;
 -              PS(player).m_weapon = WEP_BLASTER;
 -              W_Blaster_Attack(
 -                      player,
 -                      weaponentities[0], // TODO: unhardcode
 -                      WEP_BLASTER.m_id | HITTYPE_SECONDARY,
 -                      WEP_CVAR_SEC(vaporizer, shotangle),
 -                      WEP_CVAR_SEC(vaporizer, damage),
 -                      WEP_CVAR_SEC(vaporizer, edgedamage),
 -                      WEP_CVAR_SEC(vaporizer, radius),
 -                      WEP_CVAR_SEC(vaporizer, force),
 -                      WEP_CVAR_SEC(vaporizer, speed),
 -                      WEP_CVAR_SEC(vaporizer, spread),
 -                      WEP_CVAR_SEC(vaporizer, delay),
 -                      WEP_CVAR_SEC(vaporizer, lifetime)
 -              );
 -              PS(player).m_weapon = oldwep;
 -      }
 -
 -      player.weapon_blocked = false;
 -
 -      player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id];
 -
 -      if(player.ok_use_ammocharge)
 -      if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id))
 -      {
 -              if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon)
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
 -                      //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE);
 -                      player.ok_notice_time = time + 2;
 -                      play2(player, SND(DRYFIRE));
 +                      .entity weaponentity = weaponentities[slot];
 +
 +                      if(player.(weaponentity).m_weapon == WEP_Null && slot != 0)
 +                              continue;
 +
 +                      Weapon oldwep = player.(weaponentity).m_weapon;
 +                      player.(weaponentity).m_weapon = WEP_BLASTER;
 +                      W_Blaster_Attack(
 +                              player,
 +                              weaponentity,
 +                              WEP_BLASTER.m_id | HITTYPE_SECONDARY,
 +                              WEP_CVAR_SEC(vaporizer, shotangle),
 +                              WEP_CVAR_SEC(vaporizer, damage),
 +                              WEP_CVAR_SEC(vaporizer, edgedamage),
 +                              WEP_CVAR_SEC(vaporizer, radius),
 +                              WEP_CVAR_SEC(vaporizer, force),
 +                              WEP_CVAR_SEC(vaporizer, speed),
 +                              WEP_CVAR_SEC(vaporizer, spread),
 +                              WEP_CVAR_SEC(vaporizer, delay),
 +                              WEP_CVAR_SEC(vaporizer, lifetime)
 +                      );
 +                      player.(weaponentity).m_weapon = oldwep;
                }
 -              Weapon wpn = PS(player).m_weapon;
 -              .entity weaponentity = weaponentities[0]; // TODO: unhardcode
 -              if(player.(weaponentity).state != WS_CLEAR)
 -                      w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1));
 -
 -              player.weapon_blocked = true;
        }
  
        PHYS_INPUT_BUTTON_ATCK2(player) = false;
@@@ -177,14 -240,19 +181,14 @@@ MUTATOR_HOOKFUNCTION(ok, PlayerSpawn
  {
        entity player = M_ARGV(0, entity);
  
 -      if(autocvar_g_overkill_ammo_charge)
 -      {
 -              FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit));
 -
 -              player.ok_use_ammocharge = 1;
 -              player.ok_notice_time = time;
 -      }
 -      else
 -              player.ok_use_ammocharge = 0;
 -
        // if player changed their weapon while dead, don't switch to their death weapon
        if(player.impulse)
 -              player.ok_lastwep = 0;
 +      {
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +              {
 +                      player.ok_lastwep[slot] = WEP_Null;
 +              }
 +      }
  }
  
  void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); }
@@@ -206,7 -274,7 +210,7 @@@ MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpa
                        wep.noalign = ent.noalign;
                        wep.cnt = ent.cnt;
                        wep.team = ent.team;
-                       wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
+                       wep.respawntime = g_pickup_respawntime_superweapon;
                        wep.pickup_anyway = true;
                        wep.spawnfunc_checked = true;
                        setthink(wep, self_spawnfunc_weapon_hmg);
                        wep.noalign = ent.noalign;
                        wep.cnt = ent.cnt;
                        wep.team = ent.team;
-                       wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
+                       wep.respawntime = g_pickup_respawntime_superweapon;
                        wep.pickup_anyway = true;
                        wep.spawnfunc_checked = true;
                        setthink(wep, self_spawnfunc_weapon_rpc);
@@@ -238,17 -306,30 +242,21 @@@ MUTATOR_HOOKFUNCTION(ok, FilterItem
        entity item = M_ARGV(0, entity);
  
        if(item.ok_item)
-               return;
+               return false;
  
-       switch(item.items)
+       switch(item.itemdef)
        {
-               case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway);
-               case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway);
+               case ITEM_HealthMega: return autocvar_g_overkill_filter_healthmega;
+               case ITEM_ArmorMedium: return autocvar_g_overkill_filter_armormedium;
+               // WARNING: next two statements look wrong because of inconsistency between cvar names and code
+               // armor cvars need renaming to be consistent with their health counterparts
+               case ITEM_ArmorLarge: return autocvar_g_overkill_filter_armorbig;
+               case ITEM_ArmorMega: return autocvar_g_overkill_filter_armorlarge;
        }
  
        return true;
  }
  
 -MUTATOR_HOOKFUNCTION(ok, SpectateCopy)
 -{
 -      entity spectatee = M_ARGV(0, entity);
 -      entity client = M_ARGV(1, entity);
 -
 -      client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id];
 -      client.ok_use_ammocharge = spectatee.ok_use_ammocharge;
 -}
 -
  MUTATOR_HOOKFUNCTION(ok, SetStartItems, CBC_ORDER_LAST)
  {
        WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
index f407be711134657178e65f1c957c808dd5358542,cbfbaaaca2beb365f8ebff248b2542922f577594..fd57e974ac8c242773500ec2edfd835655528adc
@@@ -96,15 -96,8 +96,15 @@@ void PM_ClientMovement_UpdateStatus(ent
  
        // set crouched
        bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
 -      if(this.hook && !wasfreed(this.hook))
 -              do_crouch = false;
 +      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +      {
 +              entity wep = viewmodels[slot];
 +              if(wep.hook && !wasfreed(wep.hook))
 +              {
 +                      do_crouch = false;
 +                      break; // don't bother checking the others
 +              }
 +      }
        if(this.waterlevel >= WATERLEVEL_SWIMMING)
                do_crouch = false;
        if(hud != HUD_NORMAL)
@@@ -314,7 -307,7 +314,7 @@@ bool PlayerJump(entity this
        }
  
        if (!doublejump)
-               if (!IS_ONGROUND(this))
+               if (!IS_ONGROUND(this) && !IS_ONSLICK(this))
                        return IS_JUMP_HELD(this);
  
        bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
                }
        }
  
-       if (!WAS_ONGROUND(this))
+       if (!WAS_ONGROUND(this) && !WAS_ONSLICK(this))
        {
  #ifdef SVQC
                if(autocvar_speedmeter)
        this.velocity_z += mjumpheight;
  
        UNSET_ONGROUND(this);
+       UNSET_ONSLICK(this);
        SET_JUMP_HELD(this);
  
  #ifdef SVQC
@@@ -614,12 -608,7 +615,12 @@@ void PM_check_hitground(entity this
      this.wasFlying = false;
      if (this.waterlevel >= WATERLEVEL_SWIMMING) return;
      if (time < this.ladder_time) return;
 -    if (this.hook) return;
 +    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +    {
 +      .entity weaponentity = weaponentities[slot];
 +      if(this.(weaponentity).hook)
 +              return;
 +    }
      this.nextstep = time + 0.3 + random() * 0.1;
      trace_dphitq3surfaceflags = 0;
      tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
@@@ -653,6 -642,24 +654,24 @@@ void PM_Footsteps(entity this
  #endif
  }
  
+ void PM_check_slick(entity this)
+ {
+       if(!IS_ONGROUND(this))
+               return;
+       if(!PHYS_SLICK_APPLYGRAVITY(this))
+               return;
+       tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+       if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+       {
+               UNSET_ONGROUND(this);
+               SET_ONSLICK(this);
+       }
+       else
+               UNSET_ONSLICK(this);
+ }
  void PM_check_blocked(entity this)
  {
  #ifdef SVQC
diff --combined qcsrc/common/stats.qh
index 0714f3b97fcb409905798272ff44d7ee39803c11,09921c6f86cbfe1cbaaceb4c83b4b0cbc5798f9e..f9565db48bf0f54c4e57f893812b09a332c3dbcc
@@@ -55,7 -55,13 +55,7 @@@ REGISTER_STAT(PL_CROUCH_MAX, vector, au
  
  REGISTER_STAT(KH_KEYS, int)
  
 -/** weapon requested to switch to; next WANTED weapon (for HUD) */
 -REGISTER_STAT(SWITCHWEAPON, int)
 -/** weapon currently being switched to (is copied from switchweapon once switch is possible) */
 -REGISTER_STAT(SWITCHINGWEAPON, int)
 -REGISTER_STAT(WEAPON_NEXTTHINK, float)
  #ifdef SVQC
 -SPECTATE_COPYFIELD(_STAT(WEAPON_NEXTTHINK))
  float W_WeaponRateFactor(entity this);
  #endif
  REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
@@@ -108,6 -114,8 +108,6 @@@ REGISTER_STAT(NADE_BONUS_SCORE, float
  REGISTER_STAT(HEALING_ORB, float)
  REGISTER_STAT(HEALING_ORB_ALPHA, float)
  REGISTER_STAT(PLASMA, int)
 -REGISTER_STAT(OK_AMMO_CHARGE, float)
 -REGISTER_STAT(OK_AMMO_CHARGEPOOL, float)
  REGISTER_STAT(FROZEN, int)
  REGISTER_STAT(REVIVE_PROGRESS, float)
  REGISTER_STAT(ROUNDLOST, int)
@@@ -255,6 -263,11 +255,11 @@@ REGISTER_STAT(CAMERA_SPECTATOR, int
  
  REGISTER_STAT(SPECTATORSPEED, float)
  
+ #ifdef SVQC
+ bool autocvar_sv_slick_applygravity;
+ #endif
+ REGISTER_STAT(SLICK_APPLYGRAVITY, bool, autocvar_sv_slick_applygravity)
  #ifdef SVQC
  #include "physics/movetypes/movetypes.qh"
  #endif
diff --combined qcsrc/server/client.qc
index ec35ccb616baa4835bcde47ef5b576fb790f7896,1735a78be8988464792f97b17c52af798d6678af..328917336368c11e38341337543f0d227bf1e2c3
@@@ -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>
@@@ -112,6 -111,7 +112,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 -256,7 +251,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)
@@@ -618,6 -625,8 +618,8 @@@ void PutClientInServer(entity this
                FixPlayermodel(this);
                this.drawonlytoclient = NULL;
  
+               this.viewloc = NULL;
                this.crouch = false;
                this.view_ofs = STAT(PL_VIEW_OFS, this);
                setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
  
                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;
@@@ -1213,6 -1210,9 +1215,9 @@@ void ClientConnect(entity this
        if (IS_REAL_CLIENT(this))
                sv_notice_join(this);
  
+       // update physics stats (players can spawn before physics runs)
+       Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
        IL_EACH(g_initforplayer, it.init_for_player, {
                it.init_for_player(it, this);
        });
@@@ -1252,7 -1252,7 +1257,7 @@@ void ClientDisconnect(entity this
  
        Unfreeze(this);
  
 -      RemoveGrapplingHook(this);
 +      RemoveGrapplingHooks(this);
  
        // Here, everything has been done that requires this player to be a client.
  
@@@ -1674,6 -1674,7 +1679,7 @@@ void SpectateCopy(entity this, entity s
        this.angles = spectatee.v_angle;
        STAT(FROZEN, this) = STAT(FROZEN, spectatee);
        this.revive_progress = spectatee.revive_progress;
+       this.viewloc = spectatee.viewloc;
        if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
                this.fixangle = true;
        setorigin(this, spectatee.origin);
@@@ -2428,17 -2429,13 +2434,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_world.qc
index 803e4376b3e29b0afee09d8fc88013343764d875,48b587e5eb2de33312210f0eb139a3587cc6bc94..8e8dce9b12080abd58a7ff9ac91fd9bd2de9f317
@@@ -917,7 -917,8 +917,8 @@@ spawnfunc(worldspawn
        if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config"))
                modname = cvar_string("g_mod_config");
        // extra mutators that deserve to count as mod
-       MUTATOR_CALLHOOK(SetModname);
+       MUTATOR_CALLHOOK(SetModname, modname);
+       modname = M_ARGV(0, string);
  
        // save it for later
        modname = strzone(modname);
@@@ -2090,6 -2091,10 +2091,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 670e4c52ee2a70543675b9e6280d1ffa3dea24a7,0e0c7271ef0ee51a2e3bc343c1e14270dec41352..2d19df72bfc5aa92011786a0177be6f4bff52678
@@@ -137,14 -137,12 +137,14 @@@ MUTATOR_HOOKABLE(FormatMessage, EV_Form
  /** returns true if throwing the current weapon shall not be allowed */
  #define EV_ForbidThrowCurrentWeapon(i, o) \
      /** player        */ i(entity, MUTATOR_ARGV_0_entity) \
 +    /** weapon entity */ i(entity, MUTATOR_ARGV_1_entity) \
      /**/
  MUTATOR_HOOKABLE(ForbidThrowCurrentWeapon, EV_ForbidThrowCurrentWeapon);
  
  /** returns true if dropping the current weapon shall not be allowed at any time including death */
  #define EV_ForbidDropCurrentWeapon(i, o) \
 -    /** player */ i(entity, MUTATOR_ARGV_0_entity) \
 +    /** player */        i(entity, MUTATOR_ARGV_0_entity) \
 +    /** weapon id */     i(int, MUTATOR_ARGV_1_int) \
      /**/
  MUTATOR_HOOKABLE(ForbidDropCurrentWeapon, EV_ForbidDropCurrentWeapon);
  
@@@ -368,8 -366,7 +368,8 @@@ MUTATOR_HOOKABLE(PlayerDamaged, EV_Play
   * Called by W_DecreaseAmmo
   */
  #define EV_W_DecreaseAmmo(i, o) \
 -    /** actor */ i(entity, MUTATOR_ARGV_0_entity) \
 +    /** actor */            i(entity, MUTATOR_ARGV_0_entity) \
 +    /** weapon entity */    i(entity, MUTATOR_ARGV_1_entity) \
      /**/
  MUTATOR_HOOKABLE(W_DecreaseAmmo, EV_W_DecreaseAmmo);
  
@@@ -483,6 -480,7 +483,7 @@@ MUTATOR_HOOKABLE(SV_StartFrame, EV_NO_A
  
  #define EV_SetModname(i, o) \
      /** name of the mutator/mod if it warrants showing as such in the server browser */ \
+     /**/ i(string, MUTATOR_ARGV_0_string) \
      /**/ o(string, MUTATOR_ARGV_0_string) \
      /**/
  MUTATOR_HOOKABLE(SetModname, EV_SetModname);