
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