From fb9267ffab37eae0bcbc4ac42a8a45a1ad864c0b Mon Sep 17 00:00:00 2001
From: TimePath <andrew.hardaker1995@gmail.com>
Date: Tue, 29 Sep 2015 11:28:19 +1000
Subject: [PATCH] Offhand hook: migrate to mutator system

---
 defaultXonotic.cfg                     |  2 -
 qcsrc/common/weapons/weapon/hook.qc    | 11 +----
 qcsrc/server/autocvars.qh              |  2 -
 qcsrc/server/g_hook.qc                 | 66 ++++++++++++--------------
 qcsrc/server/g_world.qc                |  4 --
 qcsrc/server/miscfunctions.qc          |  2 +-
 qcsrc/server/miscfunctions.qh          |  1 -
 qcsrc/server/mutators/mutator_hook.qc  | 37 ++++++++++++++-
 qcsrc/server/mutators/mutator_nades.qc |  8 +++-
 qcsrc/server/teamplay.qc               |  5 --
 qcsrc/server/weapons/selection.qc      |  6 ++-
 qcsrc/server/weapons/weaponsystem.qc   |  7 +--
 12 files changed, 80 insertions(+), 71 deletions(-)

diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg
index c51dfc1a26..9e7405ed72 100644
--- a/defaultXonotic.cfg
+++ b/defaultXonotic.cfg
@@ -469,8 +469,6 @@ seta menu_monsters_edit_movetarget 1
 set g_playerclip_collisions 1 "0 = disable collision testing against playerclips, might be useful on some defrag maps"
 set g_botclip_collisions 1 "0 = disable collision testing against botclips, might be useful on some defrag maps"
 
-set g_grappling_hook 0 "let players spawn with the grappling hook which allows them to pull themselves up"
-
 set g_spawn_alloweffects 1 "allow clients to enable spawn point and event effects such as particles and sounds, see cl_spawn_ cvars for more info"
 set g_spawn_furthest 0.5 "this amount of the spawns shall be far away from any players"
 set g_spawn_useallspawns 0 "use all spawns, e.g. also team spawns in non-teamplay, and all spawns, even enemy spawns, in teamplay"
diff --git a/qcsrc/common/weapons/weapon/hook.qc b/qcsrc/common/weapons/weapon/hook.qc
index 1478d96edc..8e7afd53d5 100644
--- a/qcsrc/common/weapons/weapon/hook.qc
+++ b/qcsrc/common/weapons/weapon/hook.qc
@@ -70,16 +70,7 @@ HOOK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
 #ifdef IMPLEMENTATION
 #ifdef SVQC
 
-void spawnfunc_weapon_hook(void)
-{SELFPARAM();
-	if(g_grappling_hook) // offhand hook
-	{
-		startitem_failed = true;
-		remove(self);
-		return;
-	}
-	weapon_defaultspawnfunc(WEP_HOOK.m_id);
-}
+void spawnfunc_weapon_hook() { weapon_defaultspawnfunc(WEP_HOOK.m_id); }
 
 void W_Hook_ExplodeThink(void)
 {SELFPARAM();
diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh
index 83677a24c5..bc8876304e 100644
--- a/qcsrc/server/autocvars.qh
+++ b/qcsrc/server/autocvars.qh
@@ -335,8 +335,6 @@ float autocvar_g_freezetag_warmup;
 bool autocvar_g_full_getstatus_responses;
 bool autocvar_g_fullbrightitems;
 bool autocvar_g_fullbrightplayers;
-#define autocvar_g_grappling_hook cvar("g_grappling_hook")
-int autocvar_g_grappling_hook_tarzan;
 int autocvar_g_balance_grapplehook_pull_frozen;
 float autocvar_g_balance_grapplehook_nade_time;
 bool autocvar_g_balance_grapplehook_gravity;
diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc
index 88c9783f52..bda9c05807 100644
--- a/qcsrc/server/g_hook.qc
+++ b/qcsrc/server/g_hook.qc
@@ -129,6 +129,8 @@ float GrapplingHookSend(entity to, int sf)
 	return true;
 }
 
+int autocvar_g_grappling_hook_tarzan;
+
 void GrapplingHookThink()
 {SELFPARAM();
 	float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch;
@@ -425,47 +427,39 @@ void FireGrapplingHook (void)
 void _GrapplingHookFrame();
 void GrapplingHookFrame()
 {SELFPARAM();
-	if(g_grappling_hook && timeout_status != TIMEOUT_ACTIVE && self.weapon != WEP_HOOK.m_id && !self.vehicle)
-	{
-		// offhand hook controls
-		if(self.BUTTON_HOOK)
-		{
-			if (!(self.hook || (self.hook_state & HOOK_WAITING_FOR_RELEASE)) && (time > self.hook_refire))
-			{
-				self.hook_state |= HOOK_FIRING;
-				self.hook_state |= HOOK_WAITING_FOR_RELEASE;
-			}
+	if (self.offhand == OFFHAND_HOOK) {
+        if (timeout_status != TIMEOUT_ACTIVE && self.weapon != WEP_HOOK.m_id && !self.vehicle) {
+            // offhand hook controls
+            if (self.BUTTON_HOOK) {
+                if (!(self.hook || (self.hook_state & HOOK_WAITING_FOR_RELEASE)) && (time > self.hook_refire)) {
+                    self.hook_state |= HOOK_FIRING;
+                    self.hook_state |= HOOK_WAITING_FOR_RELEASE;
+                }
+            } else {
+                self.hook_state |= HOOK_REMOVING;
+                self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
+            }
+
+            self.hook_state &= ~HOOK_RELEASING;
+            if (self.BUTTON_CROUCH && autocvar_g_balance_grapplehook_crouchslide) {
+                self.hook_state &= ~HOOK_PULLING;
+                //self.hook_state |= HOOK_RELEASING;
+            } else {
+                self.hook_state |= HOOK_PULLING;
+                //self.hook_state &= ~HOOK_RELEASING;
+            }
+        }
+	} else {
+		if (self.switchweapon != WEP_HOOK.m_id && !self.vehicle) {
+			if (self.BUTTON_HOOK && !self.hook_switchweapon)
+				W_SwitchWeapon(WEP_HOOK.m_id);
 		}
-		else
-		{
+		if (self.weapon != WEP_HOOK.m_id && self.offhand != OFFHAND_HOOK) {
+			self.hook_state &= ~HOOK_FIRING;
 			self.hook_state |= HOOK_REMOVING;
-			self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
-		}
-
-		self.hook_state &= ~HOOK_RELEASING;
-		if(self.BUTTON_CROUCH && autocvar_g_balance_grapplehook_crouchslide)
-		{
-			self.hook_state &= ~HOOK_PULLING;
-			//self.hook_state |= HOOK_RELEASING;
 		}
-		else
-		{
-			self.hook_state |= HOOK_PULLING;
-			//self.hook_state &= ~HOOK_RELEASING;
-		}
-	}
-	else if(!g_grappling_hook && self.switchweapon != WEP_HOOK.m_id && !self.vehicle)
-	{
-		if(self.BUTTON_HOOK && !self.hook_switchweapon)
-			W_SwitchWeapon(WEP_HOOK.m_id);
 	}
 	self.hook_switchweapon = self.BUTTON_HOOK;
-
-	if(!g_grappling_hook && self.weapon != WEP_HOOK.m_id && self.offhand != OFFHAND_HOOK)
-	{
-		self.hook_state &= ~HOOK_FIRING;
-		self.hook_state |= HOOK_REMOVING;
-	}
 	_GrapplingHookFrame();
 }
 
diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc
index 2c889c09e9..40d39a4cd6 100644
--- a/qcsrc/server/g_world.qc
+++ b/qcsrc/server/g_world.qc
@@ -688,10 +688,6 @@ void spawnfunc_worldspawn (void)
 		MUTATOR_CALLHOOK(BuildMutatorsString, s);
 		s = ret_string;
 
-		// simple, probably not good in the mutator system
-		if(autocvar_g_grappling_hook)
-			s = strcat(s, ":grappling_hook");
-
 		// initialiation stuff, not good in the mutator system
 		if(!autocvar_g_use_ammunition)
 			s = strcat(s, ":no_use_ammunition");
diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc
index 9cf9d0c2c2..210d8eb667 100644
--- a/qcsrc/server/miscfunctions.qc
+++ b/qcsrc/server/miscfunctions.qc
@@ -706,7 +706,7 @@ void readplayerstartcvars()
 
 	MUTATOR_CALLHOOK(SetStartItems);
 
-	if ((start_items & ITEM_Jetpack.m_itemid) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
+	if (start_items & ITEM_Jetpack.m_itemid)
 	{
 		start_items |= ITEM_JetpackRegen.m_itemid;
 		start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh
index 6148ee8fdc..0c5a2644a1 100644
--- a/qcsrc/server/miscfunctions.qh
+++ b/qcsrc/server/miscfunctions.qh
@@ -336,7 +336,6 @@ void readlevelcvars(void)
 	sv_foginterval = cvar("sv_foginterval");
 	g_cloaked = cvar("g_cloaked");
 	g_footsteps = cvar("g_footsteps");
-	g_grappling_hook = cvar("g_grappling_hook");
 	g_jetpack = cvar("g_jetpack");
 	sv_maxidle = cvar("sv_maxidle");
 	sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
diff --git a/qcsrc/server/mutators/mutator_hook.qc b/qcsrc/server/mutators/mutator_hook.qc
index dbf1681cb7..4c162634fc 100644
--- a/qcsrc/server/mutators/mutator_hook.qc
+++ b/qcsrc/server/mutators/mutator_hook.qc
@@ -1,7 +1,42 @@
-REGISTER_MUTATOR(hook, cvar("_g_grappling_hook"));
+AUTOCVAR(g_grappling_hook, bool, _("let players spawn with the grappling hook which allows them to pull themselves up"));
+REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) {
+    MUTATOR_ONADD {
+        g_grappling_hook = true;
+    }
+    MUTATOR_ONROLLBACK_OR_REMOVE {
+        g_grappling_hook = false;
+    }
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString)
+{
+    ret_string = strcat(ret_string, ":grappling_hook");
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString)
+{
+    ret_string = strcat(ret_string, ", Hook");
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString)
+{
+    ret_string = strcat(ret_string, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
+}
 
 MUTATOR_HOOKFUNCTION(hook, PlayerSpawn)
 {
     SELFPARAM();
     self.offhand = OFFHAND_HOOK;
 }
+
+MUTATOR_HOOKFUNCTION(hook, FilterItem)
+{
+    return self.weapon == WEP_HOOK.m_id;
+}
+
+MUTATOR_HOOKFUNCTION(hook, SetStartItems)
+{
+    start_items |= ITEM_JetpackRegen.m_itemid;
+    start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
+    warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
+}
diff --git a/qcsrc/server/mutators/mutator_nades.qc b/qcsrc/server/mutators/mutator_nades.qc
index 088f3f5294..98511b238f 100644
--- a/qcsrc/server/mutators/mutator_nades.qc
+++ b/qcsrc/server/mutators/mutator_nades.qc
@@ -863,6 +863,8 @@ float CanThrowNade()
 	return true;
 }
 
+.bool nade_altbutton;
+
 void nades_CheckThrow()
 {SELFPARAM();
 	if(!CanThrowNade())
@@ -871,6 +873,7 @@ void nades_CheckThrow()
 	entity held_nade = self.nade;
 	if (!held_nade)
 	{
+		self.nade_altbutton = true;
 		if(time > self.nade_refire)
 		{
 			Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE_THROW);
@@ -880,6 +883,7 @@ void nades_CheckThrow()
 	}
 	else
 	{
+		self.nade_altbutton = false;
 		if (time >= held_nade.nade_time_primed + 1) {
 			makevectors(self.v_angle);
 			float _force = time - held_nade.nade_time_primed;
@@ -956,6 +960,8 @@ MUTATOR_HOOKFUNCTION(nades_PlayerPreThink)
 {SELFPARAM();
 	if (!IS_PLAYER(self)) { return false; }
 
+	if (self.nade && self.offhand != OFFHAND_NADE) OFFHAND_NADE.offhand_think(OFFHAND_NADE, self, self.nade_altbutton);
+
 	if(IS_PLAYER(self))
 	{
 		if ( autocvar_g_nades_bonus && autocvar_g_nades )
@@ -1054,7 +1060,7 @@ MUTATOR_HOOKFUNCTION(nades_PlayerSpawn)
 
 	self.nade_timer = 0;
 
-	self.offhand = OFFHAND_NADE;
+	if (!self.offhand) self.offhand = OFFHAND_NADE;
 
 	if(self.nade_spawnloc)
 	{
diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc
index 22dbac3328..6defa736f6 100644
--- a/qcsrc/server/teamplay.qc
+++ b/qcsrc/server/teamplay.qc
@@ -304,8 +304,6 @@ string getwelcomemessage(void)
 		modifications = strcat(modifications, ", Low gravity");
 	if(g_cloaked && !g_cts)
 		modifications = strcat(modifications, ", Cloaked");
-	if(g_grappling_hook)
-		modifications = strcat(modifications, ", Hook");
 	if(g_weapon_stay && !g_cts)
 		modifications = strcat(modifications, ", Weapons stay");
 	if(g_jetpack)
@@ -325,9 +323,6 @@ string getwelcomemessage(void)
 	if(modifications != "")
 		s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n");
 
-	if (g_grappling_hook)
-		s = strcat(s, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
-
 	if (cvar("g_nades"))
 		s = strcat(s, "\n\n^3nades^8 are enabled, press 'g' to use them\n");
 
diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc
index f145799c1a..7b5eb25ef4 100644
--- a/qcsrc/server/weapons/selection.qc
+++ b/qcsrc/server/weapons/selection.qc
@@ -24,8 +24,10 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain)
 	if(time < self.hasweapon_complain_spam)
 		complain = 0;
 
-	if(wpn == WEP_HOOK.m_id && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
-		complain = 0;
+	// ignore hook button when using nades without hook
+	if (autocvar_g_nades && cl.offhand != OFFHAND_HOOK)
+	if (wpn == WEP_HOOK.m_id && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
+	    complain = 0;
 
 	if(complain)
 		self.hasweapon_complain_spam = time + 0.2;
diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc
index 20f4970543..56a92bb779 100644
--- a/qcsrc/server/weapons/weaponsystem.qc
+++ b/qcsrc/server/weapons/weaponsystem.qc
@@ -768,12 +768,7 @@ void W_WeaponFrame()
 		v_up = up;
 
 		{
-			bool key_pressed;
-        	if (g_grappling_hook || client_hasweapon(self, WEP_HOOK.m_id, false, false) || (weaponsInMap & WEPSET_HOOK))
-        		key_pressed = self.button16; // if hook is enabled, use an alternate key
-        	else
-        		key_pressed = self.BUTTON_HOOK;
-
+			bool key_pressed = self.BUTTON_HOOK;
         	entity e = self.offhand;
         	if (e.offhand_think) e.offhand_think(e, self, key_pressed);
         }
-- 
2.39.5