Improved Gibbable Corpses Tutorial, and "Semisolid"

Discuss programming in the QuakeC language.
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Improved Gibbable Corpses Tutorial, and "Semisolid"

Post by Wazat »

Here is a newer, much simpler version of the gibbable corpses tutorial I wrote long ago. These corpses can be killed with any weapon, but they let the player move right through them without inhibiting him at all. This works across engines, including vanilla Quake (tested in GLQuake and its variants).

This same method could be used to create ghost monsters -- monsters the player can run right through, but which his attacks still hit. It could also be used to allow team mates to move through walls that block enemies (team-friendly walls instead of team doors). This tutorial focuses on corpses.

Known Bugs:
1) (minor) If the player manages to get his attack origin is inside a corpse (despite their low height), his trace hit weapons like shotguns won't hit the corpse (or possibly other targets either). He just needs to step out of the corpse or use a different weapon, if he really cares about popping it. Corpses are so short this bug is hard to encounter (would need to walk into a corpse that's hanging over a ledge).
2) Traceline-based attacks that fire during player frames (i.e. player_light1, player_axe3, etc) won't hit corpses. The axe doesn't gib corpses, and the lightning bolt only hits them when the lightning starts (which happens in w_attack). This is because player frames occur between player post think and player pre think. I'm still looking into workarounds for this.

Summary
How's it work? We use a little-known feature of Quake: All of a player's physics code happens between the end of PlayerPreThink and the beginning of PlayerPostThink. Quake runs these steps in order on each player sequentially: My Prethink, my movement & physics, my postthink (other players and objects do not do their movement and physics during mine).

This means if something were solid most of the time, but magically unsolid between the pre and post think functions for a player, then the player could walk through the same things he could shoot. This also works on BSP objects like the func_wall, meaning you could make walls through which only players or only team members could pass in a teamplay mod like Team Fortress. I intend to make another tutorial of this later.

But on with the corpses tutorial!

In this tutorial I mark text that is changing as cyan. I may post a whole function, but only the cyan text is the part of interest. (to those of you who are colorblind, I apologize).


Step 1) Adding Definitions

Add these to the very bottom of defs.qc:


// Used for searching for objects that let players past but still block bullets.
// Set "semisolid" to "y" to let players walk through the entity.
.string semisolid;
// Used to store an entity's old solid value between PlayerPreThink and PlayerPostThink
.float oldSolid;
// Functions we use often
void() corpse_die;
void(entity ent, float hp, string headmdl) BecomeCorpse;
void(float hp, string headmdl) MonsterBecomeCorpse;
void(entity ent, float hp) CopyToBodyQue;
void(string gibname, float dm) ThrowGib;
void(string gibname, float dm) ThrowHead;
void() SemisolidPreThink;
void() SemisolidPostThink;


This adds two new fields to entities, semisolid and oldSolid. Semisolid is a way to mark monsters as something the player can walk right through. I use a string so that I can easily search through them all with the find() function. If you're using an engine that has the new find function that can use floats, then feel free to change how this field works (if you know what you're doing).
The oldSolid field stores an entity's normal solid value. Between PlayerPreThink and PlayerPostThink, the entity's solid will be set to SOLID_NOT. When then the entity's solid needs to be reset to the way it was, we have it in .oldSolid.

Step 2) Changing The World (or rather, world.qc)

There are several things we want to change in world.qc:

a) Add this to the worldspawn() function. Near the bottom is good. This line simply precaches the sound we're using for corpses popping (it's already in Quake, and just needs precaching).


// Corpses: corpse gib sound
precache_sound ("zombie/z_miss.wav");


b) Replace the CopyToBodyQue function with this:


// Turn this monster or bodyque into a corpse
void(entity ent, float hp) CopyToBodyQue =
{
bodyque_head.angles = ent.angles;
bodyque_head.model = ent.model;
bodyque_head.modelindex = ent.modelindex;
bodyque_head.frame = ent.frame;
bodyque_head.colormap = ent.colormap;
setorigin (bodyque_head, ent.origin + '0 0 1');

setsize(bodyque_head, ent.mins, ent.maxs);

BecomeCorpse(bodyque_head, hp, "progs/h_player.mdl");

bodyque_head.velocity = ent.velocity;
bodyque_head.flags = 0;

bodyque_head = bodyque_head.owner;
};


Make sure you get its changed parameters as well as the function body. :)

This function is normally used to leave a corpse behind when the player respawns. Player corpses were originally meant purely as decoration, and so they were nonsolid and trivial in standard Quake. However, this rendition makes them solid and poppable. Most of this happens in the BecomeCorpse function, which is next.

c) Add these functions below the CopyToBodyQue() function:


void(entity ent, float hp, string headmdl) BecomeCorpse =
{
// Gibbable Corpses: Turn me into a damagable corpse

ent.oldSolid = ent.solid = SOLID_SLIDEBOX;
ent.movetype = MOVETYPE_TOSS;
ent.takedamage = DAMAGE_AIM; // AIM makes grenades explode on impact; YES makes grenades bounce off (and prevents autoaim from targeting the corpse)
ent.semisolid = "y"; // let players walk through me

// Remember the model to use as the head when gibbed.
// BTW, if you call BecomeCorpse on a player this will change his name, so make sure to only call it on the bodyque instead.
// I arbitrarily picked .netname because it's unused by monsters and bodyques, but perhaps a different .string would be better.
ent.netname = headmdl;

// Make sure the corpse is short and won't block shots.
local vector min, max;
min = ent.mins;
max = ent.maxs;
// Set to however short you like the corpses. 30 keeps them from being so tall they block bullets, and from being so short monsters start walking on top of them.
max_z = min_z + 30;
setsize (ent, min, max);

// If a custom HP is specified, use that
if(hp > 0)
ent.health = hp;
else
ent.health = 30; // default

// If it was a monster, it's not a monster anymore (this is important to prevent crashes or corpses getting up as invincible monsters)
ent.flags = ent.flags - (ent.flags & FL_MONSTER);
ent.classname = "corpse";

// Corpses don't have a think.
// Don't repeat the last death frame for eternity (monsters do this all the time)
ent.think = SUB_Null;
ent.nextthink = -1;

// Gib when killed
ent.th_die = corpse_die;

// Clear other th_functions
ent.th_pain = SUB_Null; // Especially important: make sure it doesn't have pain frames that get it back up again! (invincible monsters bug)
ent.th_walk = SUB_Null;
ent.th_run = SUB_Null;
ent.th_stand = SUB_Null;
ent.th_missile = SUB_Null;
ent.th_melee = SUB_Null;
};

// Convenience funtion just redirects to BecomeCorpse, assuming self as ent.
void(float hp, string headmdl) MonsterBecomeCorpse =
{
BecomeCorpse(self, hp, headmdl);
};

void() corpse_die =
{
self.takedamage = DAMAGE_NO;
self.th_die = SUB_Null;

// Pick a default head model (unidentifiable flesh chunk) if one wasn't set
if(self.netname == "")
self.netname = "progs/gib3.mdl";

sound (self, CHAN_VOICE, "zombie/z_miss.wav", 1, ATTN_NORM);
// Use the head model we stored in BecomeCorpse
ThrowHead (self.netname, self.health);
// Then chuck a few more generic pieces of meat
ThrowGib ("progs/gib1.mdl", self.health);
ThrowGib ("progs/gib2.mdl", self.health);
ThrowGib ("progs/gib3.mdl", self.health);
};


void() SemisolidPreThink =
{
// Semisolid: Make all semisolid objects non-solid while this player runs his movement code

local entity e;
local string s;
s = "y";
e = find (world, semisolid, s);
while(e)
{
if(e != self)
{
e.oldSolid = e.solid;
if(e.solid == SOLID_BSP)
e.movetype = MOVETYPE_NONE;
e.solid = SOLID_NOT;
}
e = find (e, semisolid, s);
}
};

void() SemisolidPostThink =
{
// Semisolid: Make all semisolid objects (such as corpses) solid again, now that the player's move code is done.
local entity e;
local string s;
s = "y"; // self.semisolid;
e = find (world, semisolid, s);
while(e)
{
if(e != self && e.oldSolid != -1)
{
e.solid = e.oldSolid;
e.oldSolid = -1;
if(e.solid == SOLID_BSP)
e.movetype = MOVETYPE_PUSH;
}
e = find (e, semisolid, s);
}
};


MonsterBecomeCorpse() is a convenience function that simply calls BecomeCorpse with self as the entity to change. We'll be using this function in a later step.

The real work happens in BecomeCorpse. Since we're changing a monster or bodyque into a gibbable corpse, there's a few steps and precautions we have to take. Check the comments in the function for more info.

corpse_die() is simple enough: Play a sound, chuck some gibs, and make sure the corpse doesn't take damage anymore. You can use any sound you want (just make sure to precache it); I picked this one because it works so well for splattering bits.

SemisolidPreThink searches for all objects marked as semisolid (.semisolid == "y"), and makes them non-solid (after saving their solidity in .oldSolid). SemisolidPostThink reverses the process, restoring solid to oldSolid.

Fun Fact: Notice the BSP and MOVETYPE_PUSH checks? Yes, you can make BSP objects like func_wall semisolid. Players can pass through but monsters cannot (and with minor effort you can make team-friendly walls). I assume you could also do the same thing with doors and platforms, though you would want to take a special step to make their touch triggers semisolid as well.



d) OPTIONAL step: Increase the number of player corpses allowed

The bodyque is a linked loop data structure. Every time a player dies, it moves one of the bodyque entities to his position and that becomes his corpse. Then the player is then free to respawn and go off to live a fulfilling life of gleeful jumping and shooting for however long it takes him to die horribly again. The linked loop is only 4 entities long though, so corpses turn over quickly in a busy deathmatch game:
a -> b -> c -> d -> a
When all the corpses are used up in multiplayer, it just naturally loops around and starts recycling previous ones. Quake's bodyque only allows 4 corpses at a time by default to keep entity count low, which is good given that Quake ran on some pretty limited machines in its time. However, it's a little too sparse for our purposes. Now that corpses are more than mere decoration (but also something you can shoot), we might benefit from a higher corpse capacity.
NOTE that this ONLY affects player corpses (i.e. in deathmatch/coop/etc). Monsters simply become their own corpse, so you can automatically have as many corpses as you have monsters.

To increase the player corpse limit, we'll be replacing the initialization function that creates the bodyque linked loop. That function is horribly written, and adding more corpses the same way would be painful. I've completely rewritten the function to make it super-easy to change how many corpses are allowed.

Still in world.qc, replace the InitBodyQue function with this:

void() InitBodyQue =
{
local entity e, prev;
local float numBodies, num;
numBodies = 10; // how many player corpses to allow in the world at once

num = 0;
prev = world;
bodyque_head = world;
while(num < numBodies)
{
// Spawn another body
e = spawn();
e.classname = "bodyque";

// If this is the first, set the bodyque_head pointer (start of the list)
if(bodyque_head == world)
bodyque_head = e;
// Link up to previous item to form the linked list
if(prev != world)
e.owner = prev;
// Point prev to the new item
prev = e;

// Track how many we've made so we don't have an infinite loop
num = num + 1;
}

// Now that all corpse entities are created, turn the linked list into a linked loop
bodyque_head.owner = prev;
};

You can set numBodies to whatever you like. That just increases the maximum number of bodies allowed before it starts removing old ones. I picked 10 as a conservative, safe number, but it's up to you to pick the amount you want. Just keep in mind that it's more than entity count... if you choose a massive number, your multiplayer server might start filling up with gibbable corpses that players will have to mow through just to shoot each other. :) Plus, you don't want to spawn so many entities that you crash the server. Avoid numbers like 200 or 10e+309. ;)



Step 3: Making the player use the new functions:

a) PlayerPreThink
Add this line to the very bottom of the PlayerPreThink function:


SemisolidPreThink();


This calls the function that searches through all objects with a semisolid set to 'y', which is only corpses in this tutorial. For each such entity, it saves its solid in .oldSolid, then makes it non-solid.


b) PlayerPostThink
Add this line to the very top of the PlayerPostThink function:

void() PlayerPostThink =
{

SemisolidPostThink();


This puts the corpses back in their old solidity when a player finishes moving.


c) Update CopyToBodyQue calls
In client.qc, search for the two lines that call "CopyToBodyQue(self);" (they are close together). Add a second parameter so they both look like this:

CopyToBodyQue (self, 30);


Note that CopyToBodyQue now takes an HP value, so that you could pass anything you want as the corpse's health. 30 (the health of a monster_army) seems to be a good balance, in my opinion. You could choose to have the health affected by how hard the player was hit when killed, however (e.g. if he has less than -50 health, set the corpse to 1 HP). It's up to you.


Step 4: Making gibs disappear sooner (optional)

Since all these gibbable corpses could be spawning a lot of gibs, let's shorten the time they last.

Open player.qc and find the ThrowGib function. Sorten the nextthink time (how long until the gib removes itself), perhaps to "time + 4 + random()*4" (this is less than half the normal time). The new function looks like this:

void(string gibname, float dm) ThrowGib =
{
local entity new;

new = spawn();
new.origin = self.origin;
setmodel (new, gibname);
setsize (new, '0 0 0', '0 0 0');
new.velocity = VelocityForDamage (dm);
new.movetype = MOVETYPE_BOUNCE;
new.solid = SOLID_NOT;
new.avelocity_x = random()*600;
new.avelocity_y = random()*600;
new.avelocity_z = random()*600;
new.think = SUB_Remove;
new.ltime = time;

new.nextthink = time + 4 + random()*4;

new.frame = 0;
new.flags = 0;
};

Step 5: Making monsters gibbable

Making a monster gibbable is pretty easy, but you have to edit each monster file to do it. Let's start with the soldier.

Open soldier.qc, and scroll down to the death frame functions. We're interested in two of them: army_die10 and army_cdie11. These are the last frame in both death animations. Inside the two curly braces ( { and } ), add "MonsterBecomeCorpse(30, "progs/h_guard.mdl");". The animations will look like this now:


void() army_die1 =[ $death1, army_die2 ] {};
void() army_die2 =[ $death2, army_die3 ] {};
void() army_die3 =[ $death3, army_die4 ]
{self.solid = SOLID_NOT;self.ammo_shells = 5;DropBackpack();};
void() army_die4 =[ $death4, army_die5 ] {};
void() army_die5 =[ $death5, army_die6 ] {};
void() army_die6 =[ $death6, army_die7 ] {};
void() army_die7 =[ $death7, army_die8 ] {};
void() army_die8 =[ $death8, army_die9 ] {};
void() army_die9 =[ $death9, army_die10 ] {};
void() army_die10 =[ $death10, army_die10 ] { MonsterBecomeCorpse(30, "progs/h_guard.mdl"); };

void() army_cdie1 =[ $deathc1, army_cdie2 ] {};
void() army_cdie2 =[ $deathc2, army_cdie3 ] {ai_back(5);};
void() army_cdie3 =[ $deathc3, army_cdie4 ]
{self.solid = SOLID_NOT;self.ammo_shells = 5;DropBackpack();ai_back(4);};
void() army_cdie4 =[ $deathc4, army_cdie5 ] {ai_back(13);};
void() army_cdie5 =[ $deathc5, army_cdie6 ] {ai_back(3);};
void() army_cdie6 =[ $deathc6, army_cdie7 ] {ai_back(4);};
void() army_cdie7 =[ $deathc7, army_cdie8 ] {};
void() army_cdie8 =[ $deathc8, army_cdie9 ] {};
void() army_cdie9 =[ $deathc9, army_cdie10 ] {};
void() army_cdie10 =[ $deathc10, army_cdie11 ] {};
void() army_cdie11 =[ $deathc11, army_cdie11 ] { MonsterBecomeCorpse(30, "progs/h_guard.mdl"); };

You'll recognize MonsterBecomeCorpse as the function we added to world.qc earlier. 30 is the amount of HP the corpse has, and "progs/h_guard.mdl" is the model for the head to throw.
You can pick any value you want for corpse HP. You may decide soldier and dog corpses have only 1 HP and a single bullet will splatter them, but a shambler corpse has 300 HP and takes a lot of damage to splatter. It's up to you. If you pass in 0 as the HP, it will just use the default of 30 in CopyToBodyQue();
You can find the head model in each monster's normal death function, such as army_die(). It will be different for every monster, so make sure to change it in the calls to MonsterBecomeCorpse during your copy-pasting unless it's okay for shambler corpses to launch enforcer heads. :D

Now that soldiers are done, the next step is to repeat the process for the rest of the monsters you want to make gibbable. Tarbabies, chthon and shubby won't work so well with this (in fact, I strongly recommend against touching them with this tutorial), and zombies already require gibbing to die. Beyond those exceptions, however, the rest of the monsters are fair game. I recommend making the following monsters gibbable:

demon.qc
dog.qc
enforcer.qc
fish.qc (maybe)
hknight.qc
knight.qc
ogre.qc
shalrath.qc
shambler.qc
soldier.qc (done above)
wizard.qc


When all is said and done, you need only compile and test. Load up a Quake map (say, e1m1), and start blasting away at everything that moves. Then blast everything that doesn't move: corpses. Notice how you as a player can run right through the corpse uninhibited, something other corpse tutorials don't do.



What next?

From here, you could set self.semisolid = "y" on other entities you want players to be able to pass through, like a ghost monster.

Team games: Instead of setting .semisolid to "y", maybe in a team game you set it to the team name such as "red_team", and SemisolidPreThink and post think search for self.semisolid instead of "y". Then you could allow allies to move through each other (and allied walls/doors etc). I'll make a tutorial on this too, I think.

Enjoy!


Edits:
2011/04/22: Big update that makes monsters no longer use the bodyque (they just become their own corpse). Also fixed the bodyhopping bug (corpse shows stand frame for a brief moment), corpses falling through floors when the player stands in a body during its last death frame, and player corpses not working properly.
Also improved some explanations.

2011/04/13: Updated with a way to increase max corpses. Also updated bug list now that I have updated my engine (some of it was engine-related).

2011/04/11: Update to fix corpse size/height, so players can shoot past at other monsters without corpses blocking their bullets.

2011/04/01: Updated to launch correct head model from each monster's corpse. Also updated SemisolidPreThink & SemisolidPostThink to allow for BSP objects, for coders who want to take the next step on their own.
Last edited by Wazat on Sat Apr 23, 2011 5:37 pm, edited 9 times in total.
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
ooppee
Posts: 70
Joined: Thu Oct 28, 2010 2:57 am

Post by ooppee »

Only thing for the player corpses I can think of is you'd have to edit the "PlayerDie" or "PlayerDead" function in player.qc

PlayerDead is called on the last frame of death - like how you apply the corpse code on the last frame. However it only seems to be for respawn.

Perhaps you could "fake" the player corpses by making a player model become invisible during death frames and have it spawn another entity which acts the same as a dying monster - just uses the player model and it's death frames? It would be making 2 entities though - the "real" player model that's invisible - and then the "fake" model which is gibbable. Could make a separate player model that's all bloody for the death one too (so you don't have to apply the clients colors).
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

Actually, when the player hits the button to respawn, his dead body is replaced with a corpse from the bodyque. It's this bodyque corpse that I've made gibbable (haven't gotten around to making the player himself gibbable while he dies, before respawning). By all counts it should work, but when I kill myself and then find my body and launch a rocket at it, nothing. :(
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
ceriux
Posts: 2230
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Post by ceriux »

try adding a bot and let him kill you. maybe the kill command is whats messing it?
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

ceriux wrote:try adding a bot and let him kill you. maybe the kill command is whats messing it?
Actually I'm killing myself with rockets. :D
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Post by Seven »

Hello Wazat,

...just when I needed it ! :D

I will definitely use it.

Thank you very much for your effort, time and sharing this TUT.

Kind regards,
Seven
Curt
Posts: 9
Joined: Sat Mar 26, 2011 1:59 pm
Location: Germany

Thanks and a question

Post by Curt »

Thank you very much Wazat!!

I tried it but have problems that when gibbing a dead body it's head isn't there.
Next is: formerly integrated blood trails when kicking parts (integrated through mod "KickHeads") are gone.
Last Problem:
The Monster's torso when dead "melts about 25% in to the roof...).

Anyone have ideas?

Thanks again!!!
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

I'm pleased to hear people are benefiting from this tutorial!

The head isn't there because when I call ThrowHead I pass a normal gib model instead of a head. It was a cheap solution to the problem of knowing which head model to throw. Bodyque corpses are generic and they don't know what monster they came from.
To solve this, I had a choice: Throw an extra gib instead of a head, or store the head model in a field on the corpse (i.e. netname) so it would know what model to throw when gibbed. For the sake of a simple tutorial (and laziness) I chose the simple solution. :)

This tutorial doesn't have corpse/gib kicking, but I assume you could merge in the other tutorial to implement that if you know what you're doing.

I still have not gotten back and looked at why player corpses are not working. I'll update the tutorial in the first post when I figure it out (and explain what I did for those of you who have already implemented it).

I'm also going to do some tests to see if this "semisolid" method plays will with bsp objects like func_wall. If so, I may make a tutorial on implementing team-friendly semisolid walls for team-based games.

Edit: Yes, walls work. I have a test map with a wall I can pass straight through as though it were a func_illusion, but the enforcers can neither see nor pass through it. From there it's easy to make it favor a team instead of all players (self.semisolid = "team1" instead of "y", and update what the playerprethink and postthink search for).

Yay. Now to fix these lightning and axe bugs...
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

I've confirmed that player frames (i.e. player_axe3) occur between PlayerPreThink and PlayerPostThink. This explains why the lightning gun and axe fail to hit corpses. It also means any walls using the semisolid feature will not block axes and lightning without a fix. I'm still trying to find a solution -- calling SemisolidPostThink and SemisolidPreThink before & after the tracelines doesn't work for some odd reason...

And another bug with semisolid walls -- if I die in singleplayer and let the map reload, all func_walls are now non-solid (edit: all semi-solid func_walls)! It's like they don't call their initialization function on a respawn. I'm testing this in Darkplaces, btw, so it may be engine-dependent. Or it may be the way Quake reloads a map. :(

Anyway, that's my progress so far.
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
Curt
Posts: 9
Joined: Sat Mar 26, 2011 1:59 pm
Location: Germany

Post by Curt »

Thanks again for your help, Wazat!

I have an old version of Darkplaces in which the gibbing corpses work great (even with the axe...)

The problem is, our friend Seven made some really great mods in which I want to integrate the gibbing corpses. It doesn't work so far for my contentment.

Here for example the abstract from the former soldier.qc and dog.qc:

void() army_die1 = [$death1 , army_die2 ] {body_solid('-16 -16 -24', '16 16 32');};

... until ...

void() army_die10 = [$death10, army_die10] {body_nonsolid('-16 -16 -24', '16 16 -13');};


void() army_cdie1 = [$deathc1 , army_cdie2 ] {body_solid('-16 -16 -24', '16 16 32');};

... until ...

void() army_cdie11 = [$deathc11, army_cdie11] {body_nonsolid('-16 -16 -24', '16 16 -13');};

void() army_gib =
{
monster_setalpha(1);
sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
MonsterGibs("progs/h_guard.mdl", 6, "", 0, "", 0);
};

void() army_die =

{
monster_setalpha(1);
DropBackpack();
sound (self, CHAN_VOICE, "soldier/death1.wav", 1, ATTN_NORM);
if (random() < 0.5)
MonsterCorpse(self, army_die1);
else
MonsterCorpse(self, army_cdie1);



void() dog_die1 =[ $death1, dog_die2 ] {body_solid('-16 -16 -24', '16 16 12');};

... until ...

void() dog_die9 =[ $death9, dog_die9 ] {body_nonsolid('-16 -16 -24', '16 16 -8');};


void() dog_dieb1 =[ $deathb1, dog_dieb2 ] {body_solid('-16 -16 -24', '16 16 12');};

... until ...

void() dog_dieb9 =[ $deathb9, dog_dieb9 ] {body_nonsolid('-16 -16 -24', '16 16 -9');};


void() dog_gib =
{
monster_setalpha(1);
sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
MonsterGibs("progs/h_dog.mdl", 3, "", 0, "", 0);
};

void() dog_die =

{
monster_setalpha(1);
sound (self, CHAN_VOICE, "dog/ddeath.wav", 1, ATTN_NORM);
if (random() > 0.5)
MonsterCorpse(self, dog_die1);
else
MonsterCorpse(self, dog_dieb1);
};


The entry h_dog.mdl I couldn't even find in "\\progs"... Strange thing. But it works for the dog too!

If you want and you tell me how, I can upload all the former "qc-files" here for your inspection.

Best regards
ooppee
Posts: 70
Joined: Thu Oct 28, 2010 2:57 am

Post by ooppee »

No upload option - but can just upload to sendspace.com and send the link.
Curt
Posts: 9
Joined: Sat Mar 26, 2011 1:59 pm
Location: Germany

Post by Curt »

So here's the link for the old working gibbable corpses:

http://www.sendspace.com/file/g7j0ya

Greets
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Post by Seven »

Hello Curt,

ahhh, now I see what your problem is. :)

1.) Your "link for the old working gibbable corpses" is LordHavocs dpmod progs.dat.
That source is so full of modifications and special designed variables, that you can hardly handle it.
If you want to use his code, you most probably have to implement "dpextensions.qc" and that is the end of compability to other engines than DarkPlaces.
So that is not satisfying.

2.) Regarding your other issues you mentioned in your post from march, 29th :

- "lost blood trails"
That is not an issue of Wazat fantastic code. It is an issue of the gibx.mdl´s you use.
I guess you use the one from my compilation. These gibs have no blood trail flags !
I modified/tagged them for you, so that you have your blood trails up and running (Flag = 4).
http://rapidshare.com/files/455172382/f ... trails.zip

- "Monster's corpse sinks down 25% into the floor"
I just implemented Wazat´s code into my compilation and there is no such issue.
The corpses are placed on the floor just like in regular Quake (without modification) and just like with LordHavocs "progs.dat".
So the failure must be on your side I guess.

- The head gib is not spawned
I tried to find a solution for this and I guess I am very close to it.
But Wazat must help here, because my QC knowledge is very limited:
Wazat, if you call two variables in (lets say) soldier.qc:
Instead of:
{CopyMonsterToBodyQue(30);};
use:
{CopyMonsterToBodyQue(30);seven("progs/h_guard.mdl");};
(or even combine these 2 in one call)

And in world.qc you use the new one (which only transports the head model path) like this:
void(string headmdl) seven =
{
self.netname = headmdl;
};


Then, you can use the "self.netname" in your throwhead line (INDEPENDENT from the monster):
ThrowHead (self.netname, self.health);

Instead of your:
ThrowHead ("progs/gib1.mdl", self.health);



The trick I want to use is, that you give the path for the head gib from each monsters qc-file inside the call.
And the ThrowHead line uses this path in a variable.

The way I described it above ist just a rough sketch (it doesnt give compiling errors, but still doesnt work).
But I am sure, you will be able to find my failure and maybe can modify your code so that the correct head gib will be spawned.

Of course, I might be completely wrong, because of my big handicap in QuakeC knowledge, or maybe there is another limitation in your SEMISOLID code.


Again, I must say, that this TUT for gibbable corpses is the BEST I´ve ever seen.
- There is absolutely no problem anymore, that you get stuck on corpses (when you walk inclined ways upwards and a corpse is lying there) !
- There is no more falling corpses through floors !
- You can walk right through them, but they are still shootable.
All other "gibbable corpses" mods that I am aware of have at least one of these problems (except LordHavoc´s of course).

Very nice job Wazat. :)

Kind regards,
Seven
leileilol
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Post by leileilol »

Is it even possible to adapt the corpse collision stuff to soft player collision ala TF2?
i should not be here
Post Reply