
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 functionvoid 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 upvoid 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 :
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, |