Alternate Fire Tutorial.
Code Tutorial - by Zygote (HUMAN DEBRIS)

What the hell is "Alternate Fire"?
Alternate Fire means have a secondary fire button. This feature is common in other FPS games like Unreal Tournament and Half-Life. This tutorial will will take you through the steps involved in adding alternate fire to your Quake3 modification.

Note: I am not the worlds greatest programmer by a long shot, so don't go crazy if you see a bit of bad programming in this tutorial. I would appreciate any suggested improvements or corrections you can offer. The code has been tested extensively and works fine.

In doing this tutorial I assume you know how to compile the source, and have played with the code before.

Okay, on with the tutorial. This is a client and server side modification.

Files to be modified
bg_pmove.c
g_active.c
bg_public.h
g_local.h
g_weapon.c
cg_event.c
cg_local.h
cg_weapons.c

bg_pmove.c
Go down and find the PM_Weapon() function.
Locate the following lines:
if ( ! (pm->cmd.buttons & 1) ) {
  pm->ps->weaponTime = 0;
  pm->ps->weaponstate = WEAPON_READY;
  return;
 }
This code determines whether the attack button is being pressed, if it isn't, the weapon is made ready and return.

Change the if statement to:
if ( ! ((pm->cmd.buttons & 1) ||( pm->cmd.buttons & 32) )) {
  pm->ps->weaponTime = 0;
  pm->ps->weaponstate = WEAPON_READY;
  return;
 }
This will check both the attack button (+attack) and our new alt attack (or alt fire button) +button5.

Go down a few lines and find the following code:
 // fire weapon
 PM_AddEvent( EV_FIRE_WEAPON );

 switch( pm->ps->weapon ) {
 default:
 case WP_GAUNTLET:
  addTime = 400;
  break;
 case WP_LIGHTNING:
  addTime = 50;
  break;
 case WP_SHOTGUN:
  addTime = 1000;
  break;
 case WP_MACHINEGUN:
  addTime = 100;
  break;
 case WP_GRENADE_LAUNCHER:
  addTime = 800;
  break;
 case WP_ROCKET_LAUNCHER:
  addTime = 800;
  break;
 case WP_PLASMAGUN:
  addTime = 100;
  break;
 case WP_RAILGUN:
  addTime = 1500;
  break;
 case WP_BFG:
//  addTime = 100;
  addTime = 200;
  break;
 case WP_GRAPPLING_HOOK:
  addTime = 400;
  break;
 }

We are going to write an if to determine which attack button is being pressed. Based on the key that is being pressed, we are going to call a different event. Change the above code to:
// Normal Attack Button
if (pm->cmd.buttons & 1) {

  // Normal Fire Event
  PM_AddEvent( EV_FIRE_WEAPON );

  switch( pm->ps->weapon ) {
  default:
  case WP_GAUNTLET:
   addTime = 400;
   break;
  case WP_LIGHTNING:
   addTime = 50;
   break;
  case WP_SHOTGUN:
   addTime = 1000;
   break;
  case WP_MACHINEGUN:
   addTime = 100;
   break;
  case WP_GRENADE_LAUNCHER:
   addTime = 800;
   break;
  case WP_ROCKET_LAUNCHER:
   addTime = 800;
   break;
  case WP_PLASMAGUN:
   addTime = 100;
   break;
  case WP_RAILGUN:
   addTime = 1500;
   break;
  case WP_BFG:
 //  addTime = 100;
   addTime = 200;
   break;
  case WP_GRAPPLING_HOOK:
   addTime = 400;
   break;
  }

 // New Alt Fire Button
 } else if (pm->cmd.buttons & 32) {

  // New Event
  PM_AddEvent( EV_FIRE_WEAPON2 );

  switch( pm->ps->weapon ) {
  default:
  case WP_GAUNTLET:
   addTime = 400;
   break;
  case WP_LIGHTNING:
   addTime = 50;
   break;
  case WP_SHOTGUN:
   addTime = 1000;
   break;
  case WP_MACHINEGUN:
   addTime = 100;
   break;
  case WP_GRENADE_LAUNCHER:
   addTime = 800;
   break;
  case WP_ROCKET_LAUNCHER:
   addTime = 800;
   break;
  case WP_PLASMAGUN:
   addTime = 100;
   break;
  case WP_RAILGUN:
   addTime = 1500;
   break;
  case WP_BFG:
 //  addTime = 100;
   addTime = 200;
   break;
  case WP_GRAPPLING_HOOK:
   addTime = 400;
   break;
  }
 }

This is some pretty simple stuff. You can see that the above code is one big if statement checking which attack button is being pressed.

If the normal attack button is being pressed
if (pm->cmd.buttons & 1) {

And half way down. Else if the second fire button is being pressed
} else if (pm->cmd.buttons & 32) {

You will notice that it is one or the other. This stops both buttons from being pressed at once.
You will also notice that the same switch statement is used for both attack buttons. The reason is that the switch statement controls the addTime of each weapon. "AddTime" is the amount of time in ms before the weapon can be fired again. We want the switch statement twice because when we start adding our cool new alt fire functions we may want different rates of fire between first and secondary fire.

The important part to note with the above code is these two lines:
PM_AddEvent( EV_FIRE_WEAPON );
and
PM_AddEvent( EV_FIRE_WEAPON2 );

These lines are calling an event both in game (server side) and in cgame (client side). You will notice that that each fire mode is calling a different event. This is great for us because it means we can tell quake3 to do different things with the current weapon based on which attack button is being pressed.

One more quick modification to this file.
Go down a little more to PMoveSingle and find the following code:
// set the firing flag for continuous beam weapons
if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
  && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) {
  pm->ps->eFlags |= EF_FIRING;
 } else {
  pm->ps->eFlags &= ~EF_FIRING;
 }

This is just what the comment says: // set the firing flag for continuous beam weapons

We are going to make sure that our new attack button will also set the firing flag. Change the code to:
if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
  && ( (pm->cmd.buttons & BUTTON_ATTACK) || (pm->cmd.buttons & 32) )
  && pm->ps->ammo[ pm->ps->weapon ] ) {
  pm->ps->eFlags |= EF_FIRING;
 } else {
  pm->ps->eFlags &= ~EF_FIRING;
 }

g_active.c
Go down to the ClientEvents() function. Find the following lines of code:
case EV_FIRE_WEAPON:
   FireWeapon( ent );
   break;

The above code tells the server to call FireWeapon() which is in g_weapon.c.
Remember that each of the attack keys calls a different event. Now we are going to tell the server what to do when our new alt fire is pressed. Directly below the above code place the following:
case EV_FIRE_WEAPON2:
   FireWeapon2( ent );
   break;
This tells the server to call FireWeapon2() which does not exist yet.

Go down to ClientThink_real() function. Find the following line:
( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) {

and change it to:
( (ucmd->buttons & BUTTON_ATTACK) || (ucmd->buttons & 32) ) && client->ps.weaponTime <= 0 ) {

This makes the gauntlet work when we press the new alt fire button.

bg_public.h
We need to add our new event to the entity_event_t struct. Find the following line:
EV_FIRE_WEAPON,

Now directly below it place the following:
EV_FIRE_WEAPON2,

g_local.h
Go down and find:
void FireWeapon( gentity_t *ent );

Directly below the above line place this code:
void FireWeapon2( gentity_t *ent );

g_weapon.c
So far we have told quake3 to perform an event when we press alt fire. The event will trigger code in cgame (we haven't done this yet) and it will call FireWeapon2() in game. We are now going to make our FireWeapon2() function.

Go down to the bottom of the file and find the FireWeapon() function. Now directly below that function, place the following:
/*
===============
FireWeapon2
===============
*/
void FireWeapon2( gentity_t *ent ) {
 if (ent->client->ps.powerups[PW_QUAD] ) {
  s_quadFactor = g_quadfactor.value;
 } else {
  s_quadFactor = 1;
 }

 // track shots taken for accuracy tracking.  Grapple is not a weapon and gauntet is just not tracked
 if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) {
  ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++;
 }

 // set aiming directions
 AngleVectors (ent->client->ps.viewangles, forward, right, up);

 CalcMuzzlePoint ( ent, forward, right, up, muzzle );

 // fire the specific weapon
 switch( ent->s.weapon ) {
 case WP_GAUNTLET:
  Weapon_Gauntlet( ent );
  break;
 case WP_LIGHTNING:
  Weapon_LightningFire( ent );
  break;
 case WP_SHOTGUN:
  weapon_supershotgun_fire( ent );
  break;
 case WP_MACHINEGUN:
  if ( g_gametype.integer != GT_TEAM ) {
   Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
  } else {
   Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
  }
  break;
 case WP_GRENADE_LAUNCHER:
  weapon_grenadelauncher_fire( ent );
  break;
 case WP_ROCKET_LAUNCHER:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_PLASMAGUN:
  Weapon_Plasmagun_Fire( ent );
  break;
 case WP_RAILGUN:
  weapon_railgun_fire( ent );
  break;
 case WP_BFG:
  BFG_Fire( ent );
  break;
 case WP_GRAPPLING_HOOK:
  Weapon_GrapplingHook_Fire( ent );
  break;
 default:
// FIXME  G_Error( "Bad ent->s.weapon" );
  break;
 }
}
Make sure you leave one blank line at the bottom of the file (if not your compiler might get upset!)
The above code is a copy-paste of the FireWeapon() function, but with a small name change.

Why the hell bother? Why not tell the event to call FireWeapon() and give us less work to do?
The reason is because the switch statement in this code determines which weapon the player is firing, and calls the corresponding weapon_fire_function. We want two different copies because we can now write our own weapon_fire_functions in g_weapon and call them either when we press primary or secondary fire (giving us complete control over what each fire mode of each weapon does!!!).

That's it for server side code, lets move on to the client side.

cg_event.c
Go down and find the CG_EntityEvent() function. Find the following code:
case EV_FIRE_WEAPON:
  DEBUGNAME("EV_FIRE_WEAPON");
  CG_FireWeapon( cent );
  break;

Directly below the above code put the following:
case EV_FIRE_WEAPON2:
  DEBUGNAME("EV_FIRE_WEAPON2");
  CG_FireWeapon2( cent );
  break;

This is the code that is run on the client side when the EV_FIRE_WEAPON and EV_FIRE_WEAPON2 events are called.

cg_local.h
Go down and find the this line:
void CG_FireWeapon( centity_t *cent );

Now place this line directly below it:
void CG_FireWeapon2( centity_t *cent );

cg_weapons.c
In cg_event.c we told cgame to call the CG_FireWeapon2() function when the alt fire button is pressed.
Now we are going to create this function. Find this code:
/*
================
CG_FireWeapon

Caused by an EV_FIRE_WEAPON event
================
*/
void CG_FireWeapon( centity_t *cent ) {
 entityState_t *ent;
 int    c;
 weaponInfo_t *weap;

 ent = &cent->currentState;
 if ( ent->weapon == WP_NONE ) {
  return;
 }
 if ( ent->weapon >= WP_NUM_WEAPONS ) {
  CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" );
  return;
 }
 weap = &cg_weapons[ ent->weapon ];

 // mark the entity as muzzle flashing, so when it is added it will
 // append the flash to the weapon model
 cent->muzzleFlashTime = cg.time;

 // lightning gun only does this this on initial press
 if ( ent->weapon == WP_LIGHTNING ) {
  if ( cent->pe.lightningFiring ) {
   return;
  }
 }

 // play quad sound if needed
 if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) {
  trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound );
 }

 // play a sound
 for ( c = 0 ; c < 4 ; c++ ) {
  if ( !weap->flashSound[c] ) {
   break;
  }
 }
 if ( c > 0 ) {
  c = rand() % c;
  if ( weap->flashSound[c] )
  {
   trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, weap->flashSound[c] );
  }
 }

 // do brass ejection
 if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) {
  weap->ejectBrassFunc( cent );
 }
}

Now directly below it place:
/*
================
CG_FireWeapon2

Caused by an EV_FIRE_WEAPON2 event
================
*/
void CG_FireWeapon2( centity_t *cent ) {
 entityState_t *ent;
 int    c;
 weaponInfo_t *weap;

 ent = &cent->currentState;
 if ( ent->weapon == WP_NONE ) {
  return;
 }
 if ( ent->weapon >= WP_NUM_WEAPONS ) {
  CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" );
  return;
 }
 weap = &cg_weapons[ ent->weapon ];

 // mark the entity as muzzle flashing, so when it is added it will
 // append the flash to the weapon model
 cent->muzzleFlashTime = cg.time;

 // lightning gun only does this this on initial press
 if ( ent->weapon == WP_LIGHTNING ) {
  if ( cent->pe.lightningFiring ) {
   return;
  }
 }

 // play quad sound if needed
 if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) {
  trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound );
 }

 // play a sound
 for ( c = 0 ; c < 4 ; c++ ) {
  if ( !weap->flashSound[c] ) {
   break;
  }
 }
 if ( c > 0 ) {
  c = rand() % c;
  if ( weap->flashSound[c] )
  {
   trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, weap->flashSound[c] );
  }
 }

 // do brass ejection
 if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) {
  weap->ejectBrassFunc( cent );
 }
}

This code doesn't do much. It plays the fire sound for the weapons and a few other minor things. The point in creating a second version was to completely separate both primary and secondary fire modes.

When you start adding you cool new weapon_fire_functions, you will be spending time in this file (cg_weapon.c), getting your new sounds and effects working with the different fire modes. Having separate CG_FireWeapon functions makes life a lot easier.
 

That's It!
That is all there is to it.
Compile both game and cgame. To test out the modification, load up your newly created mod and start a map. To use your new alt fire, bring down the console and type:
\bind mouse2 +button5

This will bind mouse2 (mouse button 2) to +button5 (our new alternate fire). Press it, and it should fire each of the weapons normally.
You are now ready to add your own alternate fire functions.


Extra-Curricular Activity
You have got a working alternate fire, but you want to test it out.
We are going to change the alternate fire of all the weapons (except gauntlet and machine-gun) so that they all fire rockets.

g_weapon.c
Go down to our newly created FireWeapon2() function and replace it with the following:
/*
===============
FireWeapon2
===============
*/
void FireWeapon2( gentity_t *ent ) {
 if (ent->client->ps.powerups[PW_QUAD] ) {
  s_quadFactor = g_quadfactor.value;
 } else {
  s_quadFactor = 1;
 }

 // track shots taken for accuracy tracking.  Grapple is not a weapon and gauntet is just not tracked
 if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) {
  ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++;
 }

 // set aiming directions
 AngleVectors (ent->client->ps.viewangles, forward, right, up);

 CalcMuzzlePoint ( ent, forward, right, up, muzzle );

 // fire the specific weapon
 switch( ent->s.weapon ) {
 case WP_GAUNTLET:
  Weapon_Gauntlet( ent );
  break;
 case WP_LIGHTNING:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_SHOTGUN:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_MACHINEGUN:
  if ( g_gametype.integer != GT_TEAM ) {
   Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
  } else {
   Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
  }
  break;
 case WP_GRENADE_LAUNCHER:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_ROCKET_LAUNCHER:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_PLASMAGUN:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_RAILGUN:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_BFG:
  Weapon_RocketLauncher_Fire( ent );
  break;
 case WP_GRAPPLING_HOOK:
  Weapon_GrapplingHook_Fire( ent );
  break;
 default:
// FIXME  G_Error( "Bad ent->s.weapon" );
  break;
 }
}

All we have done is replace the normal weapon fire functions with the rocket launcher's fire function.

g_items.c
What we are about to do is change quake3 so that the Rocket Launcher is loaded by all clients when they enter any level.
This is really lazy, we are doing it just to make sure that the rocket model and smoke trail will work on levels that do not contain rocket launchers. It is a quick fix and allows us to fully demonstrate the alternate fire.

Find this function: ClearRegisteredItems(). Directly below this line:
RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) );

Place this line:
RegisterItem( BG_FindItemForWeapon( WP_ROCKET_LAUNCHER ) );

THE END
Compile the code and load it up, and the alternate fire for all the weapons will fire rockets.