
Quake DeveLS - Burst Fire MG
Author: Bruce Knepper
Difficulty: Easy/Medium
For all of
you machine gun lovers out there, here's a way to make it so that you can fire
in burst mode, i.e. for one pull of the trigger you get 3 consecutive bullets.
To start out, we'll define
some variables. First we'll define a variable called fire_mode in the
persistent data section of client structure. This variable will tell us if we
are currently in burst fire (BF) mode or fully automatic (FA). In g_local.h,
head on down to line 743 and modify it to look like this:
int max_slugs; gitem_t *weapon; + qboolean fire_mode; // Muce: 0 for FA and 1 for BF } client_presistant_t
Were also
going to need a integer to keep track of where we are in the firing sequence
when using BF. Now go to line 809 and add this variable:
int machinegun_shots; // for weapon rasing+ int burstfire_count; // Muce: to keep track of BF
And of
course we will initialize fire_mode in p_client.c. Go to line 256 and add this:
client->pers.max_slugs = 50;+ client->pers.fire_mode = 0; // Muce: initialize to FA}
Good. Now
to make the function to switch between FA and BF. Open up g_cmds.c and goto
line 588. You want to insert this function after Cmd_Wave_f (); and before
ClientCommand(). This is the function:
+/*+=================+Cmd_FireMode_f+MUCE: new function for adjusting firing mode+=================+*/+void Cmd_FireMode_f (edict_t *ent)+ {+ int i;+ i=ent->client->pers.fire_mode;+ switch (i)+ {+ case 0:+ ent->client->pers.fire_mode=1;+ gi.cprintf(ent,PRINT_HIGH,"Burst Fire Mode\n");+ break;+ case 1:+ default:+ ent->client->burstfire_count=0;+ ent->client->pers.fire_mode=0;+ gi.cprintf(ent,PRINT_HIGH,"Fully Automatic Mode\n");+ break;+ }+ }
This
function is set up to toggle fire_mode every time it is called. You'll also
notice that I set burstfire_count to zero when I switch to FA, the reason for
this will be explained later. We also need to add this function to the list of
commands in ClientCommand(); so head on down to line 660 and modify it to look
like this:
else if (Q_stricmp (cmd, "wave") == 0) Cmd_Wave_f (ent); + // MUCE: added to call function for changeing fireing modes++ else if (Q_stricmp (cmd, "firemode") == 0)+ Cmd_FireMode_f (ent);
This way
all we have to do is type "cmd firemode" in the console and we will
toggle between FA and BF.
Before we dive into the
code for firing the machine gun, lets come up with a little strategy. We will
use burstfire_count to keep track of how many shots were already fired in our
burst and also to keep track of how long until we can fire another burst. We
will increment burstfire_count every frame. When burstfire_count is 0,1,2 we
will fire, and as long as we still are holding the fire button, burstfire_count
will keep getting incremented to 3,4,5,6 but during theses frames we won't be
firing. I do this to keep BF from looking like FA when we hold the fire button
down constantly.
Ok, so lets look at the
code now. Open up p_weapon.c and go to line 816:
if (!(ent->client->buttons & BUTTON_ATTACK)){ ent->client->machinegun_shots=0; ent->client->ps.gunframe++; return;}
When the
Machinegun_Fire() function is called (which I believe is every frame) this
first function checks to see whether or not the attack button is pressed. If it
isn't it returns out of the function. For our new duel mode machine gun, we
must change it to this:
if (!(ent->client->buttons & BUTTON_ATTACK) && ( (ent->client->burstfire_count > 2) || (!ent->client->burstfire_count ) ) ){ ent->client->machinegun_shots=0; ent->client->burstfire_count=0; ent->client->ps.gunframe++; return;}
What this
says is that we will not end the firing sequence unless the attack button is
not depressed and either one of two things: 1. burstfire_count > 2 (we have
fired 3 shots) or 2. burstfire_count = 0; (we are idle or in FA mode).
Then next little bit of
code after that if statement is used to cycle the gun frames while firing to
give the look of kickback on the machine gun:
if (ent->client->ps.gunframe == 5) ent->client->ps.gunframe = 4; else ent->client->ps.gunframe = 5;
This is
fine, except that we only want the gun to be moving when it's firing, i.e. when
burstfire_count = 0, 1, 2. So we just add a few lines like so:
+if (ent->client->burstfire_count < 3)+ { if (ent->client->ps.gunframe == 5) ent->client->ps.gunframe = 4; else ent->client->ps.gunframe = 5;+ }
As we
continue to the next little code segment we find a little routine to handle when
the gun runs out of ammo. The only thing needed here is to reset
burstfire_count when we run out of ammo, so just add this line at about line
838:
ent->pain_debounce_time = level.time + 1; }+ ent->client->burstfire_count=0; NoAmmoWeaponChange (ent); return;
Good, on
to the next piece of code we need to look at ( about line 858 ):
// raise the gun as it is firing if (!deathmatch->value) { ent->client->machinegun_shots++; if (ent->client->machinegun_shots > 9) ent->client->machinegun_shots = 9; }
This
little bit of code raises the gun when your firing fully auto. Since were only
firing three round bursts, we don't want the gun to raise as long as were in BF
mode so we just add another equation to the if statement:
// raise the gun as it is firing if (!deathmatch->value && !ent->client->pers.fire_mode) { ent->client->machinegun_shots++; if (ent->client->machinegun_shots > 9) ent->client->machinegun_shots = 9; }
Now for
the big change! Around line 870 we have this segment of code:
fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD); gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_MACHINEGUN | is_silenced); gi.multicast (ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
This is
the code that fires the gun and broadcasts it to the rest of the world. We need
to change it to this:
switch (ent->client->pers.fire_mode) { // Fire Burst Fire case 1: ent->client->burstfire_count++; if (ent->client->burstfire_count < 4) {fire_bullet (ent, start, forward, damage*2, kick/2, DEFAULT_BULLET_HSPREAD/2, DEFAULT_BULLET_VSPREAD/2); gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_MACHINEGUN | is_silenced); gi.multicast (ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity; } else if (ent->client->burstfire_count > 6) ent->client->burstfire_count=0; break; // Fire Fully Automatic case 0: default:fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD); gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_MACHINEGUN | is_silenced); gi.multicast (ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity; break; }
I told you
it was a big change. Here's what it does. If we are in FA mode, it simply fires
the gun using the exact same code as the original did. If we are in BF mode, we
first increment burstfire_count. Next we check to see if we are on the first,
second, or third round of our three round burst. We do this by checking if
burstfire_count < 4 ( we use 4 because we just stepped burstfire_count in
the previous line), and if it is, we fire a round. Notice I've increased the
damage and reduced the kick and spread while in BF. This is to make up for the
fact that were firing half as often in BF mode. If burstfire_count is 3,4,5,6
(before we incremented it) then we do nothing. If burstfire_count > 6 then
we reset it to zero so we can start another burst on the next run through.
And that's it... compile
it and try it out. You might want to mess with the amount of damage bursts do,
or how long the wait is between bursts to get it to your liking. The idea for
this patch came from my friend Ted who liked using the machinegun in
deathmatch, but it turns out that it's pretty fun to use in a coop game as
well. One burst will easily take out those pesky machinegun guards.
If you have any questions,
or would like some help modifying this patch, or if you want my original source
code, please fell free to contact me at brucknep@glue.umd.edu.
|
This site, and all
content and graphics displayed on it, |