Detonateable "Insane Marine" Decoy

 

 

philip
profile | email

posted 11-20-98 12:50 PM CT (US)
Title: Detonateable "Insane Marine" Decoy
Difficulty: Moderate
By: Philip (aka Maj.Bitch)
Email: peblair@gv.net
Date: 11-18-98
Note: Please give credit where credit is due.
======================================================

A while back, I took a crack at making the Detonateable
Decoy. Although commendable at the time, compared to
this, quite frankly that sucked!! In this version, for
the cost of 20 powercells you can spawn one of those
severely tortured and insane marine prisoners. (You remember
the ones in single player who were trapped in the prison
and crawling or walking around crying "KILL ME!", "KILL ME
NOW!!", "MAKE IT STOP!") Well, now you too can spawn
one of these marines as a detonateable decoy and the
marine entity walks and/or crawls around seeking out
'friendlies'!! A random generator picks determines
whether the marine walks or crawls..


You should note, however, while this marine was a prisoner
the Stroggs packed a big wad of C4 plastic explosive up
his butt with a proximity device!! So, when this poor
tortured marine comes near a friendly, the C4 detonates
literally 'blowing his ass to bits' and killing anybody
within the proximity radius!

Also, as I mentioned, this marine is detonateable, so
you hit your aliased key once to spawn the marine and
you hit it again and the marine blows up! There is a
3 second timer and a message giving you time to get the
hell away!! So, when he wanders near a desired area,
you can set off his butt full of C4 and kill everyone
within the proximity radius. Also, if the marine decoy
doesn't explode by proximity detonation or by yourself
detonating him, then after 2 minutes, the marine self
detonates (with the usual splatter of blood and guts).
Of course, you get all the frags from the decoy's kills!!

Finally, the decoy can be killed also! So, if somebody
decides to put the poor bastard out of his misery with a
rocket or something else (which will deliver death quick
and painlessly), then they'll pick up 1 frag for doing
a 'mercy killing'!

Okay, let's get started!

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

#define MOD_ZYLON_GAS 38 // Zylon Gas Grenades
#define MOD_CLUSTER_BOMBS 39 // ClusterBombs
#define MOD_GRAPPLE 40 // Grappling Hook
#define MOD_BOOBYTRAP 41 // BoobyTrap
#define MOD_DEPTHCHARGE 42 // Depth Charge
#define MOD_DECOY 43 // NEW FLAG HERE

===============================================================
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_DECOY:
message="was killed by a decoy";
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_DECOY:
if (IsFemale(victim))
message="caught her decoy";
else
message="caught his decoy";
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_DECOY:
message="was killed by";
message2="'s decoy";
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, "insane") == 0)
Cmd_InsaneDecoy_f(ent);
else if .....

This will activate the decoy.

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

Add this prototype declaration to the top of you g_cmds.c
file:

void Cmd_InsaneDecoy_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);
}

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

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

Find your Killed() function in your g_combat.c file and
add the following lines as shown:

void Killed(......)
{
if (targ->health < -999)
targ->health = -999;

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

if (deathmatch->value)
// Was this the Insane Marine which just got killed?
if (Q_stricmp(targ->classname, "XInsane")==0)
if (G_EntExists(attacker) && (attacker!=targ->activator)) {
gi.centerprintf(attacker,"1 Frag for killing Marine Decoy!\n");
attacker->client->resp.score += 1; }

-------------------------------------------------------------

This will award the 1 frag to the player who takes out the
tortured marine as a mercy killing.

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

Create a new file called p_insane.c and paste in the following
code from start to stop as indicated:

--------------------- START HERE ------------------
#include "g_local.h"
#include "m_player.h"

#define POWER1_SOUND gi.soundindex("misc/power1.wav")

//======================================================
//=========== INSANE MARINE DECOY ROUTINES =============
//======================================================

//======================================================
// Detonate Marine in Big Fireball !!
//======================================================
void Marine_Explode(edict_t *timer) {
vec3_t zvec={0,0,0};

// Has Monster died already?
if (!timer->activator) {
G_FreeEdict(timer);
return; }

// Maximally damage the Marine entity...
T_Damage(timer->activator, timer->owner, timer->owner, zvec, timer->activator->s.origin, NULL, 500, 1, 0, MOD_SPLASH);

// Spawn an explosion fireball..
G_Spawn_Explosion(TE_EXPLOSION2, timer->activator->s.origin, timer->activator->s.origin);

// Do radius damage to anybody nearby
T_RadiusDamage(timer->activator, timer->owner, 100, NULL, 300, MOD_DECOY);

// Does owner still exist?
if (timer->owner)
timer->owner->goalentity=NULL; // Flag as Detonated!!

G_FreeEdict(timer);
}

//======================================================
// timer->owner => ent;
// timer->activator => marine;
//======================================================
void Marine_Seek_Enemy(edict_t *timer) {
edict_t *ent=NULL;

// Has timer expired?
if (timer->delay <= level.time) {
Marine_Explode(timer);
return; }

// Search for all valid ents within a 100 unit radius
while ((ent=findradius(ent, timer->activator->s.origin, 100)) != NULL) {
if ((!ent)||(!ent->takedamage)) continue;
if (ent==timer) continue;
if (ent==timer->activator) continue;
Marine_Explode(timer);
return;
} // endif

timer->nextthink=level.time + 1.0; // Seek again in 1 sec.
}

//======================================================
void Spawn_InsaneMarine(edict_t *ent) {
edict_t *marine;
edict_t *timer;
vec3_t forward, torigin;
int temp;

// See if we can project Marine forward 50 units...
AngleVectors(ent->s.angles, forward, NULL, NULL);
VectorCopy(ent->s.origin, torigin);
VectorMA(torigin, 50, forward, torigin);
if (gi.pointcontents(torigin) & MASK_SHOT)
VectorCopy(ent->s.origin, torigin);

// Create basic entity stuff...
marine=G_Spawn();
marine->classname = "XInsane";// Used for Killed()
marine->activator=ent; // Link back to Owner.
ent->goalentity=marine; // Owner Linked to Marine.
VectorCopy(torigin, marine->s.origin);
gi.linkentity(marine);

//------------------------------
// Change some Global States
//------------------------------

skill->value=3; // Toggle Advanced AI.
temp=deathmatch->value; // Temp storage..
deathmatch->value=0; // Must = 0 to bypass quick-exit in Monster Code

//------------------------------
// Activate the Marine Entity
//------------------------------

SP_misc_insane(marine); // Create the Insane Marine

// Toggle marine's AI to seek out friendlies..
switch (rand()&2) {
case 0: // Marine crawls on ground in agony..
marine->monsterinfo.aiflags &= AI_GOOD_GUY;
break;
case 1: // Marine stands his ground...
marine->monsterinfo.aiflags &= AI_STAND_GROUND;
break;
case 2: // Marine walks around in agony..
marine->monsterinfo.aiflags &= AI_BRUTAL;
} // end switch

// Make Marine a bit harder to kill.
marine->health *= 2;

// Restore to previous value.
deathmatch->value=temp;

// Play powerup sound.
gi.sound(ent->activator, CHAN_VOICE, POWER1_SOUND, 1, ATTN_IDLE, 0);

//---------------------------
// Spawn Timer Entity.
//---------------------------

timer=G_Spawn();
timer->owner=ent; // Link to Owner.
timer->activator=marine; // Link to Marine.
ent->mynoise2=timer; // Link back to timer..
timer->takedamage=DAMAGE_NO;
timer->movetype=MOVETYPE_NONE;
timer->solid = SOLID_NOT;
VectorClear(timer->s.origin);
VectorClear(timer->mins);
VectorClear(timer->maxs);

timer->delay=level.time + 120.0; // 2 mins to Auto Self-Destruct

timer->think=Marine_Seek_Enemy;
timer->nextthink=level.time + 3.0; // Begin radius seek in 3 secs.
gi.linkentity(timer);
}

//======================================================
// Pay the Piper for the for Insane Marine Decoy
//======================================================
void Cmd_InsaneDecoy_f(edict_t *ent) {
int index;

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

// Insane Marine already active?
if (ent->goalentity) {
Marine_Explode(ent->mynoise2);
ent->goalentity=NULL;
gi.centerprintf(ent, "DECOY DETONATED\n");
return; }

// Try to see if we can Spawn the Marine
index = ITEM_INDEX(FindItem("cells"));
if (ent->client->pers.inventory[index] < 20){
gi.centerprintf(ent, "Decoy costs 20 PowerCells!\n");
return; }
else {
ent->client->pers.inventory[index] -= 20;
Spawn_InsaneMarine(ent);
gi.centerprintf(ent, "DECOY SPAWNED\n\n3 Secs to Activation!!\n"); }
}

--------------------- STOP HERE -------------------

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

Remember to bind a key in your Autoexec.cfg file as shown
by example below:

bind i "insane"

That's it!! Let your decoy KILL THE BASTARDS FOR YOU!

Have Fun!!

philip