
Quake DeveLS - Vulnerable Rockets
Author: Chris Hilton
Difficulty: Medium
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.
Grenades...
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.
Rockets...
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.
|
This site, and all
content and graphics displayed on it, |