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.