The key is the loop. It tries to predict the time and position where the trapper projectile hits the enemy if it keeps moving/accelerating in the same way.
It is basically a binary search which checks how far will the enemy be from the trapper at time <time>. If this distance is larger than the distance of the trapper projectile, try a later time, if it is smaller try an earlier time. If it is equal, we found our target.
while( highMsec - lowMsec > ARET_ACCURACY )
{
// always consider the time in the middle of the interval
int partitionMsec = ( highMsec + lowMsec ) / 2;
float time = (float)partitionMsec / 1000.0f;
// the projectile moves at constant speed ARETM_SPEED, so it's distance will be ARETM_SPEED * time
float projectileDistance = ARETM_SPEED * time;
// the enemy may accelerate, so use the speed, acceleration and jerk to predict it's position in dirToTarget
VectorMA( enemy->s.pos.trBase, time, enemy->s.pos.trDelta, dirToTarget );
VectorMA( dirToTarget, time * time, halfAcceleration, dirToTarget );
VectorMA( dirToTarget, time * time * time, thirdJerk, dirToTarget );
// Compute the difference vector and calculate it's length to find the distance of the enemy
VectorSubtract( dirToTarget, self->s.pos.trBase, dirToTarget );
distanceToTarget = VectorLength( dirToTarget );
if( projectileDistance < distanceToTarget )
// the enemy is too far away, we have to find a later time
lowMsec = partitionMsec;
else if( projectileDistance > distanceToTarget )
// the enemy is too near, we have to find an earlier time
highMsec = partitionMsec;
else if( projectileDistance == distanceToTarget )
// GOTCHA!
break; // unlikely to happen
}
The rest is just starting the projectile:
// store the direction for the projectile
VectorNormalize( dirToTarget );
vectoangles( dirToTarget, self->turretAim );
//fire at target
FireWeapon( self );
// start the attack animation
G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
// remember time for next shot
self->count = level.time + firespeed;
So if you want to change the prediction, just change the 3 VectorMA instructions. If you keep just the first, you get a simple linear prediction, or you could use VectorCopy(enemy->s.pos.trBase, dirToTarget) to have no prediction at all.