InsideQC
 - Front Page

Tutorials are copyrighted by their author and the Inside3D staff. And may be used in any Quake modification provided that the author and the InsideQC staff are credited. Please direct any comments to the author.


Created By: MaNiAc
eMail: miket@inside3d.com
Difficulty Scale: Easy


Remember the blood that would "splat" on the walls in Duke Nukem 3D? This tutorial contains instructions on how to add the Quake "equivalent" of this effect. I have only provided direct instructions for the Shotgun, Super Shotgun, Nailgun, and Super Nailgun, but it can be added to other weapons with relative ease.

Step 1 -

We must first add the main functions for this effect. Create splat.qc and paste the following code into it:

float() crandom; //function prototype for crandom

//SplatThink: Controls behavior of splats after they have hit the wall
void() SplatThink =
{
        if ( (self.attack_finished <= time) ||
             (self.flags & FL_ONGROUND) )
        {
                remove(self); //remove if: time "runs out" or on ground
                return;
        }
        self.velocity_z = random()*-10; //splat slowly slides down walls, changing speed
        self.angles = vectoangles(self.velocity); //point in direction of movement
        self.nextthink = time + 0.2;
};

//SplatTouch: Called(by the engine) when splats touch the world or an entity
//after being spawned
void() SplatTouch =
{
        if ( (other != world) ||
             (pointcontents(self.origin) <= -3) ||
             (self.flags & FL_ONGROUND) )
        {
                remove(self); //remove if: didn't hit wall, in liquid, or on ground
                return;
        }

        self.velocity = self.avelocity = '0 0 0'; //stop moving and spinning
        self.movetype = MOVETYPE_FLY; //changed to remove effect of gravity
        self.touch = SUB_Null; //don't call this (touch) function again
        self.attack_finished = time + 4 + (2*random()); //set random "time limit"

        self.think = SplatThink;
        self.nextthink = time + 0.2;
};

//ThrowBloodSplat: This will create a blood splat at "org", moving in
//direction "dir", and owned by "own"
void(vector dir, vector org, entity own) ThrowBloodSplat =
{
        local entity splat;
        local vector dir;

        if ( !((own.flags & FL_MONSTER) ||
               (own.classname == "player")) )
                return; //only monsters and players should create splats!

        splat = spawn();
        splat.owner = own; //move through hit monster/player
        splat.movetype = MOVETYPE_TOSS; //gravity with no bouncing
        splat.solid = SOLID_BBOX; //does not move through other entities (besides owner)

        dir = normalize(dir); //make sure "dir" has length 1
        splat.velocity = dir * (450 + 50*random()); //random velocity in direction of shot
        splat.velocity_x = splat.velocity_x + crandom()*40; //randomize x velocity (+/- 40)
        splat.velocity_y = splat.velocity_y + crandom()*40; //randomize y velocity (+/- 40)
        splat.velocity_z = splat.velocity_z + 120 + 50*random(); //randomize z velocity (+ 120-170)
        splat.avelocity = '3000 1000 2000'; //spin fast!
        splat.touch = SplatTouch;
	
        splat.nextthink = time + 2;
        splat.think = SUB_Remove;

        setmodel (splat, "progs/zom_gib.mdl");
        setsize (splat, '0 0 0', '0 0 0');     
        setorigin (splat, org); //start splat at point of damage
};
The code has been commented in many places to make it more easily readable.

Step 2 -

Add the following above "weapons.qc" in progs.src:

splat.qc
This is needed for the compiler to compile the new QC file.

Step 3 -

Add the following to the bottom of W_Precache in weapons.qc:

precache_model ("progs/zom_gib.mdl");
This loads the model used for blood splats into memory when the level starts.

Step 4 -

Now we need to make some of the weapons create blood splats when they damage enemies...

In weapons.qc, add the following after the line "if (trace_ent.takedamage)" in TraceAttack:

if(random() < 0.4) //only 2 in 5 times to prevent overflows
        ThrowBloodSplat(dir, trace_endpos, trace_ent); //throw blood splat in direction of shot
Also in weapons.qc, add the following after the line "if (other.takedamage)" in spike_touch AND superspike_touch:
if (random() < 0.8) //only 4 in 5 times to prevent overflows
{
        self.angles_x = self.angles_x*-1; //account for error in makevectors with non-player entities
        makevectors(self.angles); //find direction nail is facing
        ThrowBloodSplat(v_forward, self.origin, other); //throw blood splat in direction nail is facing
}
If added to both functions, this will affect the Nailgun and Super Nailgun.

Use this technique to add blood splats to any other weapon you wish to. The important thing to remember is that you need to have the angle of impact. This will usually be either the direction the shot was fired (for instant hit weapons) or the direction the projectile is facing for projectile weapons.

Step 5 -

Compile and run! The Super Shotgun is definitely an 'interesting' weapon with this modification!