From: terencehill <piuntn@gmail.com>
Date: Wed, 10 Apr 2019 14:54:55 +0000 (+0200)
Subject: Fix #2201 "ForbidWeaponUse mutator hook can leave the player holding a weapon they... 
X-Git-Tag: xonotic-v0.8.5~1551^2
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=0ca59fa70f5e81336415e1661d879aa91ff03db0;p=xonotic%2Fxonotic-data.pk3dir.git

Fix #2201 "ForbidWeaponUse mutator hook can leave the player holding a weapon they no longer possess". ForbidWeaponUse now forbids weapon use but allows switching to another weapon, while the new mutator hook LockWeapon applies both restrictions
---

diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
index e56d04466..97e63161d 100644
--- a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
+++ b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
@@ -45,7 +45,7 @@ MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink)
 	if(!STAT(FROZEN, player))
 	if(!PHYS_INPUT_BUTTON_CHAT(player))
 	if(IS_REAL_CLIENT(player)) // bots may camp, but that's no reason to constantly kill them
-	if(!forbidWeaponUse(player))
+	if(!weaponLocked(player))
 	{
 		// calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
 		vector dist = vec2(player.campcheck_prevorigin - player.origin);
diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc
index 6924947f0..b6c4578d5 100644
--- a/qcsrc/common/mutators/mutator/nades/nades.qc
+++ b/qcsrc/common/mutators/mutator/nades/nades.qc
@@ -1161,7 +1161,7 @@ bool CanThrowNade(entity this)
 	if (!autocvar_g_nades)
 		return false; // allow turning them off mid match
 
-	if(forbidWeaponUse(this))
+	if (weaponLocked(this))
 		return false;
 
 	if (!IS_PLAYER(this))
diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
index c8e4398b0..15a2fc2e0 100644
--- a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
+++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
@@ -143,7 +143,7 @@ MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
 	{
 		return;
 	}
-	if (!PHYS_INPUT_BUTTON_ATCK2(player) || forbidWeaponUse(player) ||
+	if (!PHYS_INPUT_BUTTON_ATCK2(player) || weaponLocked(player) ||
 		!(round_handler_IsActive() && !round_handler_IsRoundStarted()))
 	{
 		return;
diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc
index b747ce749..73435841d 100644
--- a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc
+++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc
@@ -95,7 +95,7 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
 			if (IS_DEAD(it)) continue;
 			if (time < it.msnt_timer) continue;
 			if (time < it.spawnshieldtime) continue;
-			if (forbidWeaponUse(it)) continue;
+			if (weaponLocked(it)) continue;
 			if (it == player) continue;
 
 			tested++; // i consider a teammate to be available when he passes the checks above
diff --git a/qcsrc/common/vehicles/sv_vehicles.qc b/qcsrc/common/vehicles/sv_vehicles.qc
index 9a85e1145..c8e47dabc 100644
--- a/qcsrc/common/vehicles/sv_vehicles.qc
+++ b/qcsrc/common/vehicles/sv_vehicles.qc
@@ -924,7 +924,7 @@ void vehicles_touch(entity this, entity toucher)
 	// Vehicle currently in use
 	if(this.owner)
 	{
-		if(!forbidWeaponUse(this.owner))
+		if(!weaponLocked(this.owner))
 		if(toucher != NULL)
 		if((this.origin_z + this.maxs_z) > (toucher.origin_z))
 		if(vehicles_crushable(toucher))
diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc
index a83b0928f..2bd81f12d 100644
--- a/qcsrc/common/vehicles/vehicle/bumblebee.qc
+++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc
@@ -157,7 +157,7 @@ bool bumblebee_gunner_frame(entity this, float dt)
 						  autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up,
 						  _out * -1,  _in,  autocvar_g_vehicle_bumblebee_cannon_turnspeed, dt);
 
-	if(!forbidWeaponUse(this))
+	if(!weaponLocked(this))
 	if(PHYS_INPUT_BUTTON_ATCK(this))
 		if(time > gun.attack_finished_single[0])
 			if(gun.vehicle_energy >= autocvar_g_vehicle_bumblebee_cannon_cost)
@@ -531,7 +531,7 @@ bool bumblebee_pilot_frame(entity this, float dt)
 					  autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down * -1,  autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up,
 					  autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1,  autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides,  autocvar_g_vehicle_bumblebee_raygun_turnspeed, dt);
 
-	if(!forbidWeaponUse(this))
+	if(!weaponLocked(this))
 	if((PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * PHYS_INPUT_FRAMETIME || autocvar_g_vehicle_bumblebee_raygun == 0))
 	{
 		vehic.gun3.enemy.realowner = this;
diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc
index 109eab0ca..eb9d6623a 100644
--- a/qcsrc/common/vehicles/vehicle/racer.qc
+++ b/qcsrc/common/vehicles/vehicle/racer.qc
@@ -287,7 +287,7 @@ bool racer_frame(entity this, float dt)
 
 	Weapon wep1 = WEP_RACER;
 	.entity weaponentity = weaponentities[0]; // TODO: unhardcode
-	if (!forbidWeaponUse(player))
+	if (!weaponLocked(player))
 	if (PHYS_INPUT_BUTTON_ATCK(player))
 	if (wep1.wr_checkammo1(wep1, vehic, weaponentity))
 	{
@@ -327,7 +327,7 @@ bool racer_frame(entity this, float dt)
 		}
 	}
 
-	if(!forbidWeaponUse(player))
+	if(!weaponLocked(player))
 	if(time > vehic.delay)
 	if(PHYS_INPUT_BUTTON_ATCK2(player))
 	{
diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc
index b672d348b..6e9144168 100644
--- a/qcsrc/common/vehicles/vehicle/raptor.qc
+++ b/qcsrc/common/vehicles/vehicle/raptor.qc
@@ -358,7 +358,7 @@ bool raptor_frame(entity this, float dt)
 
 	Weapon wep1 = WEP_RAPTOR;
 	.entity weaponentity = weaponentities[0];
-	if(!forbidWeaponUse(this))
+	if(!weaponLocked(this))
 	if(PHYS_INPUT_BUTTON_ATCK(this))
 	if (wep1.wr_checkammo1(wep1, vehic, weaponentity))
 	{
@@ -375,7 +375,7 @@ bool raptor_frame(entity this, float dt)
 		vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, dt, false);
 
 	Weapon wep2a = WEP_RAPTOR_BOMB;
-	if(!forbidWeaponUse(this))
+	if(!weaponLocked(this))
 	if(STAT(VEHICLESTAT_W2MODE, vehic) == RSM_BOMB)
 	{
 		if(time > vehic.lip + autocvar_g_vehicle_raptor_bombs_refire)
diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc
index 3f023729f..36f6cb2f8 100644
--- a/qcsrc/common/vehicles/vehicle/spiderbot.qc
+++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc
@@ -246,7 +246,7 @@ bool spiderbot_frame(entity this, float dt)
 	vehic.angles_x = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, vehic.angles_x, autocvar_g_vehicle_spiderbot_tiltlimit);
 	vehic.angles_z = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, vehic.angles_z, autocvar_g_vehicle_spiderbot_tiltlimit);
 
-	if(!forbidWeaponUse(this))
+	if(!weaponLocked(this))
 	if(PHYS_INPUT_BUTTON_ATCK(this))
 	{
 		vehic.cnt = time;
diff --git a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc
index a18d03213..4b48eecce 100644
--- a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc
+++ b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc
@@ -175,7 +175,7 @@ void spiderbot_rocket_do(entity this)
     if(!PHYS_INPUT_BUTTON_ATCK2(this.owner))
         return;
 
-    if(forbidWeaponUse(this.owner))
+    if(weaponLocked(this.owner))
         return;
 
     v = gettaginfo(this.tur_head,gettagindex(this.tur_head,"tag_fire"));
diff --git a/qcsrc/common/weapons/weapon/hagar.qc b/qcsrc/common/weapons/weapon/hagar.qc
index 20c588715..51699bf0c 100644
--- a/qcsrc/common/weapons/weapon/hagar.qc
+++ b/qcsrc/common/weapons/weapon/hagar.qc
@@ -257,7 +257,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity)
 	// loadable hagar secondary attack, must always run each frame
 	if(time < game_starttime || time < actor.race_penalty || timeout_status == TIMEOUT_ACTIVE)
 		return;
-	if (round_handler_IsActive() && !round_handler_IsRoundStarted())
+	if (weaponUseForbidden(actor))
 		return;
 
 	bool loaded = actor.(weaponentity).hagar_load >= WEP_CVAR_SEC(hagar, load_max);
diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc
index 2e76827d7..dbf24c796 100644
--- a/qcsrc/common/weapons/weapon/vaporizer.qc
+++ b/qcsrc/common/weapons/weapon/vaporizer.qc
@@ -295,7 +295,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent
     } else if(WEP_CVAR(vaporizer, reload_ammo) && actor.(weaponentity).clip_load < vaporizer_ammo) { // forced reload
         thiswep.wr_reload(thiswep, actor, weaponentity);
     }
-    if((fire & 1) && (GetResource(actor, RES_CELLS) || !autocvar_g_rm) && !forbidWeaponUse(actor))
+    if((fire & 1) && (GetResource(actor, RES_CELLS) || !autocvar_g_rm) && !weaponLocked(actor))
     {
         if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vaporizer, refire)))
         {
diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc
index ffb19f17a..453cfc1e7 100644
--- a/qcsrc/server/g_hook.qc
+++ b/qcsrc/server/g_hook.qc
@@ -360,7 +360,7 @@ void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float
 
 void FireGrapplingHook(entity actor, .entity weaponentity)
 {
-	if(forbidWeaponUse(actor)) return;
+	if(weaponLocked(actor)) return;
 	if(actor.vehicle) return;
 
 	// TODO: offhand hook shoots from eye
diff --git a/qcsrc/server/impulse.qc b/qcsrc/server/impulse.qc
index 9b1e38e8e..2320eb830 100644
--- a/qcsrc/server/impulse.qc
+++ b/qcsrc/server/impulse.qc
@@ -334,7 +334,7 @@ IMPULSE(weapon_reload)
 {
 	if (this.vehicle) return;
 	if (IS_DEAD(this)) return;
-	if (forbidWeaponUse(this)) return;
+	if (weaponLocked(this)) return;
 	entity actor = this;
 	for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 	{
diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh
index ff8928c98..f0237b27e 100644
--- a/qcsrc/server/mutators/events.qh
+++ b/qcsrc/server/mutators/events.qh
@@ -1083,15 +1083,23 @@ MUTATOR_HOOKABLE(Player_ChangeTeamKill, EV_Player_ChangeTeamKill);
 	/** id */       i(float, MUTATOR_ARGV_0_float) \
 	/** status */   i(float, MUTATOR_ARGV_1_float) \
 	/** data */     i(string, MUTATOR_ARGV_2_string) \
-    /**/
+	/**/
 MUTATOR_HOOKABLE(URI_GetCallback, EV_URI_GetCallback);
 
+/**
+ * return true to lock weapon (can't be used nor changed) for a player
+ */
+#define EV_LockWeapon(i, o) \
+	/** player */ i(entity, MUTATOR_ARGV_0_entity) \
+	/**/
+MUTATOR_HOOKABLE(LockWeapon, EV_LockWeapon);
+
 /**
  * return true to prevent weapon use for a player
  */
- #define EV_ForbidWeaponUse(i, o) \
-    /** player */ i(entity, MUTATOR_ARGV_0_entity) \
-    /**/
+#define EV_ForbidWeaponUse(i, o) \
+	/** player */ i(entity, MUTATOR_ARGV_0_entity) \
+	/**/
 MUTATOR_HOOKABLE(ForbidWeaponUse, EV_ForbidWeaponUse);
 
 /** called when creating a clone of the player (usually for corpses that stay after the player has re-spawned) */
diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc
index ea5bd2255..7f9c6ad0d 100644
--- a/qcsrc/server/weapons/selection.qc
+++ b/qcsrc/server/weapons/selection.qc
@@ -281,7 +281,7 @@ bool W_SwitchWeapon(entity this, Weapon w, .entity weaponentity)
 			return false;
 		}
 	}
-	else if(!forbidWeaponUse(this) && CS(this).cvar_cl_weapon_switch_reload)
+	else if(!weaponLocked(this) && CS(this).cvar_cl_weapon_switch_reload)
 	{
 		entity actor = this;
 		w.wr_reload(w, actor, weaponentity);
diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc
index e7ab90c39..b75881127 100644
--- a/qcsrc/server/weapons/weaponsystem.qc
+++ b/qcsrc/server/weapons/weaponsystem.qc
@@ -435,13 +435,20 @@ void weapon_thinkf(entity actor, .entity weaponentity, WFRAME fr, float t, void(
 	}
 }
 
-bool forbidWeaponUse(entity player)
+bool weaponUseForbidden(entity player)
+{
+	if (round_handler_IsActive() && !round_handler_IsRoundStarted()) return true;
+	if (MUTATOR_CALLHOOK(ForbidWeaponUse, player)) return true;
+	return false;
+}
+
+bool weaponLocked(entity player)
 {
 	if (time < game_starttime && !sv_ready_restart_after_countdown) return true;
 	if (player.player_blocked) return true;
 	if (game_stopped) return true;
 	if (STAT(FROZEN, player)) return true;
-	if (MUTATOR_CALLHOOK(ForbidWeaponUse, player)) return true;
+	if (MUTATOR_CALLHOOK(LockWeapon, player)) return true;
 	return false;
 }
 
@@ -459,10 +466,10 @@ void W_WeaponFrame(Player actor, .entity weaponentity)
 	int button_atck = PHYS_INPUT_BUTTON_ATCK(actor);
 	int button_atck2 = PHYS_INPUT_BUTTON_ATCK2(actor);
 
-	if (round_handler_IsActive() && !round_handler_IsRoundStarted())
+	if (weaponUseForbidden(actor))
 		button_atck = button_atck2 = 0; // forbid primary and secondary fire, switching is allowed
 
-	if (forbidWeaponUse(actor))
+	if (weaponLocked(actor))
 	{
 		if (this.state != WS_CLEAR)
 		{
@@ -591,7 +598,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity)
 		bool block_weapon = false;
 		{
 			bool key_pressed = PHYS_INPUT_BUTTON_HOOK(actor) && !actor.vehicle;
-			if (round_handler_IsActive() && !round_handler_IsRoundStarted())
+			if (weaponUseForbidden(actor))
 				key_pressed = false;
 
 			Weapon off = actor.offhand;
diff --git a/qcsrc/server/weapons/weaponsystem.qh b/qcsrc/server/weapons/weaponsystem.qh
index c53b15094..91879feb2 100644
--- a/qcsrc/server/weapons/weaponsystem.qh
+++ b/qcsrc/server/weapons/weaponsystem.qh
@@ -12,7 +12,8 @@ void CL_SpawnWeaponentity(entity e, .entity weaponentity);
 
 vector CL_Weapon_GetShotOrg(float wpn);
 
-bool forbidWeaponUse(entity player);
+bool weaponUseForbidden(entity player);
+bool weaponLocked(entity player);
 
 void W_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset);