Improved Gibbable Corpses Tutorial, and "Semisolid"

Discuss programming in the QuakeC language.
Lightning Hunter
Posts: 169
Joined: Wed Nov 28, 2007 6:15 am

Post by Lightning Hunter »

Any news on the tutorial update? :)
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

Guh... Sorry for going on hiatus there. It's been a very busy week, and on top of that Portal 2 came out (I got about 6 minutes into it before being interrupted so no spoilers please). :D

I'll try posting a tutorial update today or tomorrow. Just gotta weed out all the changes.
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 »

DOUBLE-POSTED!

So I've updated the tutorial. If someone doesn't mind going through it from scratch and testing that I got everything right, I'd appreciate it!

Enjoy! If you want to update an old implementation of your tutorial, most (or all?) of the chagnes are in the first 2 steps, in world.qc and defs.qc.
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.
Lightning Hunter
Posts: 169
Joined: Wed Nov 28, 2007 6:15 am

Post by Lightning Hunter »

This version is much more stable! The corpse hopping as well as all other bugs we've mentioned are gone. However, a new bug seems to have been introduced with this version. The monsters are now able to walk on top of other corpses, and corpses can stack up on top of each other. I did a fresh QuakeC install of this mod with no other mods in use. It happens in GLquake and Qrack.

Image

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

Post by Wazat »

I have noticed the corpse stacking as well. You can just make corpses taller to keep monsters from walking on them. I did some testing and figured out that 30 keeps them from stepping onto each other. Updating main page...

Hopefully that's not so tall they block bullets often again.
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.
Lightning Hunter
Posts: 169
Joined: Wed Nov 28, 2007 6:15 am

Post by Lightning Hunter »

Wazat wrote:I have noticed the corpse stacking as well. You can just make corpses taller to keep monsters from walking on them. I did some testing and figured out that 30 keeps them from stepping onto each other. Updating main page...

Hopefully that's not so tall they block bullets often again.
That causes an even larger issue of monsters being blocked by each other, however. If you kill anything in a tight hallway, the rest of the monsters will be stuck behind the coprse!

Edit: Bah, another bug. When monsters get angry at each other due to friendly fire, they will kill the monster, and proceed to stay "angry" at the corpse indefinitely. This means they will sometimes run around the dead body, doing nothing until the player destroys the corpse.
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

Hrm... the "angry at corpse" bug indicates we need to edit the monster hunting code to recognize when their enemy is dead. Should only take a couple of lines in either fight.qc, ai.qc, or monsters.qc...

I noticed the corpses blocking monsters bug. Later on I will test making a wrapper for each of the movement functions (movetogoal, walkmove, etc). The wrappers will call the prethink function, the real move function, and then the postthink function, effectively allowing monsters to walk through corpses.

The problem is monster physics are still run elsewhere, so a monster will not be able to fall through a corpse or fly past it (i.e. jumping dog/demon).

Another option, which may be far better, would be to simply make corpses very short (say, height 5). Was there a reason we avoided that? Will need some testing.

There's one final bug to consider. Making corpses DAMAGE_AIM lets grenades detonate upon touching them as desired... however, it also makes the auto-aim code aim at corpses, which can be troublesome. The fix is to make them DAMAGE_YES. Which is better, fixing the auto-aim issue or having grenades detonate when touching corpses?
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.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Wazat wrote:Hrm... the "angry at corpse" bug indicates we need to edit the monster hunting code to recognize when their enemy is dead. Should only take a couple of lines in either fight.qc, ai.qc, or monsters.qc...
Personally I think monsters walking over their comrades corpses cool. :D

In my mods I have gibbable corpses too, altough a far less sophisticated version than yours. To avoid the angry at corpse bug, I just don't reuse the original monster entity to build a corpse; instead, there's a spawnCorpse () function that clones the dead monster and remove it at the end.

Code: Select all

void()  corpse_remove =
{
    spawn_tfog (self.origin);
    sound (self, CHAN_VOICE, "common/itmspawn.wav", 1, ATTN_NORM);
    remove (self);
};

void()  corpse_die =
{
    local float r, i;

    r = 5 + rint (random() * 5);

    while (r > 0)
    {
        i = random();
        if (i > 0.8)
        {
            ThrowGib ("progs/gib1.mdl", self.health);
        }
        else if (i > 0.4)
        {
            ThrowGib ("progs/gib2.mdl", self.health);
        }
        else if (i > 0.2)
        {
            ThrowGib ("progs/gib3.mdl", self.health);
        }
        else
        {
            ThrowGib ("progs/gib4.mdl", self.health);
        }

        r = r - 1;
    }

    sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
    remove (self);
};

void(entity dead)  spawnCorpse =
{
    local entity corpse;

    corpse = spawn ();
    setmodel (corpse, dead.model);

    // change this to avoid blocking
    setsize (corpse, dead.mins, dead.maxs);

    corpse.frame = dead.frame;
    corpse.skin = dead.skin;
    corpse.colormap = dead.colormap;
    corpse.origin = dead.origin;
    corpse.angles = dead.angles;
    corpse.classname = "misc_corpse";
    corpse.solid = SOLID_SLIDEBOX;
    corpse.movetype = MOVETYPE_STEP;
    corpse.takedamage = DAMAGE_AIM;
    corpse.health = 5;
    corpse.th_die = corpse_die;
    corpse.think = corpse_remove;
    corpse.nextthink = time + 15 + random() *15;    
    remove (dead);
};
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Lightning Hunter
Posts: 169
Joined: Wed Nov 28, 2007 6:15 am

Post by Lightning Hunter »

Who would have thought that making the corpses gibbable would lead to so many other problems? :x
Wazat wrote:Hrm... the "angry at corpse" bug indicates we need to edit the monster hunting code to recognize when their enemy is dead. Should only take a couple of lines in either fight.qc, ai.qc, or monsters.qc...
Cool, glad to know this one won't take too long to fix!
Another option, which may be far better, would be to simply make corpses very short (say, height 5). Was there a reason we avoided that? Will need some testing.
This sounds like the best solution for now. Of course, you could argue that making a corpse so thin ruins the point of having them semi-solid in the first place. :P
There's one final bug to consider. Making corpses DAMAGE_AIM lets grenades detonate upon touching them as desired... however, it also makes the auto-aim code aim at corpses, which can be troublesome. The fix is to make them DAMAGE_YES. Which is better, fixing the auto-aim issue or having grenades detonate when touching corpses?
I'm not sure what my stance is here. I never use auto-aim, so that part doesn't matter to me. However, I'm not sure I want grenades detonating on corpses. On one hand, this is possibly interfering with gameplay. If you are trying to bounce grenades to hit monsters and a corpse gets in the way, it could waste ammo and/or blow up in your face. On the other hand, it makes more sense for a grenade to blow up on a corpse. I think I'm leaning more toward having the grenades go through the corpses, since gameplay is more important.
Lightning Hunter
Posts: 169
Joined: Wed Nov 28, 2007 6:15 am

Post by Lightning Hunter »

Any more work on this, Wazat? :)
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

Not really, I've been pretty distracted with other stuff. :)
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 »

Likewise with me. Did you try the idea I sent off and if so did it work?
Can't wait for the update :)
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Post by Wazat »

I have not tried it, but I'm fairly certain it would work.

I think the other option would be to call SemisolidPostThink before the lightning/axe attack and SemisolidPreThink after (but I had issues last time I tried that, not sure if I resolved them).

I've been very busy lately, and tired when I do have time, so I haven't gotten back to it. If you try it yourself, let me know how it goes. I can add it to the tutorial if it works well.
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.
Lightning Hunter
Posts: 169
Joined: Wed Nov 28, 2007 6:15 am

Re: Improved Gibbable Corpses Tutorial, and "Semisolid"

Post by Lightning Hunter »

Hey Wazat, did you ever get time to fix the last remaining bugs? The only two things preventing me from using this really cool mod is the monsters getting angry at corpses, and the corpses blocking monsters.
Wazat
Posts: 771
Joined: Fri Oct 15, 2004 9:50 pm
Location: Middle 'o the desert, USA

Re: Improved Gibbable Corpses Tutorial, and "Semisolid"

Post by Wazat »

I haven't touched this in a long time. I've been extremely busy and it's not looking easier in the future either. :/

If someone else wants to address these bugs, I encourage them to do so. I have ideas below, but I'm not really set up to implement them myself.

Monsters getting angry at corpses:
Monsters need a check in their AI to determine if what they're considering picking as a target is already dead. Conquest I think has corpses have negative health, but this tutorial doesn't so we could just check for the "corpse" classname or something unique to corpses. That way they check if it's a corpse before they fly into a rage and attack (though it's hilarious that they have this issue). I would assume this is an easy if-statement, but I might be wrong.

Corpses blocking monsters:
Hrm... I don't remember quite enough about the tutorial to say for certain, but I think you might be able to have the monster movement code pull the same trick as the player movement code, though there's caveats. This tutorial was primarily meant for multiplayer so monsters were not in my scope at the time, and they work a bit... differently.

So here's the gist (none of this is tested or fleshed out, just fyi): The player "unsolids" corpses in PlayerPreThink and "resolids" them in PlayerPostThink. This could be done by monsters as well, and we can pull it off relatively easily with a trick I learned from FrikaC's frikbot: wrappers.

Monster movement is primarily channeled through just a few functions: walkmove, movetogoal, droptofloor. If we make wrappers for these functions, we can cover the majority of their movement. However, velocity (for example, a leaping dog or fiend) is not really under our control, since monster velocity is handled like all other objects' velocities; player velocity alone is special in when it's handled. In other words, there's no monster pre and post think wrapping its physics.

So this will not be a perfect solution, because a dog or fiend will not be able to simply jump through a corpse the way it can walk through one. You could maybe do something tricky like have the leaping touch function check for touching a corpse, and either set the corpse owner to the dog/fiend (so they don't clip each other) or gib the corpse; then reinitiate the jump.

So back to wrappers. Defs.qc lists all these functions that are actually callouts from QC to the engine. For example:

float(float yaw, float dist) walkmove = #32; // returns TRUE or FALSE
float(float yaw, float dist) droptofloor= #34; // TRUE if landed on floor
void(float step) movetogoal = #67;

It turns out you can rename these. QC doesn't care what they're called, so long as they're assigned the correct ID. For example:

float(float yaw, float dist) real_walkmove = #32; // returns TRUE or FALSE
float(float yaw, float dist) real_droptofloor = #34; // TRUE if landed on floor
void(float step) real_movetogoal = #67;

At this point your code would fail to compile, because it no longer knows what "walkmove" is. However, we're going to solve that next.

float(float yaw, float dist) walkmove =
{
real_walkmove(yaw, dist);
}

What we've just done here is create a redirect. When other places in the code want to call walkmove, they call our function instead. Then our function calls walkmove so the caller gets what they expected. This is the same concept as a man-in-the-middle attack that hackers use. But we're not after the monsters' credit card information, we just want to gain control over their bodies.

So now that we have this nifty redirect function, we probably want it to do something other than simply call the real thing -- otherwise it's wasteful. Recall that the player calls SemisolidPreThink() and SemisolidPostThink() before and after movement, respectively. We would like monsters to do the same thing...

float(float yaw, float dist) walkmove =
{
SemisolidPreThink();
real_walkmove(yaw, dist);
SemisolidPostThink();
}

Now we can repeat the same process for movetogoal and droptofloor.

This is so totally untested that I can't stress enough that you should back up your code before trying it. I promise nothing. ;)

Now there's a few caveats of which we need to be aware. First, as mentioned above, dogs and fiends won't much like corpses because they can't just jump through them. We could discuss alternatives, but lets get the base working first. Second, assuming this works, it has the potential to be slow (though I'm not sure this is likely). Each monster calls one of these functions about once per frame, if not more frequently (some might actually call walkmove etc several times in one function; can't remember). Additionally, items and other objects call walkmove and droptofloor to make sure they're not stuck, or to reinitiate touch. We don't want non-monsters calling SemisolidPre/PostThink.

To solve that last problem, we can simply limit our little redirect to only treating monsters specially:
float(float yaw, float dist) walkmove =
{
if(self.flags & FL_MONSTER)
SemisolidPreThink();

real_walkmove(yaw, dist);

if(self.flags & FL_MONSTER)
SemisolidPostThink();
}

The other issues may be harder to solve. Plus I'm tired. O_o Try these things out and let me know how they work.

Have fun, man!
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.
Post Reply