Quake Style - Quake 3 Tutorials
Game Enhancements - Willi's Q3 Offhand Grappling Hook
Converts the existing "weapon" grapple into an offhand hook!

Willi's Q3A Offhand Grappling Hook v1.0:

Please give credit where credit is due.

First, these changes will effectively nullify the original grappling hook made by Zoid in the Q3A source code. You will NOT be able to use the grappling hook as a normal "on hand" weapon - this converts the weapon to offhand permanently, unless you want to copy/rename numerous functions.

Files to be modified from the game source code:

g_local.h
g_active.c
g_items.c
g_missile.c
g_weapon.c

First open up g_local.h and go down to the gclient_s structure (roughly line 230). Near the fireHeld qboolean add this:
 
	// Willi - Offhand Grappling Hook
	qboolean hookhasbeenfired;


We'll be using that later when we modify g_active.c

Now open up g_active.c. Right at the top after the #include for g_local.h add this prototype:
 
// Willi - Offhand Grappling Hook
void Weapon_GrapplingHook_Fire (gentity_t *ent);


Next find the definition for the ClientThink_real( gentity_t *ent ) function, then inside of that function, find these lines:
 
	// Let go of the hook if we aren't firing
	if ( client->ps.weapon == WP_GRAPPLING_HOOK &&
	client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) {
		Weapon_HookFree(client->hook);
	}


Comment ALL of the above lines out! What we just did is change how the hook is released. Before, the hook wouldn't be released unless you had the actual "weapon" in hand and weren't holding down the +attack key. Now it just relies on there being an active hook and the fireHeld qboolean, which we'll do next.

Still in g_active.c, find these lines:
  
	if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
		client->fireHeld = qfalse; // for grapple
	}
 

Comment them ALL out! Since the offhand hook isn't an actual weapon then it won't be toggling the EF_FIRING flag. We don't want the hook to detach itself prematurely, now do we? Just below the above code that you commented out or removed, add this whole section:
  
	// Willi - Offhand Grappling Hook
	if ( (pm.cmd.buttons & 32)  &&
		ent->client->ps.pm_type != PM_DEAD &&
		!ent->client->hookhasbeenfired)	{
		Weapon_GrapplingHook_Fire( ent );
		ent->client->hookhasbeenfired = qtrue;
	}
	if ( !(pm.cmd.buttons & 32)  &&
		ent->client->ps.pm_type != PM_DEAD &&
		ent->client->hookhasbeenfired &&
		ent->client->fireHeld)	{
		ent->client->fireHeld = qfalse;
		ent->client->hookhasbeenfired = qfalse;
	}
	if ( client->hook && client->fireHeld == qfalse )
		Weapon_HookFree(client->hook);
 

This handles how the hook is fired. The (cmd.buttons & 32) is actually an unused command left by id Software. It corresponds to +button5. You can also use (cmd.buttons & 64) which corresponds to +button6. When you press the key bound to +button5 the hook will fire, and if you release it, the hook will release. Pretty swanky, eh?

Now open up g_item.c. Since we're going to be using some of the models and effects for the grappling hook, and no maps have the actual weapon, we need to precache it. We also need to precache the lightning gun so the grappling "rope" is also loaded. Find the void ClearRegisteredItems( void ) function and add these to the existing list of weapons to be registered:

	// Willi - Offhand Grappling Hook
	RegisterItem( BG_FindItemForWeapon( WP_LIGHTNING ) );
	RegisterItem( BG_FindItemForWeapon( WP_GRAPPLING_HOOK ) );


Next lets open up g_weapon.c. Find the definition for the Weapon_GrapplingHook_Fire (gentity_t *ent)function. Add this to the top of the function, above the first if statement:
 
	// Willi - Offhand Grappling Hook
	AngleVectors (ent->client->ps.viewangles, forward, right, up);
	CalcMuzzlePoint ( ent, forward, right, up, muzzle );


Since we're calling the offhand grappling hook from a command, and not from firing a weapon, we need to figure out where the player is pointing.

Next open up g_missile.c and go to the G_RunMissile( gentity_t *ent ) function. Find these lines:

			if (ent->parent && ent->parent->client->hook == ent)
				ent->parent->client->hook = NULL;


Replace them with these:

			// Willi - Offhand Grappling Hook
			if (ent->parent && ent->parent->client->hook == ent)
			{
				ent->parent->client->hook = NULL;
				ent->parent->client->hookhasbeenfired = qfalse;
				ent->parent->client->fireHeld = qfalse;
			}


If the hook hits the sky this resets the variables, so that the next time you hit your bound key, it fires the hook again.

That should be it. Give it a run through with MSVC, then go for the game.bat and the qvm. If there are problems, I've probably forgotten something, and I apologize in advance. The command will be
  +button5
unless you decide to use the other button, in which case it's
 +button6
Remember, the normal hook "weapon" won't work correctly after doing this modification. It feels nice to have the first offhand grapplng hook for Q3A :-)

Enjoy!

-- Credits:
   Tutorial by Willi
   Return to QS Tutorials

-- Important:
   If you do use something from QuakeStyle in your mod, please give us credit.
   Our code is copyrighted, but we give permission to everyone to use it in any way they see fit, as long as we are recognized.