Shrapnel Grenades


Send metal flying everywhere :-)

These grenades are more lifelike because they actually send metal flying everywhere when they explode!  All of the code being added/changed is in g_weapon.c, so open that sucker up and follow along.  Remember to add in the blue code and take out the pink code.

First go down to the Grenade_Explode function and add in these two new functions below it.

void Shrapnel_Explode (edict_t *ent)
{
    vec3_t        origin;    
    vec3_t grenade1;
    vec3_t grenade2;
    vec3_t grenade3;
    vec3_t grenade4;
    vec3_t grenade5;
    vec3_t grenade6;
    vec3_t grenade7;
    vec3_t grenade8;
    int             mod;
    int             i;
    vec3_t offset;

    if (ent->owner->client)
        PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);

    VectorSet(offset,0,0,32);
    VectorAdd(offset,ent->s.origin,offset);
    VectorCopy (offset, ent->s.origin);
    VectorSet(grenade1,20,20,5);
    VectorSet(grenade2,20,-20,5);
    VectorSet(grenade3,-20,20,5);
    VectorSet(grenade4,-20,-20,5);
    VectorSet(grenade5,0,20,5);
    VectorSet(grenade6,0,-20,5);
    VectorSet(grenade7,-20,0,5);
    VectorSet(grenade8,20,0,5);
    for (i = 0; i < 6; i++)
    {
        fire_bullet (ent, offset, grenade1, 2, 2, 4500, 4500, MOD_G_SPLASH);
        fire_bullet (ent, offset, grenade2, 2, 2, 4500, 4500, MOD_G_SPLASH);
        fire_bullet (ent, offset, grenade3, 2, 2, 4500, 4500, MOD_G_SPLASH);
        fire_bullet (ent, offset, grenade4, 2, 2, 4500, 4500, MOD_G_SPLASH);
        fire_bullet (ent, offset, grenade5, 2, 2, 4500, 4500, MOD_G_SPLASH);
        fire_bullet (ent, offset, grenade6, 2, 2, 4500, 4500, MOD_G_SPLASH);
        fire_bullet (ent, offset, grenade7, 2, 2, 4500, 4500, MOD_G_SPLASH);
        fire_bullet (ent, offset, grenade8, 2, 2, 4500, 4500, MOD_G_SPLASH);
    }

    if (ent->takedamage)
    {
        float    points;
        vec3_t    v;
        vec3_t    dir;

        VectorAdd (ent->mins, ent->maxs, v);
        VectorMA (ent->s.origin, 0.5, v, v);
        VectorSubtract (ent->s.origin, v, v);
        points = ent->dmg - 0.5 * VectorLength (v);
        VectorSubtract (ent->s.origin, ent->s.origin, dir);
        if (ent->spawnflags & 1)
            mod = MOD_HANDGRENADE;
        else
            mod = MOD_GRENADE;
        T_Damage (ent, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
    }

    if (ent->spawnflags & 2)
        mod = MOD_HELD_GRENADE;
    else if (ent->spawnflags & 1)
        mod = MOD_HG_SPLASH;
    else
        mod = MOD_G_SPLASH;
    T_RadiusDamage(ent, ent->owner, ent->dmg, ent, ent->dmg_radius, mod);

    VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
    gi.WriteByte (svc_temp_entity);
    if (ent->waterlevel)
    {
        if (ent->groundentity)
            gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
        else
            gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
    }
    else
    {
        if (ent->groundentity)
            gi.WriteByte (TE_GRENADE_EXPLOSION);
        else
            gi.WriteByte (TE_ROCKET_EXPLOSION);
    }
    gi.WritePosition (origin);
    gi.multicast (ent->s.origin, MULTICAST_PHS);

    G_FreeEdict (ent);
}

static void Shrapnel_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
    if (other == ent->owner)
        return;

    if (surf && (surf->flags & SURF_SKY))
    {
        G_FreeEdict (ent);
        return;
    }

    if (!other->takedamage)
    {
        if (ent->spawnflags & 1)
        {
            if (random() > 0.5)
                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
            else
                gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
        }
        else
        {
            gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
        }

        return;
    }

    ent->enemy = other;
    Shrapnel_Explode (ent);
}

Ok, all the shrapnel_explode function does is fire bullets out from the explosion.  It uses vectors to tell the bullets which way to go in the x, y, and z axis.  You may notice that it only has 8 fire_bullet calls, but don't worry!  It fires many more bullets than that because of the integer "i" lines right above it.  The integer is used like a "x" symbol in math class, and will call those 8 functions however many times you want.  Be careful with this though, because too many bullets, when fired underwater, will cause sz_getspace overflows.   Too many bubbletrails with too many entities are being shown at once.  The shrapnel_think function is exactly like the grenade_think function, except it calls upon shrapenl_explode if the grenade touches a monster or player.

Next go down to the fire_grenade and/or fire_grenade2 functions.  You can modify the fire_grenade function for the grenade launcher and the fire_grenade2 function for the hand grenades.

First the fire_grenade function:

void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
{
    edict_t    *grenade;
    vec3_t    dir;
    vec3_t    forward, right, up;

    vectoangles (aimdir, dir);
    AngleVectors (dir, forward, right, up);

    grenade = G_Spawn();
    VectorCopy (start, grenade->s.origin);
    VectorScale (aimdir, speed, grenade->velocity);
    VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
    VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
    VectorSet (grenade->avelocity, 300, 300, 300);
    grenade->movetype = MOVETYPE_BOUNCE;
    grenade->clipmask = MASK_SHOT;
    grenade->solid = SOLID_BBOX;
    grenade->s.effects |= EF_GRENADE;
    VectorClear (grenade->mins);
    VectorClear (grenade->maxs);
    grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
    grenade->owner = self;
   
grenade->touch = Grenade_Touch;
   
grenade->nextthink = level.time + timer;
    grenade->think = Grenade_Explode;

    grenade->dmg = damage;
    grenade->dmg_radius = damage_radius;
    grenade->classname = "grenade";

    grenade->think = Shrapnel_Explode;
    grenade->touch = Shrapnel_Touch;


    gi.linkentity (grenade);
}

And then the fire_grenade2 function:

void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
{
    edict_t    *grenade;
    vec3_t    dir;
    vec3_t    forward, right, up;

    vectoangles (aimdir, dir);
    AngleVectors (dir, forward, right, up);

    grenade = G_Spawn();
    VectorCopy (start, grenade->s.origin);
    VectorScale (aimdir, speed, grenade->velocity);
    VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
    VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
    VectorSet (grenade->avelocity, 300, 300, 300);
    grenade->movetype = MOVETYPE_BOUNCE;
    grenade->clipmask = MASK_SHOT;
    grenade->solid = SOLID_BBOX;
    grenade->s.effects |= EF_GRENADE;
    VectorClear (grenade->mins);
    VectorClear (grenade->maxs);
    grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
    grenade->owner = self;
   
grenade->touch = Grenade_Touch;
    grenade->nextthink = level.time + timer;
    grenade->think = Grenade_Explode;

    grenade->dmg = damage;
    grenade->dmg_radius = damage_radius;
    grenade->classname = "hgrenade";

    grenade->think = Shrapnel_Explode;  
    grenade->touch = Shrapnel_Touch;


   if (held)
        grenade->spawnflags = 3;
    else
        grenade->spawnflags = 1;
    grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");

    if (timer <= 0.0)
        Grenade_Explode (grenade);
    else
    {
        gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
        gi.linkentity (grenade);
    }
}

All that was done was adding in the new lines to call the two new functions: Shrapnel_Explode and Shrapnel_Think.
That's it!  Enjoy you new grenades.

Tutorial by Willi
Quake Style - Tutorials