TUTORIAL 11
- Vortex Grenades by AssKicka
This tutorial will
show you how to convert the grenade launcher's grenades into Vortex
Grenades. Vortex Grenades ? They are simply grenades that suck
players closer and when the player hits the grenade it
explodes.
First we need to
add a new utility function that can search the world to find a
target that will be sucked into our vortex grenade. Open g_utils.c
and add the bellow function to the end of the file : /*
================
findradius
================
*/
gentity_t *findradius (gentity_t *ent, vec3_t org, float rad) {
vec3_t eorg;
int j;
if (!ent)
ent = g_entities;
else
ent++;
for (; ent < &g_entities[level.num_entities]; ent++)
{
if (!ent->inuse)
continue;
for (j=0; j<3; j++)
eorg[j] = org[j] - (ent->r.currentOrigin[j] + (ent->r.mins[j] + ent->r.maxs[j])*0.5);
if (VectorLength(eorg) > rad)
continue;
return ent;
}
return NULL;
}
I did not code the
findradius function but it has been re-done many times from the q2
days. Lets see if we can disect this function. This function runs
through all the entities in a level and checks if any entities are
in the radius (rad) of the entity 'ent'. The 'ent' entity and 'rad'
float are passed to this function from the calling function, you
will see how this works in a second.
Now that our
findradius is complete we need to add the function prototype in
g_local.h. goto line 438 and add the code bellow : gentity_t *findradius (gentity_t *ent, vec3_t org, float rad);
Now we will add our
grenade think function, goto line 326 and add the following function
: /*
=================
G_Suck
=================
*/
static void G_Suck( gentity_t *self ) {
gentity_t *target;
vec3_t start,dir,end;
target = NULL;
while ((target = findradius(target, self->r.currentOrigin, 500)) != NULL) //check if there are any entity's within a radius of 500 units.
{
if (target == self) // target must not be vortex grenade
continue;
if (!target->client) // target must be a client
continue;
if (target == self->parent) // target must not be the player who fired the vortex grenade
continue;
if (!target->takedamage) // target must be able to take damage
continue;
VectorCopy(target->r.currentOrigin, start); // put target position in start
VectorCopy(self->r.currentOrigin, end); // put grenade position in end
VectorSubtract(end, start, dir); // subtract start from end to get directional vector
VectorNormalize(dir);
VectorScale(dir,200, target->client->ps.velocity); // scale directional vector by 200 and add to the targets velocity
VectorCopy(dir, target->movedir); // make targets move direction = to directional vector.
}
self->nextthink = level.time + 20;
if (level.time > self->wait) // check if vortext grenade is older than 20 seconds.
G_ExplodeMissile( self);
}
Now for the
explanation : The first thing we did is to create new three
dimensional vectors, nl. start,dir and end, we will use these
vectors when modifying the velocity of the affected entity. We also
created a new entity type called 'target', we will use this to
identify with the entity that will be sucked into the vortex
grenade. On line 12 you will see a while statement that checks if
target is NOT NULL after target is returned from the findradius
function (Rmember the findradius function we added earlier ?). If
target is NOT NULL (target has been found by the findradius
function) we then do some checks on what entity the target is. If
the target is itself we continue the while statement (search for new
target). If the target is NOT a client then search for new
target. If the target is the entity (player) that fired the
grenade then search for a new target. If the target can't be
damaged then search for a new target. If NONE of the above
conditions apply then we modify the velocity of the target to nudge
it towards the grenade. Let's look at how we did this : 1) On
line 26 we copied the target's current position into the 'start'
vector. 2) On line 27 we copied the grenade's current position
into the 'end' vector. 3) On line 28 we subtracted the 'start'
vector from the 'end' vector to get the directional (dir)
vector. 4) On line 30 we scaled the directional vector by 200 and
put the result into the target velocity, this is what makes the
target nudge closer to the grenade. 5) On line 31 we change the
players direction to match the directional vector. On line 35 we
make sure our G_Suck function is executed in .02 seconds from
now. On line 37 we check if the grenade has been in the world for
more than 20 seconds, if it has been we explode the vortext grenade,
we have achived this my making the grenades self->wait = to
20000.
We will now change
the fire_grenade function to use our G_Suck function as the grenades
think function. Find the 'fire_grenade' function and modify the
bellow code : bolt = G_Spawn();
bolt->classname = "grenade";
bolt->nextthink = level.time + 2500;
bolt->think = G_ExplodeMissile;
to look like
this bolt = G_Spawn();
bolt->classname = "grenade";
bolt->nextthink = level.time + 1000; // call G_Suck in 1 second
bolt->think = G_Suck;
bolt->wait = level.time + 20000; // vortext grenade lifetime.
All we have done
here is changed the think function of the grenade to G_Suck and set
it to be called 1 second after the grenade is fired. We also set the
bolt->wait to the current time + 20 seconds. This is the amount
of time that will be allowed to pass before the grenade explodes,
that's if a player has not been sucked into the grenade yet
!
That's it, compile
and enjoy !
|