|
The bodyque system which came with the 3.20 source was a good idea but
flawed. The problem was quite difficult to understand because of the circular
nature of that code. The player would die in T_Damage which would call
Killed() which would call player_die() which would call ThrowClientHead()
which would call the bodyqueue code which had its own die function (so that
you could kill the dead body) which (when you shot at it) the damage you did
would get processed in T_Damage then Killed then into the body_die function
which had its own call to ThrowClientHead() which called the bodyque stuff
again etc. etc... You get the idea? Really difficult to chase down just why
this all didn't work!
So, I removed the bodyque
altogether! No more queue'ing dead bodies!!
All in all, 2 things are
suppose to happen when a player dies.
First, if they die a
normal death (final health between 0 and -40) then one of the randomized
death animations is setup and the body lays down on the floor. It will
disappear on its own in 10 to 15 seconds OR will explode into gibs if
somebody repeatedly shoots at the dead body (get its health below -40)..
Second, is that the
player died an extreme death (final health < -40) in which case the body
will immediately disappear and explode into a bunch of gibs (making it look
like the player's body blew up!). The gibs go flying all over the place and
each gib has its own think function and its own touch function. The player's
normal respawn() function will handle the rest.. Anyway.. here it is..
==============================================================================
Find this definition and comment it out or delete it entirely..
#define BODY_QUEUE_SIZE 8
Now, find this function and remove it entirely..
void InitBodyQue(void)
Also, remove the call to InitBodyQue() near the top of SP_worldspawn() in your
g_spawn.c file.
Now, find your void G_FreeEdict() function and comment out (or delete) the indicated line:
void G_FreeEdict(edict_t *ed) {
gi.unlinkentity(ed);
// if ((ed-g_edicts)<=(ga.maxclients+8)) return; <==== REMOVE THIS LINE HERE
Okay, now find and delete these gib related functions in your g_misc.c file.
void VelocityForDamage()
void ClipGibVelocity()
void gib_think()
void gib_die()
void gib_touch()
void ThrowGib()
void ThrowHead()
void ThrowClientHead()
Now, paste these reworked gib handling routines at the top of
your g_misc.c file:
//=====================================================
//=====================================================
//=====================================================
#define zvec vec3_origin
#define GIB_SM_MEAT_MODEL "models/objects/gibs/sm_meat/tris.md2"
#define GIB_ARM_MODEL "models/objects/gibs/arm/tris.md2"
#define GIB_LEG_MODEL "models/objects/gibs/leg/tris.md2"
#define GIB_BONE_MODEL "models/objects/gibs/bone/tris.md2"
#define GIB_BONE2_MODEL "models/objects/gibs/bone2/tris.md2"
#define GIB_CHEST_MODEL "models/objects/gibs/chest/tris.md2"
#define GIB_SKULL_MODEL "models/objects/gibs/skull/tris.md2"
#define GIB_HEAD_MODEL "models/objects/gibs/head/tris.md2"
#define GIB_HEAD2_MODEL "models/objects/gibs/head2/tris.md2"
void Grenade_Explode(edict_t *); // you may have these already prototyped
void BecomeExplosion1(edict_t *);
//=====================================================
// Notice that I put in some various things you can
// make the gib do when the gib is touch by something
// other than the world.
// Uncomment the one you'd like to try (or add your
// own special things you'd like to do to a player
// when they step onto a gib!
//=====================================================
void gib_touch(edict_t *gib,edict_t *other,cplane_t *plane,csurface_t *surf) {
if (other!=world)
// BecomeExplosion1(gib); // gib explodes but doesn't do damage
G_FreeEdict(gib); // gib just disappears
// { gib->enemy=other; // gib explodes AND does damage
// Grenade_Explode(gib); }
}
//=====================================================
// This will make a gib entity and toss it in the same
// direction that the player was last moving in when he
// died and adds a bit of randomness to its movementy
// so all the gibs don't land in the exact same spot!
//=====================================================
void ThrowGib(edict_t *self,char *gibname,int damage,int type) {
vec3_t origin;
if (gi.pointcontents(self->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME))
return;
edict_t *gib=G_Spawn();
gi.setmodel(gib,gibname);
gib->solid=SOLID_BBOX;
gib->s.effects |= EF_GIB;
gib->flags |= FL_NO_KNOCKBACK;
gib->takedamage=DAMAGE_NO;
VectorSet(gib->mins,-8,-8,-8);
VectorSet(gib->maxs,+8,+8,+8);
if (!strcmp(gibname,GIB_SM_MEAT_MODEL))
gib->movetype=MOVETYPE_TOSS;
else
gib->movetype=MOVETYPE_BOUNCE;
// Don't touch this because it breaks easily!
VectorCopy(self->s.origin,origin);
VectorSet(gib->avelocity,random()*100,random()*100,random()*100);
gib->s.origin[0]=origin[0]+crandom()*self->size[0];
gib->s.origin[1]=origin[1]+crandom()*self->size[1];
gib->s.origin[2]=(rand()%10)+origin[2]+crandom()*self->size[2];
if (self->velocity[0] || self->velocity[1])
VectorCopy(self->velocity,gib->velocity);
else
VectorSet(gib->velocity,10*(rand()%10),10*(rand()%10),0);
if (self->velocity[2]>=0) gib->velocity[2]=200;
VectorScale(gib->velocity,random(),gib->velocity);
gib->touch=gib_touch; // Do this when gib is touched
gib->think=G_FreeEdict; // Gib disappears in 5 to 5.5 seconds
gib->nextthink=level.time + 5+random()*5;
gi.linkentity(gib);
}
//=====================================================
// This will throw body parts everywhere!!
// Change amounts and types of gibs to fit your mod
//=====================================================
void ThrowBodyParts(edict_t *self, int damage) {
int n;
gi.sound(self,4,gi.soundindex("misc/udeath.wav"),1,1,0);
for (n=0; n<3; n++)
ThrowGib(self, GIB_BONE_MODEL, damage, GIB_ORGANIC);
for (n=0; n<1; n++)
ThrowGib(self, GIB_SKULL_MODEL, damage, GIB_ORGANIC);
for (n=0; n<4; n++)
ThrowGib(self, GIB_SM_MEAT_MODEL, damage, GIB_ORGANIC);
for (n=0; n<2; n++)
ThrowGib(self, GIB_ARM_MODEL, damage, GIB_ORGANIC);
for (n=0; n<1; n++)
ThrowGib(self, GIB_CHEST_MODEL, damage, GIB_ORGANIC);
for (n=0; n<2; n++)
ThrowGib(self, GIB_LEG_MODEL, damage, GIB_ORGANIC);
for (n=0; n<4; n++)
ThrowGib(self, GIB_SM_MEAT_MODEL, damage, GIB_ORGANIC);
for (n=0; n<3; n++)
ThrowGib(self, GIB_BONE2_MODEL, damage, GIB_ORGANIC);
}
//=====================================================
// This is ONLY CALLED when extreme death happens..
// When player's final health < -40 in player_die()
// or when the body's health < -40 in body_die()
//=====================================================
void ThrowClientHead(edict_t *self,int damage) {
// If already disappeared, exit!
if (self->svflags & SVF_NOCLIENT) {
G_FreeEdict(self);
return; }
// Don't bother tossing gibs when player died in lava or slime
if (!(gi.pointcontents(self->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME)))
ThrowBodyParts(self,damage);
// This makes the body disappear
self->svflags |= SVF_NOCLIENT;
self->client->anim_priority=ANIM_DEATH;
self->s.frame=0;
self->client->anim_end=0;
gi.linkentity(self);
}
//=====================================================
void body_die(edict_t *self,edict_t *inflictor,edict_t *attacker,int damage,vec3_t point) {
// If you shoot the player's body BEFORE it disappears
// (because its think timer ran out) then make the
// player's body explode into a bunch of gibs
if (self->health<-40) {
gi.sound(self,4,gi.soundindex("misc/udeath.wav"),1,1,0);
ThrowBodyParts(self,damage);
self->s.origin[2] -= 16;
self->takedamage=DAMAGE_NO; }
}
//=====================================================
void CopyToBodyQue(edict_t *ent) {
edict_t *body=G_Spawn();
gi.unlinkentity(ent);
body->s=ent->s;
body->takedamage=DAMAGE_YES;
body->s.number=body-g_edicts;
body->svflags|=ent->svflags;
VectorCopy(ent->mins,body->mins);
VectorCopy(ent->maxs,body->maxs);
VectorCopy(ent->absmin,body->absmin);
VectorCopy(ent->absmax,body->absmax);
VectorCopy(ent->size,body->size);
body->solid=ent->solid;
body->clipmask=ent->clipmask;
body->owner=ent->owner;
body->movetype=ent->movetype;
body->die=body_die;
// Make player's body disappear in 10-15 secs
// if it is NOT TOUCHED BY WEAPON FIRE
body->think=G_FreeEdict;
body->nextthink=level.time + 10+rand()%5;
gi.linkentity(body);
}
Okay....
Now, find your void body_die() and void CopyBodyToQue() and delete them and
substitute these two in their place..
//=====================================================
void body_die(edict_t *self,edict_t *inflictor,edict_t *attacker,int damage) {
if (self->health<-40) {
gi.sound(self,4,gi.soundindex("misc/udeath.wav"),1,1,0);
ThrowBodyParts(self,damage);
self->s.origin[2] -= 16;
self->takedamage=DAMAGE_NO; }
}
//=====================================================
void CopyToBodyQue(edict_t *ent) {
edict_t *body=G_Spawn();
gi.unlinkentity(ent);
body->s=ent->s;
body->takedamage=DAMAGE_YES;
body->s.number=body-g_edicts;
body->svflags|=ent->svflags;
VectorCopy(ent->mins,body->mins);
VectorCopy(ent->maxs,body->maxs);
VectorCopy(ent->absmin,body->absmin);
VectorCopy(ent->absmax,body->absmax);
VectorCopy(ent->size,body->size);
body->solid=ent->solid;
body->clipmask=ent->clipmask;
body->owner=ent->owner;
body->movetype=ent->movetype;
body->die=body_die;
body->think=G_FreeEdict;
body->nextthink=level.time + 10+rand()%5;
gi.linkentity(body);
}
Remember, you may (or may not) have to do some function prototyping..
Lastly, substitue this player_die for your player_die() function
//=====================================================
void player_die(edict_t *self,edict_t *inflictor,edict_t *attacker,int damage,vec3_t point) {
VectorClear(self->avelocity);
self->takedamage=DAMAGE_YES;
self->movetype=MOVETYPE_TOSS;
self->s.modelindex2=0;
self->s.angles[0]=0;
self->s.angles[2]=0;
self->s.sound=0;
self->client->weapon_sound=0;
self->maxs[2]=-8;
self->svflags |= SVF_DEADMONSTER;
// First time thru!
if (self->deadflag==DEAD_NO) {
self->client->respawn_time=level.time+1.0;
LookAtKiller(self,inflictor,attacker);
self->client->ps.pmove.pm_type=PM_DEAD;
ClientObituary(self,inflictor,attacker);
TossClientWeapon(self);
Cmd_Score_f(self);
for (int n=0; n < ga.num_items; n++)
self->client->pers.inventory[n]=0; }
self->client->quad_framenum=0;
self->client->invincible_framenum=0;
self->client->breather_framenum=0;
self->client->enviro_framenum=0;
self->flags &= ~FL_POWER_ARMOR;
// Extreme death sequence
if (self->health<-40) {
ThrowClientHead(self,damage);
self->takedamage=DAMAGE_NO; }
else
// Normal death sequence
if (self->deadflag==DEAD_NO) {
self->client->anim_priority=ANIM_DEATH;
if (self->client->ps.pmove.pm_flags & PMF_DUCKED) {
self->s.frame=172;
self->client->anim_end=177; }
else
switch ((int)(rand()%2)) { // DO THIS!!
case 0:
self->s.frame=177;
self->client->anim_end=183;
break;
case 1:
self->s.frame=183;
self->client->anim_end=189;
break;
case 2:
self->s.frame=189;
self->client->anim_end=197;
break; }
// Also, the random value which selects the death scream sound was OUT-OF-BOUNDS!
// so, I fixed it to have ONLY the values of [1..4]
gi.sound(self,2,gi.soundindex(va("*death%i.wav",(rand()%3)+1)),1,1,0); }
self->deadflag=DEAD_DEAD;
gi.linkentity(self);
}
Finally, change your Cmd_Kill_f() function (suicide function) to this:
//=====================================================
void Cmd_Kill_f(edict_t *ent) {
if ((level.time-ent->client->respawn_time) < 5) return;
ent->health=0; // set so do regular death in player_die
ent->flags=0; // clear these out
meansOfDeath=MOD_SUICIDE; // flag for ClientObituary()
player_die(ent,ent,ent,10000,vec3_origin); // kill the bastard!
}
That's it!!
Let me know if you encounter problems making the above changes.
Have fun!!
Maj.Bitch
One last thing to think about. Take the code which tests if the player is in lava/slime and paste that at the very top of TossClientWeapon(). This way, NO weapon gets tossed when a player dies while in lava or slime (nobody can get the weapon anyway without dying!!)...
|