I didn't really feel like releasing this as an add-on, so here it is if you want to use it for your own purposes.
package WeaponPiercing
{
function ProjectileData::onCollision(%data, %proj, %col, %one, %hitPos, %hitNorm, %hitVel)
{
Parent::onCollision(%data, %proj, %col, %one, %hitPos, %hitNorm, %hitVel);
if(isObject(%exp = %data.explosion) && %exp.damageRadius != 0) return;
if(%col.getType() & $Typemasks::PlayerObjectType) return;
if(%col.getName() $= "GroundPlane") return;
%normVel = vectorNormalize(%hitVel);
%box = %col.getWorldBox();
//Run through each of the three axes - east, north, and up.
for(%i=0;%i<3;%i++)
{
//Get the speed of the projectile on the current axis
%value = getWord(%hitVel, %i);
//The projectile isn't moving on this axis; it'll never exit this way.
if(%value == 0) { %len[%i] = 999999; continue; }
//We're moving in the negative direction; get the lower box coordinate
else if(%value < 0) %exit = getWord(%box, %i);
//If neither of the above are true, we must be moving in the positive direction
else %exit = getWord(%box, %i + 3);
//Get the distance to travel on the current axis,
%dist = %exit - getWord(%hitPos, %i);
//and do some math to turn that into the overall distance.
%len[%i] = vectorLen(vectorScale(%normVel, %dist / getWord(%normVel, %i)));
}
%exitLen = getMin(getMin(%len0, %len1), %len2);
%exitPos = vectorAdd(%hitPos, vectorScale(vectorNormalize(%hitVel), %exitLen));
if((%pierce = %data.maxPierceDepth) $= "") %pierce = %data.directDamage * %data.muzzleVelocity / 5000;
if(%proj.piercedDist + %exitLen < %pierce)
{
%data.schedule((%exitLen / vectorLen(%hitVel)) * 1000, "spawnPiercingProjectile", %proj.client, %exitPos, %hitVel, %proj.piercedDist + %exitLen);
if(isObject(%data.stickExplosion)) %proj.delete();
}
}
};
activatePackage("WeaponPiercing");
function ProjectileData::spawnPiercingProjectile(%data, %cl, %pos, %vel, %piercedDist)
{
%newProj = new Projectile()
{
client = %cl;
datablock = %data;
initialPosition = %pos;
initialVelocity = %vel;
piercedDist = %piercedDist;
};
}