The BoobyTrapped Armor Add-On

 

 

philip
profile | email

posted 11-20-98 11:50 AM CT (US)
Title: The BoobyTrapped Armor Add-On
Difficulty: Moderate
By: Philip (aka Maj.Bitch)
Email: peblair@gv.net
Date: 11-16-98
Note: Please give credit where credit is due.
======================================================
During past wars, it was very common for the enemy to
boobytrap certain items which had a high likelihood of
being attractive to the enemy (such as souvenirs, armor,
medals, weapons, etc). So, I decided to make the booby
trapped body armor.

How it works:

You have to be in possession of either the Jacket, Combat,
or Body Armor.. Once you have armor, you can boobytrap it
with a bunch of grenades and drop the armor to the ground.
The armor looks and rotates exactly as if it is there for
the taking. Once boobytrapped, you have 5 seconds before
the armor's self-activation. The armor is in a search loop
waiting for somebody to come withing 50 radius units. Once
it detects somebody in that close proximity, the tosses out
10 grenades in random directions (some hitting the poor sucker
who wanted the armor and the rest bouncing around until they
hit somebody or self-detonate at random intervals)!! Replace
that power armor with a booby trapped version and teach those
bastards a lesson!!

Okay, let's get started..

============================================================
We need to add another MOD_* obituary flag. So, inside of
your g_local.h add the MOD_BOOBYTRAP 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_BOOBYTRAP 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_BOOBYTRAP:
message="was killed by a boobytrap";
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_BOOBYTRAP:
if (IsFemale(victim))
message="caught her booby trap";
else
message="caught his booby trap";
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_BOOBYTRAP:
message="was killed by";
message2="'s booby trap";
break;

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

============================================================
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, "booby") == 0)
Cmd_BoobyTrap_f(ent);
else if .....

This will initiate a boobytrap if the player has any of
the possible armor types in their possession.

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

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

void Cmd_BoobyTrap_f(edict_t *ent);

============================================================
Lets open you g_weapons.c file and add these helper routines
and the baton's 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 start and end are within radius distance.
//======================================================
qboolean G_Within_Radius(vec3_t start, vec3_t end, float rad) {
vec3_t eorg={0,0,0};
int j;
for (j=0; j<3; j++)>
eorg[j]=abs(start[j]-end[j]);
return (VectorLength(eorg) < rad);
}

//======================================================
// 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 buried=true;
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)&&(buried);
}

//======================================================
// 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);
}

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

Okay, now paste in these routines into the bottom of you
g_weapon.c file from start to end as indicated:

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

//======================================================
//============ BOOBY TRAP ROUTINES =====================
//======================================================

//======================================================
// Throw out 10 grenades in all directions!
//======================================================
void Spawn_Grenades(edict_t *booby) {
trace_t tr;
gitem_t *item=NULL;
edict_t *grenade=NULL;
vec3_t offset={0,0,0};
int i,speed;
vec3_t spray[] = { { 25, 00, 40 },
{ 17, -17, 40 },
{ 00, -25, 40 },
{ -17, -17, 40 },
{ -25, 00, 40 },
{ -17, 17, 40 },
{ 00, 25, 40 },
{ 17, 17, 40 },};
vec3_t forward = { 5.0, 5.0, 5.0 };
vec3_t right = { 5.0, 5.0, 5.0 };
vec3_t up = { 5.0, 5.0, 5.0 };
vec3_t dir = { 0.0, 0.0, 0.0 };

for (i=1; i <= 10; i++) {
grenade = G_Spawn();
grenade->owner=booby->owner;

// Set Item's Vector Positioning Info.
VectorSet(offset, spray[i-1][0], spray[i-1][1], spray[i-1][2]);
offset[0] += ((crandom()*16.0) - 8.0);
offset[1] += ((crandom()*16.0) - 8.0);
offset[2] += ((crandom()*16.0) - 8.0);
vectoangles(offset, dir);
AngleVectors(dir, forward, right, up);
speed += (crandom()*10.0);
VectorSet(grenade->mins, -15, -15, -15);
VectorSet(grenade->maxs, 15, 15, 15);
G_ProjectSource(booby->s.origin, offset, forward, right, grenade->s.origin);
tr=gi.trace(booby->s.origin, grenade->mins, grenade->maxs, grenade->s.origin, booby, CONTENTS_SOLID);
VectorCopy(tr.endpos, grenade->s.origin);
VectorScale(offset, speed, grenade->velocity);

grenade->velocity[2]=300;
grenade->movetype=MOVETYPE_BOUNCE;
grenade->classname="grenade";
item = FindItem("grenades");
grenade->clipmask=MASK_SHOT;
grenade->solid=SOLID_BBOX;
grenade->s.effects |= EF_GRENADE;

grenade->dmg=40;
grenade->dmg_radius=120;

grenade->spawnflags = 5; // Signal to Grenade_Explode().

grenade->model = "models/objects/grenade/tris.md2";
grenade->s.modelindex=gi.modelindex(grenade->model);
grenade->touch=Grenade_Touch;
grenade->think=Grenade_Explode;
grenade->nextthink = level.time + 10.0+(random()*5.0);
gi.linkentity(grenade);
} // end for

G_FreeEdict(booby);
}

//======================================================
void Booby_Think(edict_t *booby) {
edict_t *ent=NULL;
int i;

if (booby->delay <= level.time) {
G_FreeEdict(booby);
return; }

// Detonate on first player in 50 units radius.
for(i=0;i < game.maxclients;i++) {
ent=g_edicts+i+1;
if (!G_ClientInGame(ent)) continue;
if (ent==booby) continue;
if (!G_Within_Radius(booby->s.origin, ent->s.origin, 50)) continue;
// Blow up the Booby Trap in a big explosion.
G_Spawn_Explosion(TE_EXPLOSION2, booby->s.origin, booby->s.origin);
// Give off radius damage!
T_RadiusDamage(booby, booby->owner, booby->dmg, NULL, booby->dmg_radius, MOD_BOOBYTRAP);
// Toss out Grenades
Spawn_Grenades(booby);
return; }

booby->nextthink = level.time + 0.1;
}

//======================================================
void Booby_Touch(edict_t *baton, edict_t *other, cplane_t *plane, csurface_t *surf)
{}

//======================================================
qboolean SetBoobyTrap(edict_t *ent, int index) {
edict_t *booby=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, 25, forward, torigin);
if (gi.pointcontents(torigin) & MASK_SHOT) {
gi.cprintf(ent,PRINT_HIGH,"Cannot set Trap in Solid!\n");
return false; }

booby = G_Spawn();
booby->classname = "Booby";
booby->owner = ent;
booby->takedamage = DAMAGE_NO;
VectorCopy(torigin, booby->s.origin);
VectorClear(booby->movedir);
VectorClear(booby->s.angles);
VectorClear(booby->velocity);
booby->movetype = MOVETYPE_TOSS;
booby->solid = SOLID_BBOX;
booby->s.effects = EF_ROTATE;
VectorSet(booby->mins,-16,-16,-16);
VectorSet(booby->maxs,16,16,16);

if (index==ARMOR_JACKET)
booby->s.modelindex = gi.modelindex("models/items/armor/jacket/tris.md2");
else if (index==ARMOR_COMBAT)
booby->s.modelindex = gi.modelindex("models/items/armor/combat/tris.md2");
else
booby->s.modelindex = gi.modelindex("models/items/armor/body/tris.md2");

booby->s.modelindex2 = 0;

booby->delay = level.time + 60; // Turns OFF in 60 seconds..

booby->dmg=40; // same as grenade
booby->dmg_radius=120; // same as grenade

booby->touch = Booby_Touch;
booby->think = Booby_Think;
booby->nextthink = level.time + 5.0; // 5 Secs. to Activation

gi.linkentity(booby);

return true;
}

//======================================================
void Cmd_BoobyTrap_f(edict_t *ent) {
int index;

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

// Find out which armor the Player has (if any)
index=ITEM_INDEX(FindItem("Jacket Armor"));
if (!ent->client->pers.inventory[index]) {
index=ITEM_INDEX(FindItem("Combat Armor"));
if (!ent->client->pers.inventory[index]) {
index=ITEM_INDEX(FindItem("Body Armor"));
if (!ent->client->pers.inventory[index]) {
gi.centerprintf(ent,"NO ARMOR TO BOOBYTRAP\n");
return; } } }

ent->client->pers.inventory[index]=0;

if (SetBoobyTrap(ent,index))
gi.centerprintf(ent,"MOVE AWAY NOW!\n");
}

------------------ END HERE ------------------------

===========================================================
Find your grenade_explode() function and right before the call
to T_Damage() make the following change so that the obituaries
will work right.

void T_Damage(.....) {

if (ent->spawnflags & 5) // <=== NEW LINE HERE
mod=MOD_BOOBYTRAP; // <=== NEW LINE HERE
else // <=== NEW LINE HERE
if (ent->spawnflags & 1)
mod=MOD_HANDGRENADE;
else
mod=MOD_GRENADE;
T_Damage(.............);
} // endif

Also, further down in this same function make these changes:

if (ent->spawnflags & 5) // <=== NEW LINE HERE
mod=MOD_BOOBYTRAP; // <=== NEW LINE HERE
else // <=== NEW LINE HERE
if (ent->spawnflags & 2)
mod=MOD_HELD_GRENADE;
else if (ent->spawnflags & 1)
mod=MOD_HG_SPLASH;
else
mod=MOD_G_SPLASH;

T_RadiusDamage(.........)


Okay, these changes make sure that the correct MOD_* flag gets
passed into the T_Damage() and T_RadiusDamage() functions based
on the setting of the spawnflag = 5 so that the client obituaries
work right.

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

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

bind b "booby"

That's it!! Now go KILL ALL THOSE BASTARDS!

Have Fun!!

philip