Quake DeveLS - THROW-UP

Author: SumFuka
Difficulty: Medium


My mate Jarlaxe, throwing up his guts in dm6.
(this screenshot actually includes gibs, from tutorial 8...)

Lets do something fun ! Let's learn how to add a new action that our quake character can do. Let's also throw some blood around and play some sounds. Hell, let's just throw up all over the place.

A new command

Open up g_cmds.c and look at the at the very top of the file. Add a #include "throwup.h" line after the other #include lines. Now go to line 88 in the Cmd_give_f function and add these lines so that the function starts like this :

void Cmd_Give_f (edict_t *ent)
{
	char		*name;
	gitem_t		*it;
	int			index;
	int			i;
	qboolean	give_all;


	// STEVE added this bit.

	if (Q_stricmp(gi.argv(1), "throwup") == 0)
	{
		// throw up !
		ThrowUpNow (ent);
		return;
	}

	...
Now let's create the blueprint for what we are going to program : create a new text file called throwup.h in the same directory as all the other .c and .h files... enter these few lines :

// THROWUP.H

// main function
void ThrowUpNow(edict_t *self);
As you can see, the blueprint is very simple, and we are only defining one new function. Create our new source file, throwup.c and enter the following code fragments :
// THROWUP.C by SumFuka

#include "g_local.h"
#include "throwup.h"


// this function makes you throw up
void ThrowUpNow(edict_t *self)
{
	// use some local vector variables to work with
	vec3_t	forward, right;
	vec3_t	mouth_pos, spew_vector;
	float rnum;
	int i;
This is the top of our c file, and we have first included g_local.h (the game blueprint) and throwup.h (the throwup blueprint). Next we have defined our ThrowUpNow function, which takes an entity (self) as it's only parameter. Some local variables are declared at the top of the function.

	// set the spew vector, based on the client's view angle
	AngleVectors (self->client->v_angle, forward, right, NULL);
The AngleVectors function sets vectors for us that are directly forward, and directly to the right of where we are facing in the game (the client vangle is an input parameter to this function, forward and right are output parameters. NULL means that we are not supplying a forth parameter).

	// Make the spew originate from our mouth
	VectorScale (forward, 24, mouth_pos);
	VectorAdd (mouth_pos, self->s.origin, mouth_pos);
	mouth_pos[2] += self->viewheight;
Next we define a mouth_pos vector, which is 24 units forward of our player's origin, and at their viewheight (i.e. the middle of the screen from the player's viewpoint).

	// Make the spew come forwards out of our mouth.
	VectorScale (forward, 24, spew_vector);
Above we have made a 'spew vector' that is travelling directly forwards from us, at a speed of 24 units per timeframe.
	// BLOOD ! (copied from SpawnDamage function)
	gi.WriteByte (svc_temp_entity);
	gi.WriteByte (TE_BLOOD);
	gi.WritePosition (mouth_pos);
	gi.WriteDir (spew_vector);
	gi.multicast (mouth_pos, MULTICAST_PVS);
Next we have created a 'temporary entity' of blood. Temporary entities are the little effects you can see in the game such as blood sprays, blaster sparks (when they hit a wall) or bullet marks on the walls. The communications protocol for creating a temporary entity in the game world is this :
  1. Transmit the 'svc_temp_entity' code to all clients
  2. Transmit the type of temp entity (blood/sparks/whatever)
  3. Transmit the origin
  4. Transmit it's direction of movement
  5. (I'm not sure what the multicast does !)
Have a look in the function SpawnBlood, that's where I copied the protocol from.

	// say something cool
	rnum = random();
	if (rnum < 0.2)
		gi.bprintf (PRINT_MEDIUM, "Retch !\n");
	else if (rnum < 0.4)
		gi.bprintf (PRINT_MEDIUM, "...Vomit...\n");
	else if (rnum < 0.6)
		gi.bprintf (PRINT_MEDIUM, "Chunder.\n");
	else if (rnum < 0.8)
		gi.bprintf (PRINT_MEDIUM, "Chuck. chuck. chuck.\n");
	else 
		gi.bprintf (PRINT_MEDIUM, "Hmmmmff hmmmf hhhuuuuuuurrrrrllll.\n");
The code above randomly chooses something to say....

	// make a painful sound
	rnum = random();
	if (rnum < 0.125)
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "gurp1"), 1, ATTN_NORM, 0);
	else if (rnum < 0.25)
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "gurp2"), 1, ATTN_NORM, 0);
	else if (rnum < 0.375)
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "pain50_1"), 1, ATTN_NORM, 0);
	else if (rnum < 0.5)
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "pain50_2"), 1, ATTN_NORM, 0);
	else if (rnum < 0.625)
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "pain75_1"), 1, ATTN_NORM, 0);
	else if (rnum < 0.75)
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "pain75_2"), 1, ATTN_NORM, 0);
	else if (rnum < 0.875)
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "pain100_1"), 1, ATTN_NORM, 0);
	else
		gi.sound (self, CHAN_BODY, SexedSoundIndex(self, "pain100_2"), 1, ATTN_NORM, 0);	
We randomly make a painful sound here. The sexed sound index will choose the correct sound for the player's sex. For example, "player/male/gurp1.wav" or "player/female/gurp1.wav". We are playing the pain sound on the 'BODY' sound channel. Here are all the sound channels (from g_shared.h, don't type this bit in) :

#define CHAN_AUTO 0
#define CHAN_WEAPON 1
#define CHAN_VOICE 2
#define CHAN_ITEM 3
#define CHAN_BODY 4

You can actually play sounds on any channel up to 7, I believe. A sound played on the same channel as a previous sound will stop that sound... to 'mix' sounds you must play them on different channels, or use CHAN_AUTO which will try to find a free channel (but will not play it if no channels are free).


	// also do a spewing sound
	gi.sound (self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
}
That's the end of throwup.c, save it now ! The last like makes a sound that sounds very chunder-like. Notice how it is on a different sound channel to the previous pain sound, so they should be heard together ok.

Vomit, Chuck, Hurl.

To use this mod, compile the gamex86.dll and go into quake2. Now type this at the console :

	bind h "give throwup"
Now hit h and run around throwing up all over your friends...

Next week, let's actually cough up some gibs !

Tutorial by SumFuka



This site, and all content and graphics displayed on it,
are ©opyrighted to the Quake DeveLS team. All rights received.
Got a suggestion? Comment? Question? Hate mail? Send it to us!
Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on 800*600.
Thanks to Planet Quake for there great help and support with hosting.
Best viewed with Netscape 4 or IE 3