The MegaHealth Death Beam

 

 

philip
profile | email

posted 11-20-98 11:45 AM CT (US)
Title: The MegaHealth Death Beam
Difficulty: Moderate
By: Philip (aka Maj.Bitch)
Email: peblair@gv.net
Date: 11-13-98
Note: Please give credit where credit is due.
======================================================

I've been wanting to put togther this 'elevation beam'
type weapon for a while. You've heard of Mega Health
paks and StimPaks.. Well, this is the opposite!! This
is what I call the MegaHealth DeathPak..

Well, what is it?

For the cost of 25 Health Units, you toss out what looks
like a MegaHealth Pak with the normal bounce physics and
such. However, this MegaHealth paks a big surprise inside!
After the pak stops bouncing around and comes to rest, the
pak sits there waiting for some poor bastard to come along
and pick up the 200 health units!! Surprise!! When the
player tries to pick up the health unit pak (touches it)
the pak explodes into a fireball (without hurting the player)
and out of the fireball rises a wide red laser beam from floor
to sky/ceiling with the player trapped inside of the beam!!
The beam then grabs the player and slowly raises them up inside
the beam zapping them so their health slowly goes to zero (with
the associated moans and groans).. When the player's health has
gone to zero, they explode into a huge fireball (with the usual
blood and body parts raining down on those below)..

GREAT for replacing the normal MegaHealths with one of these
babies after you've already taken the real mega health pak!

BETTER YET, throw out one of these in an open area and watch
some sucker head straight for it!!

If nobody touches the pak in 30 seconds then the pak simply
disappears.

Okay, let's get started..

============================================================
We need to add a new variable to your gclient_s struct. So,
at the bottom of that struct, add this:

int is_trapped;

This will signal the ClientThink() function to disable..

============================================================

We need to add another MOD_* obituary flag. So, inside of
your g_local.h add the MOD_DEATHPAK flag as the next integer
in the numerical sequence as shown by example:

#define MOD_CLUSTER_BOMBS 0x00000024 // Cluster Airstrike
#define MOD_ZYLON_GAS 0x00000025 // Gas Grenade
#define MOD_RAILSTRIKE 0x00000026 // BFG Nuke Airstrike
#define MOD_BFG_NUKE 0x00000027 // Railgun Airstrike
#define MOD_DECOY 0x00000028 // Decoy Blast
#define MOD_LASERDRONE 0x00000029 // LaserDrone Weapon
#define MOD_DEATHPAK 0x00000030 // NEW FLAG HERE
#define MOD_FRIENDLY_FIRE 0x10000000 // DON'T TOUCH THIS ONE!!

============================================================

Okay, let's make some obituaries for your gameplay. Find your
ClientObituary() function in your p_client.c file and add the
following:

---------- FIND THESE LINES HERE -------------

case MOD_TARGET_BLASTER:
message="got blasted";
break;
------------ ADD THESE LINES HERE ------------
case MOD_DEATHPAK:
message="picked up a deathpak";
break;

Further down in this same function:

-------------- FIND THESE LINES -----------

case MOD_G_SPLASH:
if (IsFemale(victim))
message="tripped on her own grenade";
else
message="tripped on his own grenade";
break;

----------- ADD THESE LINES HERE -------------
case MOD_DEATHPAK:
message="picked up a deathpak";
break;

Still further down in this same function:

---------- FIND THESE LINES HERE -------------

case MOD_TELEFRAG:
message="tried to invade";
message2="'s personal space";
break;

--------- ADD THESE LINES HERE ---------------

case MOD_DEATHPAK:
message="picked up";
message2="'s deathpak";
break;

Remember: You should feel free to change these client
obituaries to whatever you think is more appropriate for
your particular mod..

============================================================

Okay, go into InitClientPersistant() function in your p_client.c
file and add this line near the bottom:

client->is_trapped=0; // default to OFF.

This will set the default to OFF each time the player enters
the game..

============================================================

Also in your p_client.c file, go into Player_Die() function
and add this line too:

self->client->is_trapped=0; // Turn OFF.

This will be reset when the player dies..

============================================================

Open up your g_cmds.c file and go near the bottom of your
ClientCommand() function and add the following as shown
by example..

---------- IF-ELSE STATEMENTS --------
else if (Q_stricmp(cmd, "decoy") == 0 )
SP_Decoy(ent);
else if (Q_stricmp(cmd, "drone") == 0 )
Cmd_LaserDrone_f(ent);
else if (Q_stricmp(cmd, "teleport") == 0)
Cmd_Teleport_f(ent);
----------- ADD THESE LINES ------
else if (Q_stricmp(cmd, "deathpak") == 0)
Cmd_DeathPak_f(ent);
else if .....

This will shoot out a deathpak with a single stroke of the player's
aliased key (providing they can pay the cost!).

============================================================

Also, near the top of you g_cmds.c file add this forward
declaration:

void Cmd_DeathPak_f(edict_t *ent);

============================================================
Lets open you g_weapons.c file and add these helper routines
and the source code somewhere near the top of the file.

NOTE: I've used these helper routines in many of my previous
tutorials so you may already have them someplace in your source.
If you already do have them, then you don't need them anymore.
Your compiler should warn you that you've already got these
functions previously defined..

Okay, add the following text to the top of your g_weapons.c file.

------------------ START HERE ------------------------

//======================================================
// True if Ent is valid, has client, and edict_t inuse.
//======================================================
qboolean G_EntExists(edict_t *ent) {
return ((ent) && (ent->client) && (ent->inuse));
}

//======================================================
// True if ent is not DEAD or DEAD or DEAD (and BURIED!)
//======================================================
qboolean G_ClientNotDead(edict_t *ent) {
qboolean b1=ent->client->ps.pmove.pm_type!=PM_DEAD;
qboolean b2=ent->deadflag != DEAD_DEAD;
qboolean b3=ent->health > 0;
return (b3 || b2 || b1);
}

//======================================================
// True if ent is not DEAD and not just did a Respawn.
//======================================================
qboolean G_ClientInGame(edict_t *ent) {
if (!G_EntExists(ent)) return false;
if (!G_ClientNotDead(ent)) return false;
return (ent->client->respawn_time + 5.0 < level.time);
}

//======================================================
//======================================================
//======================================================
/*
Spawns a (type) explosion at (start} and Broadcasts to all Potentially
Visible Sets from {origin}

TE_BFG_BIGEXPLOSION - Spawns a BFG particle explosion
TE_BFG_EXPLOSION - Spawns a BFG explosion sprite
TE_BOSSTPORT - Spawns a mushroom-cloud particle effect
TE_EXPLOSION1 - Spawns a mid-air-style explosion
TE_EXPLOSION2 - Spawns a nuclear-style explosion
TE_GRENADE_EXPLOSION - Spawns a grenade explosion
TE_GRENADE_EXPLOSION_WATER - Spawns an underwater grenade explosion
TE_ROCKET_EXPLOSION - Spawns a rocket explosion
TE_ROCKET_EXPLOSION_WATER - Spawns an underwater rocket explosion

Note: The last four EXPLOSION entries overlap to some degree.
TE_GRENADE_EXPLOSION is the same as TE_EXPLOSION2,
TE_ROCKET_EXPLOSION is the same as TE_EXPLOSION1,
and both of the EXPLOSION_WATER entries are the same, visually.
*/
//======================================================
void G_Spawn_Explosion(int type, vec3_t start, vec3_t origin ) {
gi.WriteByte(svc_temp_entity);
gi.WriteByte(type);
gi.WritePosition(start);
gi.multicast(origin, MULTICAST_PVS);
}

//======================================================
//============== DEATHPAK ROUTINES =====================
//======================================================
void Beam_Touch(edict_t *a, edict_t *b, cplane_t *c, csurface_t *d)
{}

//==========================================================
qboolean Raise_Player(edict_t *beam) {
vec3_t zvec=(0,0,0);
vec3_t end;
float dmg=5;

// Check if time to lift player more..
if (level.time >= beam->delay) {
// Can we lift this player another 50 units?
VectorMA(beam->goalentity->s.origin, 50, beam->movedir, end);
if (!gi.pointcontents(end) & MASK_SHOT)
VectorCopy(end, beam->goalentity->s.origin);
if (beam->goalentity->health <= dmg) dmg += 30;
// Damage this player in decrements of 'dmg' units per cycle.
T_Damage(beam->goalentity, beam, beam->activator, zvec, beam->s.origin, NULL, dmg, 0, 0, MOD_DEATHPAK);
// Has player died yet?
if (beam->goalentity->health <= 0) {
// Fireball explosion..
G_Spawn_Explosion(TE_EXPLOSION1, beam->goalentity->s.origin, beam->goalentity->s.origin);
return false; }
// Otherwise, repeat in 1 sec..
beam->delay = level.time + 1.0; }

return true;
}

//==========================================================
void Beam_Think(edict_t *beam) {
trace_t tr;
vec3_t end={0,0,0};

if (!Raise_Player(beam)) {
G_FreeEdict(beam);
return; }

// Generate Beam up to the sky/ceiling
VectorMA(beam->s.origin, 8192, beam->movedir, end);
tr=gi.trace(beam->s.origin, NULL, NULL, end, beam, MASK_ALL);
VectorCopy(tr.endpos, beam->s.old_origin);
beam->nextthink = level.time + 0.1; // Every frame else it flashes!!
}

//======================================================
void DeathPak_Touch(edict_t *deathpak, edict_t *other, cplane_t *plane, csurface_t *surf) {
edict_t *beam=NULL;

// Not touchable until stopped moving.
if (VectorLength(deathpak->velocity) > 1) return;

// Only touchable by real players.
if (!G_EntExists(other)) return;

// Fireball explosion..
G_Spawn_Explosion(TE_GRENADE_EXPLOSION, deathpak->s.origin, deathpak->s.origin);

// Create the Laser Beam Effect
beam = G_Spawn();
beam->owner=NULL; // Can trap owner too!!
beam->goalentity=other; // Link to trapped player
beam->activator=deathpak->activator; // Link to Owner For frags!!
beam->movetype = MOVETYPE_NONE;
beam->solid=SOLID_BBOX;
beam->takedamage = DAMAGE_NO;
beam->clipmask=MASK_SHOT;
beam->s.renderfx |= RF_BEAM | RF_TRANSLUCENT | RF_GLOW;
beam->s.modelindex=1; // must be non-zero
beam->s.frame=60; // beam diameter in units
beam->s.skinnum=0xf2f2f0f0; // Red Laser Beam
VectorClear(beam->velocity);// No Movement
VectorClear(beam->s.angles);// No Tilting
VectorSet(beam->movedir,0,0,1); // Straight UP!
VectorSet(beam->mins,-20,-20,-10); // Size of BBOX for Touch()
VectorSet(beam->maxs, 20, 20, 10); // Size of BBOX for Touch()
VectorCopy(other->s.origin, beam->s.origin);
beam->s.origin[2] -= 15; // bit of offset
VectorCopy(beam->s.origin, beam->s.old_origin);

beam->delay = level.time + 0.5; // Lift delay

beam->touch = Beam_Touch;
beam->think = Beam_Think;
beam->nextthink = level.time + 0.1;

gi.linkentity(beam);

G_FreeEdict(deathpak);

other->client->is_trapped=1; // Trap this player!
}

//======================================================
void Spawn_DeathPak(edict_t *ent) {
edict_t *deathpak=NULL;
vec3_t torigin;
vec3_t forward=(0,0,0);
vec3_t right=(0,0,0);
vec3_t up=(0,0,0);
gitem_t *item=NULL;

VectorCopy(ent->s.origin,torigin);
AngleVectors(ent->client->v_angle, forward, right, up);
VectorMA(torigin, 50, forward, torigin);

if (gi.pointcontents(torigin) & MASK_SHOT) {
gi.cprintf(ent,PRINT_HIGH,"Cannot project into Solid!\n");
return; }

deathpak = G_Spawn();
deathpak->owner=NULL;
deathpak->activator=ent; // For frag assignments

deathpak->movetype=MOVETYPE_BOUNCE;
deathpak->solid = SOLID_BBOX;
deathpak->s.renderfx = 0;
deathpak->s.effects = 0;

VectorSet(deathpak->mins, -15, -15, -15);
VectorSet(deathpak->maxs, 15, 15, 15);
VectorClear(deathpak->velocity);
VectorScale(forward, 600, deathpak->velocity);
VectorMA(deathpak->velocity, 200+crandom()*10.0, up, deathpak->velocity);
VectorMA(deathpak->velocity, crandom()*10.0, right, deathpak->velocity);
VectorClear(deathpak->avelocity);
VectorClear(deathpak->s.angles);
VectorCopy(torigin,deathpak->s.origin);

deathpak->model = "models/items/mega_h/tris.md2";
gi.setmodel(deathpak, deathpak->model);

deathpak->touch=DeathPak_Touch;

deathpak->think=G_FreeEdict;
deathpak->nextthink = level.time + 30.0; // Auto Self Destruct in 30 secs.

gi.linkentity(deathpak);
}

//======================================================
// Pay the Piper for the DeathPak
//======================================================
void Cmd_DeathPak_f(edict_t *ent) {

// Don't allow dead/respawning players to have Chamber!
if (!G_ClientInGame(ent)) return;

if (ent->health <= 25) {
gi.centerprintf(ent, "DeathPak costs 25 health to spawn!\n");
return; }

ent->health -= 25;
Spawn_DeathPak(ent);
}

============================================================
And, we need to go into your ClientThink() function and at the
very top of this function add the following code:

void ClientThink(edict_t *ent, usercmd_t *ucmd) {


if (ent->client->is_trapped) return;


===============================================================
Lastly..

Be sure to add the following to your Autoexec.cfg file as
shown by example..

bind d "deathpak"

That's it!! Now get out there and KILL ALL THOSE BASTARDS!

Have Fun!!

philip