|
QuakeZone MOD Coding Tutorials
|
|
|
Introduction Code Notes Exercises |
|
| Introduction |
|
|
These shocking beauts will fire thunderbolts at all opponents in the vicinity while in flight. On impact with a wall, an electric vortex is created that continues to let loose thunderbolts on anyone foolish enough to venture clode. Eventually the vortex explodes in a huge fireball. This tutorial is built on the lightning weapon tutorial on the Quakestyle site (see Links page) and credit is hereby given to the author. This Lightning Rockets tutorial requires that the Quakestyle tutorial has already been coded. This tutorial assumes you have already run through the Nuclear Rockets tutorial for adding new rocket types and the Status Bar tutorial for updating the player's HUD. If you have already coded the positron rockets tutorial you will notice a striking resemblence with this tutorial. The vortex/fireball model used in this tutorial is the same as that used for Nuke rockets and
is supplied from Lox - see the Nuke Rocket tutorial for more details. The other models and sounds
required for the thunderbolt effect will have been provided in the Quakestyle Lightning
Gun tutorial. |
| Code |
|
|
g_weapon.c Add the following 2 procedures just after procedure fire_bfg:
// PJT - lightning rockets - fireball explosion
void lightning_explode2 (edict_t *self)
{
edict_t *ent;
float points;
vec3_t v;
float dist;
// comment out damage - done in touch function
// if (self->s.frame == 0) - do for all frames
{
// PJT - still electricute people
rocket_think_lightning(self);
// the positron effect
ent = NULL;
while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
{
if (!ent->takedamage)
continue;
// let owner take damage
//if (ent == self->owner)
// continue;
if (!CanDamage (ent, self))
continue;
if (!CanDamage (ent, self->owner))
continue;
VectorAdd (ent->mins, ent->maxs, v);
VectorMA (ent->s.origin, 0.5, v, v);
VectorSubtract (self->s.origin, v, v);
dist = VectorLength(v);
points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
if (ent == self->owner)
points = points * 0.5;
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_ROCKET_EXPLOSION);
gi.WritePosition (ent->s.origin);
gi.multicast (ent->s.origin, MULTICAST_PHS);
T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, 0, MOD_TBOLT); // last 0 was DAMAGE_ENERGY
}
}
//*/
self->nextthink = level.time + FRAMETIME;
self->s.frame++;
self->dmg_radius = self->dmg_radius * 1.25;
self->s.renderfx = RF_SHELL_GREEN + RF_SHELL_BLUE + RF_SHELL_RED; // PJT - make lightning explosion white.
self->s.effects |= EF_COLOR_SHELL; //
if (self->s.frame == 10) // 16 for full positron
self->think = G_FreeEdict;
}
// PJT
// PJT - lightning rockets - initial explosion
void lightning_explode (edict_t *self)
{
// if (other == self->owner) : allow impact on rocket firer!
// return;
// if (surf && (surf->flags & SURF_SKY))
// {
// G_FreeEdict (self);
// return;
// }
if (self->owner->client)
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
// core explosion - prevents firing it into the wall/floor
// if (other->takedamage)
// T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
T_RadiusDamage(self, self->owner, 200, self, 100, MOD_TBOLT);
gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
self->solid = SOLID_BBOX; // SOLID_NOT;
self->touch = NULL;
VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
VectorClear (self->velocity);
self->s.modelindex = gi.modelindex ("models/positron/tris.md2");
self->s.frame = 0;
self->s.sound = 0;
self->s.effects &= ~EF_ANIM_ALLFAST;
self->s.renderfx = RF_SHELL_GREEN + RF_SHELL_BLUE + RF_SHELL_RED; // PJT - make lightning explosion white.
self->s.effects |= EF_COLOR_SHELL; //
self->think = lightning_explode2;
self->nextthink = level.time + FRAMETIME;
self->enemy = NULL; // PJT - was other;
self->radius_dmg = 100;
self->dmg_radius = 10;
// self->touch = positron_touch;
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_ROCKET_EXPLOSION);
gi.WritePosition (self->s.origin);
gi.multicast (self->s.origin, MULTICAST_PVS);
}
// PJT
Add the following procedure before procedure rocket_touch:
// PJT - lightning rockets - timer think
void rocket_think_lightning (edict_t *self)
{
edict_t *ent, *ignore;
vec3_t dir, start, end, point;
trace_t tr;
int damage;
ent = NULL;
// timer expired - blow up
if (level.time > self->vortex_timer)
{
self->think = lightning_explode;
self->nextthink = level.time + 0.2;
self->vortex_timer = 9999; // PJT don't re-trigger explosion again
return;
}
// look for players close by to electrocute
while ((ent = findradius(ent, self->s.origin, 512)) != NULL)
{
if (ent == self) // don't shock self
continue;
if(ent->client && (!G_ClientNotDead(ent))) // don't shock dead clients
continue;
if ((!ent->client) && (strcmp(ent->classname, "decoy") != 0)
&& (strcmp(ent->classname, "superdecoy") != 0)) // only shock clients and decoys
continue;
if ((ent == self->owner) && (self->vortex_timer == 9999)) // can shock firer
continue;
if (!visible(self, ent)) // must have clear path to shock
continue;
if (!ent->takedamage) // not entity that cannot take damage
continue;
if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
continue;
// shock client
damage = 10;
fire_lightning (self, self->s.origin, NULL, ent->s.origin, 1, damage);
}
if (!(self->vortex_timer == 9999))
{
self->nextthink = level.time + 0.2;
self->think = rocket_think_lightning;
}
}
// PJT
Add the following code after procedure rocket_touch:
// PJT - lightning rockets - touch function
/*
=================
rocket_touch_lightning
=================
*/
void rocket_touch_lightning (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
// can damage owner!!
if (other == ent->owner)
return;
// remove if hits sky
if (surf && (surf->flags & SURF_SKY))
{
G_FreeEdict (ent);
return;
}
// vortex explosion if hits player
if (other->client)
{
lightning_explode(ent);
return;
}
// vortex explosion if hits decoy
if ((Q_stricmp(other->classname, "decoy") == 0) ||
(Q_stricmp(other->classname, "superdecoy") == 0))
{
lightning_explode(ent);
return;
}
// Quake Rockets only if activated by real Player..
if (G_EntExists(ent->owner))
{
VectorClear (ent->velocity);
ent->s.modelindex = gi.modelindex ("models/positron/tris.md2");
ent->s.frame = 1;
ent->solid = SOLID_BBOX; // SOLID_NOT;
ent->s.effects &= ~EF_ANIM_ALLFAST;
ent->s.effects |= EF_COLOR_SHELL;
ent->s.effects |= EF_ROTATE;
ent->s.renderfx = RF_SHELL_BLUE + RF_SHELL_RED + RF_SHELL_GREEN; // PJT - make vortex white for lightning.
ent->nextthink = level.time + 0.2;
ent->think = rocket_think_lightning;
ent->vortex_timer = level.time + 10.0; // PJT make last a little longer
ent->s.sound = 0;
return;
}
G_FreeEdict (ent);
}
// PJT
In procedure rocket_think, add the following lines as indicated:
// PJT <-- start insert here
// 4a. lightning rockets
if (self->touch == rocket_touch_lightning)
{
self->vortex_timer = 9999;
rocket_think_lightning(self);
}
// PJT <-- end of insert
// 5. next think
self->nextthink = level.time + .1;
self->think = rocket_think;
Then, in procedure fire_rocket, add the following code as indicated within the code added for
different rocket types (see Nuclear Rockets tutorial):
// PJT - determine rocket type
if (self->client)
{
rocket->touch = rocket_touch;
switch(self->client->pers.nuke_state)
{
case 0: // standard
// <-- start inserting code here
case 6: // PJT - lightning rockets
rocket->touch = rocket_touch_lightning;
rocket->s.renderfx = RF_SHELL_BLUE + RF_SHELL_GREEN; // make vortex rockets blue/green
rocket->s.effects |= EF_COLOR_SHELL;
break;
// <-- end of insert
}
}
else
rocket->touch = rocket_touch;
// PJT
Finally, in procedure fire_lightning, which was created by the Quakestyle Lightning
Gun tutorial, make the following changes as indicated:
Firstly, alter the defintion of the procedure itself as follows: void fire_lightning (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t aimend, int dirtype, int damage) // PJT - aimend and dirtype added /* PJT if dirtype = 0, use aimdir (fire direction) to calculate end point if dirtype = 1, use aimend (end point) as end point of lightning */Then, after the variable declarations, amend the next 3 lines as follows:
// PJT - end point calculated from direction of fire
if (dirtype == 0)
{
vectoangles2 (aimdir, dir);
AngleVectors (dir, forward, right, up);
VectorMA (start, 8192, forward, end);
}
else // PJT - end point passed as parameter
VectorCopy (aimend, end);
// PJT
Finally, locate the code that creates the damage and amend as follows:
if ((tr.ent != self) && (tr.ent->takedamage))
{
// PJT - when source is not a client (eg a lightning rocket!)
if (self->client)
T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, damage, 0, MOD_TBOLT);
else
{
tmpobj = G_Spawn();
VectorCopy (tr.endpos, tmpobj->s.origin);
tmpobj->movetype = MOVETYPE_NONE;
tmpobj->solid = SOLID_NOT;
tmpobj->classname = "tmpobject";
tmpobj->s.modelindex = 0;
tmpobj->nextthink = level.time + 0.01;
tmpobj->think = G_FreeEdict;
gi.linkentity (tmpobj);
T_RadiusDamage(tmpobj, self, damage*2, NULL, damage*4, MOD_TBOLT_WATER);
// PJT
}
}
p_weapon.c In procedure Rocket_Count, which was added by the Nuclear Rockets tutorial, add the following lines as indicated:
switch (ent->client->pers.nuke_state)
{
case 0: // standard
// <-- start inserting code here
case 6: // lightning rockets
count = 15; // or any value you choose
break;
// <-- end of insert
}
g_cmds.c In procedure Cmd_Nukes_f, which was added by the Nuclear Rockets tutorial, add the following lines as indicated: i++; if (i>6) i = 0; // PJT - match highest case statement belowthen...
switch (i)
{
case 0: // standard rockets
// <-- start inserting code here
case 6: // PJT lightning rockets
break;
// <-- end of insert
}
p_hud.c In procedure init_hud_rocket_strings, which was added by the Statusbar tutorial, add the following line as indicated: gi.configstring (CS_TR_STRINGS +5, "Vortex "); // <-- insert line after here gi.configstring (CS_TR_STRINGS +6, "Lightning "); // PJT - lightning rockets g_local.h Add the following line to the end of the edict_s structure: int vortex_timer; // PJT - vortex timer - timer for vortex duration |
| Notes |
|
|
g_weapon.c
p_weapon.c
g_cmds.c
p_hud.c
g_local.h
|
| Exercises |
|
|