I've written a couple of mods making rockets and grenades deadlier. But
if you're on the receiving end of one of these beauties, you want to
take some defensive action. You want to shoot that homing missile down.
You want to blow up those proximity mines and detpipes BEFORE you step
on them. Here's how.

Let's start with grenades. Open up g_weapon.c and make these changes to
fire_grenade() ('+' signs indicate lines added)

 	grenade->dmg_radius = damage_radius;
 	grenade->classname = "grenade";
 
+	// CCH: a few more attributes to let the grenade 'die'
+	VectorSet(grenade->mins, -3, -3, 0);
+	VectorSet(grenade->maxs, 3, 3, 6);
+	grenade->mass = 2;
+	grenade->health = 10;
+	grenade->die = Grenade_Die;
+	grenade->takedamage = DAMAGE_YES;
+	grenade->monsterinfo.aiflags = AI_NOSTEP;
+
 	gi.linkentity (grenade);
 }

These extra attributes have been copied out of the SP_misc_explobox()
function that creates exploding barrels. First we use VectorSet to
define the limits of the bounding box. Then we assign several new
attributes to this grenade, including it's health value, a 'die'
function to call when the health is depleted, and a takedamage value
indicating this edict can take damage. Mass and monsterinfo.aiflags may
not be necessary, but I've copied them from the other function to be
sure.

Similar changes should be made to the fire_grenade2() function.

 	grenade->spawnflags = 1;
 	grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
 
+	// CCH: a few more attributes to let the grenade 'die'
+	VectorSet(grenade->mins, -3, -3, 0);
+	VectorSet(grenade->maxs, 3, 3, 6);
+	grenade->mass = 2;
+	grenade->health = 10;
+	grenade->die = Grenade_Die;
+	grenade->takedamage = DAMAGE_YES;
+	grenade->monsterinfo.aiflags = AI_NOSTEP;
+
 	if (timer <= 0.0)
 		Grenade_Explode (grenade);
 	else

So, what is this new Grenade_Die() function? Here you go. It's based on
a similar function barrel_delay() used for exploding barrels. I've added
this just before the fire_grenade() function.

+// CCH: When a grenade 'dies', it blows up next frame
+static void Grenade_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
+{
+	self->takedamage = DAMAGE_NO;
+	self->nextthink = level.time + .1;
+	self->think = Grenade_Explode;
+}
+
 void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
 {
 	edict_t	*grenade;

You might be wondering why we have a separate die function, instead of
calling Grenade_Explode() directly. The simple answer is they have
conflicting parameters. The reason we modify the think attribute rather
than calling Grenade_Explode() directly is that, well, Grenade_Explode()
frees the edict, and apparently Quake 2 expects that edict to still
exist when returning from the die function. So don't do this. It locks
Quake 2 up, trust me. 

That's it for grenades, now for rockets. Let's modify fire_rocket()
like we did for grenades.

 	rocket->radius_dmg = radius_damage;
 	rocket->dmg_radius = damage_radius;
 	rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
+
+	// CCH: a few more attributes to let the rocket 'die'
+	VectorSet(rocket->mins, -10, -3, 0);
+	VectorSet(rocket->maxs, 10, 3, 6);
+	rocket->mass = 10;
+	rocket->health = 10;
+	rocket->die = Rocket_Die;
+	rocket->takedamage = DAMAGE_YES;
+	rocket->monsterinfo.aiflags = AI_NOSTEP;
 
 	if (self->client)
 		check_dodge (self, rocket->s.origin, dir, speed);

The only adjustment here is that the bounding box is slightly slarger
and the mass is heavier.

Now, here's the other new rocket functions.

+// CCH: Explode rocket without touching anything
+static void Rocket_Explode (edict_t *ent)
+{
+	vec3_t		origin;
+
+	if (ent->owner->client)
+		PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
+
+	// calculate position for the explosion entity
+	VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
+
+	T_RadiusDamage(ent, ent->owner, ent->radius_dmg, NULL, ent->dmg_radius);
+
+	gi.WriteByte (svc_temp_entity);
+	if (ent->waterlevel)
+		gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
+	else
+		gi.WriteByte (TE_ROCKET_EXPLOSION);
+	gi.WritePosition (origin);
+	gi.multicast (ent->s.origin, MULTICAST_PVS);
+
+	G_FreeEdict (ent);
+}
+
+// CCH: When a rocket 'dies', it blows up next frame
+static void Rocket_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
+{
+	self->takedamage = DAMAGE_NO;
+	self->nextthink = level.time + .1;
+	self->think = Rocket_Explode;
+}
+
 void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
 {
 	edict_t	*rocket;

The Rocket_Die() is similar to Grenade_Die(), and we've added a
Rocket_Explode() function because rockets usually only explode on touch.
So, we've just taken the rocket_touch() function and cut out some
extraneous code having to do with the object touched. When we explode,
we just deal the damage, play the appropriate sound and get out.

I don't have a model viewer, so I've just guessed at the bounding box
sizes used. Feel free to correct them. I haven't had any access to
multiplayer games and it's kind of hard to get the monsters to shoot
down my shots, but I've definitely had success blowing up grenades
before their time. Let me know how it goes for you.

Full source and patch file at http://www.jump.net/~dctank.

Chris Hilton
chilton@scci-ad.com
