PDA

View Full Version : Recoil tutorial


Commodus
07-30-2003, 05:07 PM
Hiya,
I thought since I am probably going to move to HL2, I might provide some tutorials for people. This one will tell you how to add recoil.

I haven't tested the code yet BTW, because I moved recently and the JK2 CD has yet to arrive. However, it did compile to QVM without errors, so that should indicate it works...

Theory/Concept/Introduction
Well it isn't really theory, but just an outline of what's meant to happen. Basically, the longer you fire the higher the recoil gets. Then the recoil will be used to calculate a new direction vector for the blaster shot or repeater bolt or whatever.

Now, I'm not exactly sure how to measure a space of time between shots, so we can't use time (besides, time can cause problems). However, what we can do is set up a counter, to which we will add every time we fire a shot. We can also set up code which can check every client frame if the player is not firing so the recoil counter can decrease. In a whole other place, we can check a client's recoil and use this to change the direction of the projectile.

Also, the way the code is set up allows you to do stuff with the recoil counter in cgame (for instance, you can use the recoil information to make a heat display, so you can see if your weapon is overheating - like in RTCW).

bg_public.h

Our first change will involve the stats index. Find statIndex_t (at line 312) - it should look like this: (provided you haven't changed it beforehand)

typedef enum {
STAT_HEALTH,
STAT_HOLDABLE_ITEM,
STAT_HOLDABLE_ITEMS,
STAT_PERSISTANT_POWERUP,
STAT_WEAPONS, // 16 bit fields
STAT_ARMOR,
STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?)
STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?)
STAT_MAX_HEALTH, // health / armor limit, changable by handicap
} statIndex_t;

What we need to add is another index. So below "STAT_MAX_HEALTH", add "STAT_RECOIL," without the inverted commas. The code should look like this:

typedef enum {
STAT_HEALTH,
STAT_HOLDABLE_ITEM,
STAT_HOLDABLE_ITEMS,
STAT_PERSISTANT_POWERUP,
STAT_WEAPONS, // 16 bit fields
STAT_ARMOR,
STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?)
STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?)
STAT_MAX_HEALTH, // health / armor limit, changable by handicap
STAT_RECOIL, // COMMODUS - the recoil index.
} statIndex_t;

Now that we are done with bg_public.h, we need to go into bg_pmove.c.

bg_pmove.c

When you have opened bg_pmove.c, go to line 3709. You are now inside the PM_Weapon function, which handles weapon events and does things like decreasing the ammo of the player when he fires. This part of bg_pmove.c does exactly that (take away the appropriate ammo amount) so we will add an extra line of code here which will increment the recoil counter. After adding the code needed it should look like this:

if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
{
// enough energy to fire this weapon?
if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0)
{
pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
pm->ps->stats[STAT_RECOIL]++; // COMMODUS HERE - this increments the recoil when the ammo is taken away
}
else // Not enough energy
{
// Switch weapons
if (pm->ps->weapon != WP_DET_PACK || !pm->ps->hasDetPackPlanted)
{
PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
if (pm->ps->weaponTime < 500)
{
pm->ps->weaponTime += 500;
}
}
return;
}
}

Quite simple, eh? Now, we need to go to g_weapons.c, where we can play with the direction of bullets.

g_weapons.c

Now, go to, for instance, the firing function for the blaster, WP_FireBlaster(). It is at line 379 and should look something like this:

static void WP_FireBlaster( gentity_t *ent, qboolean altFire )
//---------------------------------------------------------
{
vec3_t dir, angs;

vectoangles( forward, angs );

if ( altFire )
{
// add some slop to the alt-fire direction
angs[PITCH] += crandom() * BLASTER_SPREAD;
angs[YAW] += crandom() * BLASTER_SPREAD;
}

AngleVectors( angs, dir, NULL, NULL );

// FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
WP_FireBlasterMissile( ent, muzzle, dir, altFire );
}


Now, right under the last } of the if (altFire) {... tree, add:

angs[PITCH] += ent->client->ps.stats[STAT_RECOIL]; //COMMODUS HERE - recoil stuff. added to blaster only (for now).


What this does is add the amount in the recoil counter to pitch angle, which should kick the bullets up. If it instead kicks them down, change the += to -=. I still haven't tested the code so I'm not sure if it will give the desired effect...

Almost there - go into g_active.c now.

g_active.c

Go to line 1000 - this is the beginning of a function called ClientThink_real(). The thing about it which shines is that it is called at regular intervals (every client frame if g_synchronousclients is 0, every server frame if g_synchronousclients is 1 I think) so you can do stuff with it like poison effects or other timed things. What we're going to do is check if the player is not firing and if the recoil counter is over zero so it can be decreased.

Right under ucmd = &ent->client->pers.cmd; add the following if-conditional:

//COMMODUS HERE - this decreases the recoil if the player is not firing
if (!(client->pers.cmd.buttons & (BUTTON_ATTACK | BUTTON_ALT_ATTACK)) && client->ps.stats[STAT_RECOIL] > 0) {
--client->ps.stats[STAT_RECOIL];
}

Congratulations! You are finished now. Although the code is not perfect, it gives you a good place to start from. Feel free to experiment and do different things with it.

Final Notes
Like I said before, I do not have JK2 yet so I cannot guarantee it will work, but the code did compile without any errors so that should assure that it will work to some extent.

You might need to play around with times because if the recoil decreases every client frame the client is not firing, the recoil might decrease too quickly.

When you compile, you have to compile BOTH cgame and game modules because changes were made in bg_ files (both games files). You might have problems otherwise.

Hope this helps someone,
Commodus

razorace
07-30-2003, 11:56 PM
I see some issues with this. First off, as is, it's probably not going to work.

Having the clientside decrease the counter is only going to cause problems. There's no debounce and the client side PM data a nonlinked copy of the server side data. Depending on exactly where you placed the recoil counter, it will either do nothing or the recoil will increase to infinite.

In addition, it doesn't account for weapon switching.

However, it's very fixable and still a good starting point tutorial for anyone that wants to add recoil effects to the game. :)

Thanks Commodus.

On another note, why have you decided to move to the HL2 engine?

Commodus
08-04-2003, 01:52 AM
How would you fix it, exactly?

And as for moving to HL2, well... I'm quite enthralled by the idea of physics and materials. But don't worry, HL2 won't be released tomorrow, or next week... I hope to try one last project in time for moddb's mod fest event...

Emon
08-04-2003, 04:28 AM
I want to edit HL2, but since most of my work is Star Wars based, I am sticking to JA. Half the work is done creating the SW resources, plus mods won't get shut down like they might for HL2.

I'm much more impressed with HL2's animation and materials than anything else. Other games have environments just as big and detailed, and have physics just as good. However they didn't hire a professional for the facial expressions.

razorace
08-04-2003, 06:35 AM
Well, while I agree that HL2 looks impressive, the proof is in the pudding. And we haven't seen the pudding yet. :)