In this tutorial you're going to get to add new keys to the game, in a
similar vein to +attack and +jump. In order to do this, there are three
main things need doing:
Myself, I'm going to add a secondary fire and a crouch button, and
re-enable the use button (which is a special case as some of the code's
still lying about)
kbutton_t in_secattack, in_crouch; If you scroll down from the kbutton_t definitions, you'll see two functions, KeyUp and KeyDown, followed by a whole group of little routines calling them, an up and down pair for each routine. At the bottom of them, add: void IN_SecAttackDown(void) {KeyDown(&in_secattack);} void IN_SecAttackUp(void) {KeyUp(&in_secattack);} void IN_CrouchDown(void) {KeyDown(&in_crouch);} void IN_CrouchUp(void) {KeyUp(&in_crouch);} Scroll down to CL_InitInput, and add the following lines: Cmd_AddCommand ("+secattack", IN_SecAttackDown); Cmd_AddCommand ("-secattack", IN_SecAttackUp); Cmd_AddCommand ("+crouch", IN_CrouchDown); Cmd_AddCommand ("-crouch", IN_CrouchUp); The next stage is nice and easy, as we only need to alter one function, CL_SendMove - which is conveniently also resident in cl_input.c. This is the routine that puts together the player move message each frame. This message consists of a timestamp (for ping calculation), the player's viewing angle (for shooting purposes, mostly), the three components of the player's intended direction of movement, a bitfield containing the state of the fire and jump buttons, and finally the player's impulse for that frame. For the sake of compatability we'd like to keep this structure, so the obvious place to stick our new button data is in the bitfield. As you read through the function, you'll come across this little section: // // send button bits // bits = 0; if ( in_attack.state & 3 ) bits |= 1; in_attack.state &= ~2; if (in_jump.state & 3) bits |= 2; in_jump.state &= ~2; You may notice that this is the first time in our code that there's been no reference to the use button - so it's time to start fixing things! Add this code below the section I listed above: if ( in_use.state & 3 ) bits |= 4; if ( in_secattack.state & 3 ) bits |= 8; in_secattack.state &= ~2; if ( in_crouch.state & 3 ) bits |= 16; in_crouch.state &= ~2; Open up sv_user.c, and look for the function SV_ReadClientMove. This function is the opposite of CL_SendMove - it reads in the data and updates the player entity accordingly. Now we have a second choice - where do we put the new data? In order to avoid adding new fields, I decided to turn .button0 (the attack button) and .button2 (the jump button) into bitfields, and put use into .button1 where it always should've been (unfortunately this breaks some mods which used it as a spare field - I'll leave you to implement an option to circumvent this). First of all, take a look at the existing code: // read buttons bits = MSG_ReadByte (); host_client->edict->v.button0 = bits & 1; host_client->edict->v.button2 = (bits & 2)>>1; if (bits & 1) host_client->edict->v.button0 = 1; else host_client->edict->v.button0 = 0; if (bits & 2) host_client->edict->v.button2 = 1; else host_client->edict->v.button2 = 0; Anyway, here's the replacement code: // read buttons //bits layout: //attack 1 //jump 2 //secattack 4 //crouch 8 //use 16 //grapple 32 //grapple_in 64 //grapple_out 128 bits = MSG_ReadByte (); host_client->edict->v.button0 = bits & 1; host_client->edict->v.button2 = (bits & 2)>>1; host_client->edict->v.button0 += (bits & 4)>>1; host_client->edict->v.button2 += (bits & 8)>>2; host_client->edict->v.button1 = (bits & 16)>>4; button0: attack 1 secattack 2 button1: use 1 button2: jump 1 crouch 2 So why do all this bit-twiddling when a bunch of if statements will do? The short answer is there's no really good reason in this situation. The longer one is that when it comes to optimising code on modern processors, Branches Are Bad - they mess up pipelining something nasty. It's quite possible that John Carmack writes bit-twiddling over branches instinctively by now :-) If you find yourself writing speed-critical loops then bear this in mind... Finally, you'll want to actually *do* something with all these buttons. Within your QuakeC code, you check whether or not a button is down like this: if (self.button0 & 1) //checks attack if ((self.button0 & 1) && (self.button0 & 2)) //checks for both attacks Anyway, you should now have some new buttons in your engine - so arise and free yourself from the tyranny of impulses! |