
Quake DeveLS - Flash Grenades
Author: NightHawk
Difficulty: Medium
Well, I bet everyone
wonders why anyone would create or use a weapon that doesn't REALLY create any
damage, but flash grenades could be a rather effective tool if one knows how to
use them. Basically, when the grenade goes off anyone within a certain range
will go "blind", that is to say their screen display will flash
bright white and slowly fade. This, in combination with some fancy moving
around, could be menacing.
1. Client
Variables
First of all we need to
create some variables in the gclient_t structure. At or around line 830 of
"g_local.h", right at the end of the structure definitions, add:
... float pickup_msg_time; float respawn_time; // can respawn when time > this + int grenadeType;+ float blindTime, blindBase;} gclient_t;
What are
these, simple: two different fields. The "grenadeType" sets the type
of the currently active grenade (I use that instead of a boolean because, in my
version, I have six different types. Look for more tutorials soon!). The
"blindTime" and "blindBase" sets the duration and delay of
the blinding effect on client targets, which will be discussed earlier. At the
end of the "g_local.h" file add the following lines to define our
different grenade types:
+ #define GRENADE_NORMAL 0+ #define GRENADE_FLASH 1
2. Initialization
Even though other
mechanisms within the DLL source don't make it necessary, I find myself in the
habit of initializing all the data. So, in the "p_client.c" file,
within the "PutClientInServer" function (around line 855?), add the
following:
... VectorCopy (ent->s.angles, client->ps.viewangles); VectorCopy (ent->s.angles, client->v_angle); + client->grenadeType = GRENADE_NORMAL;+ client->blindBase = 0;+ client->blindTime = 0; if (!KillBox (ent)) ...
3. Firing a Flash Grenade
Now we need to modify the
"fire_grenade" and "fire_grenade2" in
"g_weapon.c" functions so that they check for the type of grenade. In
"fire_grenade" (approx. line 463)
... grenade->owner = self; grenade->touch = Grenade_Touch; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; grenade->dmg = damage; grenade->dmg_radius = damage_radius;+ if ((self->client) && (self->client->grenadeType == GRENADE_FLASH))+ {+ grenade->touch = Flash_Touch;+ grenade->think = Flash_Explode;+ grenade->classname = "flash_grenade";+ }+ else grenade->classname = "grenade"; ...
... and
modify the EXACT same lines as above that appear in the
"fire_grenade2" function (approx. line 496).
4. Think
and Touch
Ed's note: Now that
sounds kinky !
Now we need to make the
grenade "think" and "touch". For this, we need to search a
given radius for each client, see if he's looking in the direction of the
grenade, and "increment his blindness". The touching routine is
literally identical to the "Grenade_Touch" routine.
The following is pretty
much all "new" code. You could add this somewhere in
"g_weapon.c" before the "fire_grenade" functions, or you
could create a separate file.
#define FLASH_RADIUS 200 #define BLIND_FLASH 50 // Time of blindness in FRAMES void Flash_Explode (edict_t *ent) { vec3_t offset, origin; edict_t *target; // Move it off the ground so people are sure to see it VectorSet(offset, 0, 0, 10); VectorAdd(ent->s.origin, offset, ent->s.origin); if (ent->owner->client) PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); target = NULL; while ((target = findradius(target, ent->s.origin, FLASH_RADIUS)) != NULL) { if (target == ent->owner) continue; // You know when to close your eyes, don't you? if (!target->client) continue; // It's not a player if (!visible(ent, target)) continue; // The grenade can't see it if (!infront(target, ent)) continue; // It's not facing it // Increment the blindness counter target->client->blindTime += BLIND_FLASH * 1.5; target->client->blindBase = BLIND_FLASH; // Let the player know what just happened // (It's just as well, he won't see the message immediately!) gi.cprintf(target, PRINT_HIGH, "You are blinded by a flash grenade!!!\n"); // Let the owner of the grenade know it worked gi.cprintf(ent->owner, PRINT_HIGH, "%s is blinded by your flash grenade!\n", target->client->pers.netname); } // Blow up the grenade BecomeExplosion1(ent); } void Flash_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { if (other == ent->owner) return; // If it goes in to orbit, it's gone... if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict (ent); return; } // All this does is make the bouncing noises when it hits something... 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; } // The ONLY DIFFERENCE between this and "Grenade_Touch"!! Flash_Explode (ent); }
5. Blinding Effect
Next we need to make the
blinding effect. For this, we need to tinker with the viewport's alpha channel.
In the same way that a rebreather makes it look greenish, we want it to look
solid white a nd gradually fade. So in "p_view.c" there's a function
called "SV_CalcBlend". Around line 430 add...
... else if (contents & CONTENTS_WATER) SV_AddBlend (0.5, 0.3, 0.2, 0.5, ent->client->ps.blend); + if (ent->client->blindTime > 0)+ {+ float alpha = ent->client->blindTime / ent->client->blindBase;+ if (alpha > 1)+ alpha = 1;+ SV_AddBlend (1, 1, 1, alpha, ent->client->ps.blend);+ } // add for powerups ...
Note that
if blindTime is greater then blindBase, it will remain solid white.
6.
Switching Grenades
Finally, we need to let
the user change between the two types. First, we need to create a command
function in "g_cmds.c". This is totally new:
void Cmd_FlashGrenade_f(edict_t *ent) { if (ent->client->grenadeType == GRENADE_NORMAL) { gi.cprintf(ent, PRINT_HIGH, "Flash grenades selected.\n"); ent->client->grenadeType = GRENADE_FLASH; } else { gi.cprintf(ent, PRINT_HIGH, "Standard grenades selected.\n"); ent->client->grenadeType = GRENADE_NORMAL; } }
.. and
then add it to the command listing. Near the end of "g_cmds.c",
add...
... else if (Q_stricmp (cmd, "wave") == 0) Cmd_Wave_f (ent);+ else if (Q_stricmp (cmd, "flash") == 0)+ Cmd_FlashGrenade_f (ent); ...
And that's
it! "Flash on"!!!
Addendum: one thing I
added to my version of the above is that, when a person is blinded, it spins
them around in a random direction. After all, let's face it: if you get a
blinding, searing flash in the face, you're not just going to stand there.
Therefore, to the above piece of code within "Flash_Explode", add...
// Increment the blindness counter target->client->blindTime += BLIND_FLASH * 1.5; target->client->blindBase = BLIND_FLASH;+ target->s.angles[YAW] = (rand() % 360); // Whee!
Tutorial by NightHawk .
|
|