Merge branch 'master' into Mario/wepent_experimental
authorMario <mario@smbclan.net>
Tue, 6 Dec 2016 15:01:32 +0000 (01:01 +1000)
committerMario <mario@smbclan.net>
Tue, 6 Dec 2016 15:01:32 +0000 (01:01 +1000)
1  2 
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/server/bot/default/havocbot/havocbot.qc
qcsrc/server/mutators/events.qh
qcsrc/server/player.qc
qcsrc/server/weapons/selection.qc

@@@ -231,6 -231,7 +231,7 @@@ void buff_Think(entity this
                this.skin = buff.m_skin;
  
                setmodel(this, MDL_BUFF);
+               setsize(this, BUFF_MIN, BUFF_MAX);
  
                if(this.buff_waypoint)
                {
@@@ -842,14 -843,8 +843,14 @@@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreTh
        }
  
        if(player.buffs & BUFF_AMMO.m_itemid)
 -      if(player.clip_size)
 -              player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
 +      {
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +              {
 +                      .entity weaponentity = weaponentities[slot];
 +                      if(player.(weaponentity).clip_size)
 +                              player.(weaponentity).clip_load = player.(weaponentity).(weapon_load[player.(weaponentity).m_switchweapon.m_id]) = player.(weaponentity).clip_size;
 +              }
 +      }
  
        if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid))
        if(player.alpha != autocvar_g_buffs_invisible_alpha)
                        player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO);
                        player.items |= IT_UNLIMITED_WEAPON_AMMO;
  
 -                      if(player.clip_load)
 -                              player.buff_ammo_prev_clipload = player.clip_load;
 -                      player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size;
 +                      if(player.buffs & BUFF_AMMO.m_itemid)
 +                      {
 +                              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +                              {
 +                                      .entity weaponentity = weaponentities[slot];
 +                                      if(player.(weaponentity).clip_load)
 +                                              player.(weaponentity).buff_ammo_prev_clipload = player.(weaponentity).clip_load;
 +                                      if(player.(weaponentity).clip_size)
 +                                              player.(weaponentity).clip_load = player.(weaponentity).(weapon_load[player.(weaponentity).m_switchweapon.m_id]) = player.(weaponentity).clip_size;
 +                              }
 +                      }
                }
  
                BUFF_ONREM(BUFF_AMMO)
                        else
                                player.items &= ~IT_UNLIMITED_WEAPON_AMMO;
  
 -                      if(player.buff_ammo_prev_clipload)
 -                              player.clip_load = player.buff_ammo_prev_clipload;
 +                      if(player.buffs & BUFF_AMMO.m_itemid)
 +                      {
 +                              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +                              {
 +                                      .entity weaponentity = weaponentities[slot];
 +                                      if(player.(weaponentity).buff_ammo_prev_clipload)
 +                                              player.(weaponentity).clip_load = player.(weaponentity).buff_ammo_prev_clipload;
 +                              }
 +                      }
                }
  
                BUFF_ONADD(BUFF_INVISIBLE)
@@@ -13,7 -13,6 +13,7 @@@
  #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>
@@@ -49,7 -48,7 +49,7 @@@ void havocbot_ai(entity this
  
                // TODO: tracewalk() should take care of this job (better path finding under water)
                // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
-               if(IS_DEAD(this))
+               if(!(IS_DEAD(this)))
                if(!this.goalcurrent)
                if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
                {
                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;
 +                              ));
 +                      }
                }
        }
  }
@@@ -610,35 -589,25 +610,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
@@@ -1009,7 -978,7 +1009,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;
                        }
                }
@@@ -145,14 -145,12 +145,14 @@@ MUTATOR_HOOKABLE(PreFormatMessage, EV_P
  /** 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);
  
@@@ -377,8 -375,7 +377,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);
  
@@@ -894,3 -891,18 +894,18 @@@ MUTATOR_HOOKABLE(ForbidWeaponUse, EV_Fo
      /** keepvelocity? */        i(bool, MUTATOR_ARGV_2_bool) \
      /**/
  MUTATOR_HOOKABLE(CopyBody, EV_CopyBody);
+ /** called when sending a chat message, ret argument can be changed to prevent the message */
+ #define EV_ChatMessage(i, o) \
+     /** sender */ i(entity, MUTATOR_ARGV_0_entity) \
+     /** ret */ i(int, MUTATOR_ARGV_1_int) \
+     /**/ o(int, MUTATOR_ARGV_1_int) \
+     /**/
+ MUTATOR_HOOKABLE(ChatMessage, EV_ChatMessage);
+ /** return true to prevent sending a chat (private, team or regular) message from reaching a certain player */
+ #define EV_ChatMessageTo(i, o) \
+     /** destination player */ i(entity, MUTATOR_ARGV_0_entity) \
+     /** sender */ i(entity, MUTATOR_ARGV_1_entity) \
+     /**/
+ MUTATOR_HOOKABLE(ChatMessageTo, EV_ChatMessageTo);
diff --combined qcsrc/server/player.qc
@@@ -24,7 -24,6 +24,7 @@@
  #include "../common/effects/qc/all.qh"
  #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
  #include "../common/triggers/include.qh"
 +#include "../common/wepent.qh"
  
  #include "weapons/weaponstats.qh"
  
@@@ -486,7 -485,6 +486,7 @@@ void PlayerDamage(entity this, entity i
  
        valid_damage_for_weaponstats = 0;
        Weapon awep = WEP_Null;
 +      .entity weaponentity = weaponentities[0]; // TODO: unhardcode
  
        if(vbot || IS_REAL_CLIENT(this))
        if(abot || IS_REAL_CLIENT(attacker))
        if(DIFF_TEAM(this, attacker))
        {
                if(DEATH_ISSPECIAL(deathtype))
 -                      awep = PS(attacker).m_weapon;
 +                      awep = attacker.(weaponentity).m_weapon;
                else
                        awep = DEATH_WEAPONOF(deathtype);
                valid_damage_for_weaponstats = 1;
        da = da - max(this.armorvalue, 0);
        if(valid_damage_for_weaponstats)
        {
 -              WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da);
 +              WeaponStats_LogDamage(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot, dh + da);
        }
        if (damage)
        {
                }
  
                if(valid_damage_for_weaponstats)
 -                      WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot);
 +                      WeaponStats_LogKill(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot);
  
                if(autocvar_sv_gentle < 1)
                if(sound_allowed(MSG_BROADCAST, attacker))
                MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
                excess = M_ARGV(4, float);
  
 -              Weapon wep = PS(this).m_weapon;
 -              /*for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +              Weapon wep = this.(weaponentity).m_weapon;
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
 -                      .entity weaponentity = weaponentities[slot];
 -                      wep.wr_playerdeath(wep, this, weaponentity);
 -              }*/
 -              .entity weaponentity = weaponentities[0]; // TODO: unhardcode
 -              wep.wr_playerdeath(wep, this, weaponentity);
 +                      .entity went = weaponentities[slot];
 +                      wep.wr_playerdeath(wep, this, went);
 +              }
  
 -              RemoveGrapplingHook(this);
 +              RemoveGrapplingHooks(this);
  
                Portal_ClearAllLater(this);
  
                // clear waypoints
                WaypointSprite_PlayerDead(this);
                // throw a weapon
 -              SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id);
 +              for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +              {
 +                      .entity went = weaponentities[slot];
 +                      SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, this.(went).m_switchweapon.m_id, went);
 +              }
  
                // become fully visible
                this.alpha = default_player_alpha;
@@@ -913,6 -909,9 +913,9 @@@ int Say(entity source, int teamsay, ent
                ret = 1;
        }
  
+       MUTATOR_CALLHOOK(ChatMessage, source, ret);
+       ret = M_ARGV(1, int);
        if(sourcemsgstr != "" && ret != 0)
        {
                if(ret < 0) // faked message, because the player is muted
                else if(privatesay) // private message, between 2 people only
                {
                        sprint(source, sourcemsgstr);
-                       sprint(privatesay, msgstr);
                        if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled
-                       if(cmsgstr != "")
-                               centerprint(privatesay, cmsgstr);
+                       if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source))
+                       {
+                               sprint(privatesay, msgstr);
+                               if(cmsgstr != "")
+                                       centerprint(privatesay, cmsgstr);
+                       }
                }
                else if ( teamsay && source.active_minigame )
                {
                        sprint(source, sourcemsgstr);
                        dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, sprint(it, msgstr));
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
                }
                else if(teamsay > 0) // team message, only sent to team mates
                {
                        dedicated_print(msgstr); // send to server console too
                        if(sourcecmsgstr != "")
                                centerprint(source, sourcecmsgstr);
-                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, {
+                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
                                sprint(it, msgstr);
                                if(cmsgstr != "")
                                        centerprint(it, cmsgstr);
                {
                        sprint(source, sourcemsgstr);
                        dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
+                       FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
                }
                else
                {
                  dedicated_print(msgstr); // send to server console too
                  MX_Say(strcat(playername(source), "^7: ", msgin));
              }
-             FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
+             FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
          }
        }
  
@@@ -9,7 -9,6 +9,7 @@@
  #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)
@@@ -23,7 -22,7 +23,7 @@@
  void Weapon_whereis(Weapon this, entity cl)
  {
        if (!autocvar_g_showweaponspawns) return;
-       IL_EACH(g_items, it.weapon == this.m_id,
+       IL_EACH(g_items, it.weapon == this.m_id && (it.ItemStatus & ITS_AVAILABLE),
        {
                if (it.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2)
                        continue;
@@@ -40,7 -39,7 +40,7 @@@
        });
  }
  
 -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);
  }