|
QuakeZone MOD Coding Tutorials
|
|
|
Introduction Code Notes Exercises |
|
| Introduction |
|
|
These fiendish devils are the opposite of homing missiles - instead of homing in on players, the players home in on these! Any players unlucky enough to be in the vicinity of these while in flight or at impact site will rapidly get sucked in, then wiped out by the resulting explosion that follows in a short delay. The firer will not sucked in until impact, giving him chance to get out of harm's way. Tip: fire one of these into lava and watch your opponets standing nearby get dragged in. This tutorial is based on a tutorial for vortex grenades on the Quakestyle page - go to the Links page from the menu at the top of this page. The 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. 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. |
| Code |
|
|
g_weapon.c Add the following 2 procedures just after procedure fire_bfg:
// PJT - vortex rockets - fireball explosion
void vortex_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 drag people in while exploding
rocket_think_vortex(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_VORTEX); // 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_BLUE; // PJT - make vortex explosion blue.
self->s.effects |= EF_COLOR_SHELL; //
if (self->s.frame == 10) // 16 for full positron
self->think = G_FreeEdict;
}
// PJT
// PJT - vortex rockets - initial explosion
void vortex_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_VORTEX);
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_BLUE; // PJT - make vortex explosion blue.
self->s.effects |= EF_COLOR_SHELL; //
self->think = vortex_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 - vortex rockets - timer think
void rocket_think_vortex (edict_t *self)
{
edict_t *ent, *ignore;
vec3_t dir, start, end, point;
trace_t tr;
ent = NULL;
// timer expired - blow up
if (level.time > self->vortex_timer)
{
self->think = vortex_explode;
self->nextthink = level.time + 0.2;
self->vortex_timer = 9999; // PJT don't re-trigger explosion again
return;
}
// look for players close by and drag in
while ((ent = findradius(ent, self->s.origin, 512)) != NULL)
{
if (ent == self) // don't drag in self
continue;
if ((!ent->client) && (strcmp(ent->classname, "decoy") != 0)) // only drag in clients and decoys
continue;
if ((ent == self->owner) && (self->vortex_timer == 9999)) // can drag in firer
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;
// bubble trail between vortex and client
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_BUBBLETRAIL); // PJT - was TE_BFG_LASER
gi.WritePosition (self->s.origin);
gi.WritePosition (ent->s.origin); // PJT - was (tr.endpos);
gi.multicast (self->s.origin, MULTICAST_PHS);
// drag client towards vortex
VectorCopy(ent->s.origin, start);
VectorCopy(self->s.origin, end);
VectorSubtract(end, start, dir);
VectorNormalize(dir);
VectorScale(dir,1000, ent->velocity);
VectorCopy(dir, ent->movedir);
// PJT - cause slight damage when caught in vortex
// if ((ent->takedamage) && (ent != self->owner))
// T_Damage (ent, self, self->owner, dir, ent->s.origin, self->s.origin, 5, 1, DAMAGE_ENERGY, MOD_VORTEX);
}
if (!(self->vortex_timer == 9999))
{
self->nextthink = level.time + 0.2;
self->think = rocket_think_vortex;
}
}
// PJT
Add the following code after procedure rocket_touch:
// PJT - vortex rockets - touch function
/*
=================
rocket_touch_vortex
=================
*/
void rocket_touch_vortex (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)
{
vortex_explode(ent);
return;
}
// vortex explosion if hits decoy
if (Q_stricmp(other->classname, "decoy") == 0)
{
vortex_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 = 2;
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; // PJT - make vortex blue.
ent->nextthink = level.time + 0.2;
ent->think = rocket_think_vortex;
ent->vortex_timer = level.time + 5.0;
ent->s.sound = 0;
return;
}
G_FreeEdict (ent);
}
// PJT
In procedure rocket_think, add the following lines as indicated:
self->think = rocket_think;
// PJT - vortex rocket
if (self->touch == rocket_touch_vortex)
{
self->vortex_timer = 9999;
rocket_think_vortex(self);
}
// PJT - end of insert
Finally, 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 5: // PJT - vortex rockets
rocket->touch = rocket_touch_vortex;
rocket->s.renderfx = RF_SHELL_BLUE; // make vortex rockets blue
rocket->s.effects |= EF_COLOR_SHELL;
break;
// <-- end of insert
}
}
else
rocket->touch = rocket_touch;
// 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 5: // vortex 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>5) i = 0; // PJT - match highest case statement belowthen...
switch (i)
{
case 0: // standard rockets
// <-- start inserting code here
case 5: // PJT vortex 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 +4, "Earthquake "); // <-- insert line after here gi.configstring (CS_TR_STRINGS +5, "Vortex "); // PJT - vortex rockets g_local.h Add the following line as indicated: #define MOD_TARGET_BLASTER 33 // <-- insert line after here #define MOD_VORTEX 39 // PJT - vortex rocketsAdd the following line to the end of the edict_s structure: int vortex_timer; // PJT - vortex timer - timer for vortex duration p_client.c Add the following line as indicated in the following places to procedure ClientObituary:
// PJT - vortex rockets
case MOD_VORTEX:
message="was sucked into the vortex";
break;
// PJT - end of insert
case MOD_BOMB:
case MOD_SPLASH:
case MOD_TRIGGER_HURT:
message = "was in the wrong place";
break;
then...
// PJT - vortex rockets case MOD_VORTEX: message = "was sucked into his own private vortex"; break; // PJT - end of insert default: if (IsNeutral(self)) message = "killed itself"; else if (IsFemale(self)) message = "killed herself"; else message = "killed himself"; break;and finally... // PJT vortex rockets case MOD_VORTEX: message = "was sucked into"; message2 = "'s vortex"; break; // PJT - end of insert case MOD_SHOTGUN: message = "was gunned down by"; break; |
| Notes |
|
|
g_weapon.c
p_weapon.c
g_cmds.c
p_hud.c
g_local.h
p_client.c
|
| Exercises |
|
|