|
Title:
Insane Marine Death
Difficulty: Moderate
By: Philip (aka Maj.Bitch)
Email: peblair@frontier.net
Date: 08-17-99
Note: Please give credit where credit is due.
======================================================
I've been working on another project for quite sometime now
(and yes it is Quake related) but I was thinking..
You know, I was
thinking.. Even when a player's health is
really really low (say 2 units) they are severely damaged!!
Yet, they can still run around and jump and fight and all
the rest of the stuff the same as a player with 100 health.
This doesn't make any sense. If the player's health value
is really low, then they should act like they are damaged!!
So.. What if when a
player is damaged so that their health falls
below 10 units, they become one of those insane marines like
the ones in the Stroggs prison? That would be cool to toggle the
player's animation sequence so that they'd become one of those
insane marines and crawl around until killed! Very cool!!
Well, I thought about it and decided to code up this tutorial!
It is an easy add-on to
your mod and adds a bunch more realism
to the game!
How it works:
There is a server cvar
which is set by the server admin to
enable/disable this entire feature. If enabled, then when a
player's health is between 1..10 (adjustable), they become one
of these insane marines for 20 seconds (also adjustable) AND
THERE IS NOTHING THEY CAN DO ABOUT IT UNTIL THEIR TIMER EXPIRES!!
So, for the 20 second
interval, the severely damaged player
involuntarily crawls around moaning and moaning!! Since there are
3 insane marine animations, (crawling marine, standing marine, and
walking around marine), a random generator assigns one to the
lucky player with the very low health value!
If somebody kills the
insane marine player during this interval,
then they get 1 frag for the mercy killing! But, why kill the
player? They are stuck in this mode for the entire duration of
their timer value and so, in the meantime, you can run around
and rack up frags while they have to wait for their timer to
expire. Once their timer expires, they BLOW UP into a huge
fireball with the usual blood and gibs raining down all over the
place.. hehe Beware though! Players nearby the marine during the
explosion may be killed (or severely damaged themselves) if they
are within the blast radius!! hehe
Okay, let's get started..
============================================================
We need to add 3 new Server Cvars. Open you locals.h and
add the following new cvars to the other cvars there..
extern cvar_t
*sv_insanedeath; // 1=ON - Enable Insane Marine Death
extern cvar_t *sv_insanehealth;// Default health value for insane marine
death
extern cvar_t *svinsanetime // Default timer value for insane marine death
sequence
And let's formally
declare these at the top of g_main.c
like so:
cvar_t *sv_insanedeath;
cvar_t *sv_insanehealth;
cvar_t *svinsanetime
And let's set the default
values in InitGame() in g_save.c like
this:
sv_insanedeath=gi.cvar("sv_insanedeath","1",
CVAR_SERVERINFO); // 1=enable insane marine death
sv_insanehealth=gi.cvar("sv_insanehealth","10",
CVAR_SERVERINFO); // 1..health to trigger new death
sv_insanetime=gi.cvar("sv_insanetime","20",
CVAR_SERVERINFO); // default duration before self destruct
This will set the
defaults to ON, 10 health, 20 second timer.
Feel free to change these to whatever you think is more appropriate
for your particular mod..
============================================================
We need to add 2 new
variables to your gclient_s struct. So,
at the bottom of that struct, add this:
int is_insane; // Insane
Marine Death sequence, 1=ON, 0=OFF
float insane_time; // Time duration of Insane Marine Sequence
These are used to track
which player is currently insane and
how much time they have left before they detonate!
Let's set these new vars
in your at the bottom of your player_die()
function in your g_combat.c file add these lines:
self->client->is_insane=OFF;
self->client->insane_time=0.0;
This will turn these vars
OFF when the player dies!
And at the bottom of your
InitClientPersistant() function in your
p_client.c file add this:
client->is_insane=OFF;
client->insane_time=0.0;
This will init these vars
when the player joins the game.
============================================================
Open your m_insane.c file and add these 2 new routines to
the very bottom of the file:
----------------------
CUT HERE -----------------------
//======================================================
// Think function for Insane Marine Death Sequence.
//======================================================
void MarineThink(edict_t *marine) {
// Marine has already
died?
if (marine->healthdelaythink=NULL;
marine->nextthink=0.0;
// Spawn a Big Fireball explosion.
G_Spawn_Explosion(TE_EXPLOSION2, marine->s.origin, marine->s.origin);
// Maximally damage the Marine entity...
T_Damage(marine, world, world, zvec, marine->s.origin, zvec, 500, 1, 0,
MOD_SPLASH);
// Do radius damage to anybody nearby
T_RadiusDamage(marine, world, 100, NULL, 300, MOD_SPLASH);
return; }
marine->nextthink=level.time
+ 0.1;
}
//======================================================
// This turns the player into an insane marine!!
//======================================================
void InsaneMarineDeathSequence(edict_t *ent) {
edict_t *marine;
int temp;
marine = ent; // Make
entity the Insane Marine
temp=deathmatch->value;
// Temp storage..
deathmatch->value=0; // Must = 0 to bypass quick-exit in Monster Code
SP_misc_insane(marine);
// Create the Insane Marine
// Randomly toggle
marine's AI sequence
switch ((int)(10*random())%3) {
case 0: // Marine crawls around screaming "It HURTS!!"
BitAND(marine->monsterinfo.aiflags,AI_GOOD_GUY);
break;
case 1: // Marine stands asking to "Make it STOP!!"
BitAND(marine->monsterinfo.aiflags,AI_STAND_GROUND);
break;
case 2: // Marine walks around pleading "Kill me NOW!!"
BitAND(marine->monsterinfo.aiflags,AI_BRUTAL); }
// Restore to previous
value.
deathmatch->value=temp;
marine->die=player_die;
// Set to regular player_die() func!
// Create little think
func for marine
marine->think=MarineThink;
marine->nextthink=level.time+1.0;
marine->delay=level.time + marine->client->insane_time;
}
----------------------
STOP HERE -----------------------
And be sure to prototype this in the bottom of your g_locals.h
file like so:
void InsaneMarineDeathSequence(edict_t
*ent);
============================================================
Now, lets open your g_combat.c file and find your T_Damage()
function:
Add the following lines
in the exact place as shown..
// treat powerup savings
the same as armor
asave += save;
//---------------------------------------
// Apply damage amount to target entity.
//---------------------------------------
if (take) {
// Do splash of Sparks or Blood..
G_Spawn_Sparks((BitCmp(targ->svflags,SVF_MONSTER)||(targ->client))?TEBLOODBitCmp(dflags,DAMAGE_BULLET)?TE_BULLETSPARKSTE_SPARKS,
point, normal, point);
targ->health -= take;
//----------ADD THESE LINES HERE -----------------
// Is insane marine death sequence enabled?
if (sv_insanedeath->value) {
// Real Client and NOT already insane!
if (targ->client && !targ->client->is_insane)
// Health between 1 and ... ??
if (targ->health>0 && targ->healthvalue)
targ->client->is_insane=true;
targ->client->insane_time=sv_insanetime->value;
InsaneMarineDeathSequence(targ); // Convert them over!!
return; }
else
//-------END ADDING LINES HERE ------------------
// has their health gone to zero?
if (targ->healthsvflags,SVF_MONSTER) || (targ->client))
BitOR(targ->flags,FL_NO_KNOCKBACK);
targ->health=MaxOf(targ->health,-999);
// If so,, then process their die() function..
Killed(targ, inflictor, attacker, take, point);
return; } }
What this does is after
the amount of damage (take) is applied
to the entity's health, we test if this is a player and if so,
whether their health value is now between 1 and the deathvalue.
If it is and they are NOT already insane, then make them into
an insane marine! But, if their health has fallen below zero
(like they just got hit by the BFG) then they have a normal
death sequence.
===============================================================
Lastly.. In your G_RunFrame() function, make this change as
shown:
//
-------------------------------------
// Every edict_t gets a chance to think!
// -------------------------------------
ent=&g_edicts[0];
for (i=0; iinuse) continue;
level.current_entity=ent;
VectorCopy(ent->s.origin, ent->s.old_origin);
if ((ent->groundentity) && (ent->groundentity->linkcount !=
ent->groundentity_linkcount)) {
ent->groundentity=NULL;
if (!BitCmp(ent->flags,(FL_SWIM|FL_FLY)) && BitCmp(ent->svflags,SVF_MONSTER)
)
M_CheckGround(ent); }
if ((i>0) && (iclient->is_insane) { Add this line here
ClientBeginServerFrame(ent);
continue; } } Add extra brace here..
G_RunEntity(ent); }
What this does is instead of doing a regular ClientBeginServerFrame()
for a player who is currently insane, skip that and do a G_RunEntity()
because the player's movetype is now MOVETYPE_STEP (see m_insane.c)
and we want the g_phys.c file to process this player like a monster
instead of a client.
===============================================================
Be sure to add the
following to your Autoexec.cfg file as
shown by example..
set sv_insanedeath 1
set sv_insanehealth 10 // Or whatever low value you want
set sv_insanetime 20 // Or whatever duration you want!
That's it!! Now get out there and DON'T KILL THE BASTARDS!!
JUST DAMAGE THEM SEVERELY!!!
Have Fun!!
philip
|