Quake Style - Quake 3 Tutorials
Weapon Mods - Mega-Railgun
You can't hide behind walls no more!

This has been rather fun to play around with! It's fairly easy, too.

Open up g_weapons.c and replace the entire weapon_railgun_fire function with this one:
(change the #defines to anything you may find more appropriate, too)

/*
=================
weapon_railgun_fire
=================
*/
#define MAX_RAIL_HITS	4	// max people the rail can hit
#define MAX_RAIL_SHOTS	2	// ie. how many times through walls
#define RAIL_WALL_MAX	96	// how big a wall can be before it won't go through

void weapon_railgun_fire (gentity_t *ent, int count)
{
	vec3_t	end, oldmuzzle;
	trace_t	trace, trace2;
	gentity_t	*tent;
	gentity_t	*traceEnt;
	int			damage;
	int			radiusDamage;
	int			i;
	int			hits;
	int			unlinked;
	gentity_t	*unlinkedEntities[MAX_RAIL_HITS];

	count++;

	if (count > MAX_RAIL_SHOTS)
	{
		// too many shots through walls!
		return;
	}

	damage = 100 * s_quadFactor;
	radiusDamage = 30 * s_quadFactor;

	VectorMA (muzzle, 8192, forward, end);

	// trace only against the solids, so the railgun will go through people
	unlinked = 0;
	hits = 0;
	do {
		trap_Trace (&trace, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
		if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
			break;
		}
		traceEnt = &g_entities[ trace.entityNum ];
		if ( traceEnt->takedamage )
		{
			if( LogAccuracyHit( traceEnt, ent ) )
			{
				hits++;
			}
			G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
		}
		if ( trace.contents & CONTENTS_SOLID ) {
			break;		// we hit something solid enough to stop the beam
		}
		// unlink this entity, so the next trace will go past it
		trap_UnlinkEntity( traceEnt );
		unlinkedEntities[unlinked] = traceEnt;
		unlinked++;
	} while ( unlinked < MAX_RAIL_HITS );

	// link back in any entities we unlinked
	for ( i = 0; i < unlinked; i++ ) {
		trap_LinkEntity( unlinkedEntities[i] );
	}

	// the final trace endpos will be the terminal point of the rail trail

	// snap the endpos to integers to save net bandwidth, but nudged towards the line
	SnapVectorTowards( trace.endpos, muzzle );

	// send railgun beam effect
	tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );

	// set player number for custom colors on the railtrail
	tent->s.clientNum = ent->s.clientNum;

	VectorCopy( muzzle, tent->s.origin2 );
	// move origin a bit to come closer to the drawn gun muzzle
	VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
	VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );

	// no explosion at end if SURF_NOIMPACT, but still make the trail
	if ( trace.surfaceFlags & SURF_NOIMPACT ) {
		tent->s.eventParm = 255;	// don't make the explosion at the end
	} else {
		tent->s.eventParm = DirToByte( trace.plane.normal );
	}
	tent->s.clientNum = ent->s.clientNum;

	// give the shooter a reward sound if they have made two railgun hits in a row
	if ( hits == 0 )
	{
		// complete miss
		ent->client->accurateCount = 0;
	} else {
		// check for "impressive" reward sound
		ent->client->accurateCount += hits;
		if ( ent->client->accurateCount >= 2 )
		{
			ent->client->accurateCount -= 2;
			ent->client->ps.persistant[PERS_REWARD_COUNT]++;
			ent->client->ps.persistant[PERS_REWARD] = REWARD_IMPRESSIVE;
			ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
			// add the sprite over the player's head
			ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET );
			ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
			ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
		}
		ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
	}

	// prepare for firing through the wall
	VectorCopy (muzzle, oldmuzzle);
	VectorCopy (trace.endpos, muzzle);
	VectorMA (muzzle, RAIL_WALL_MAX, forward, muzzle);                

	if ( !( trap_PointContents( muzzle, -1 ) & CONTENTS_SOLID ) )
	{
		trap_Trace (&trace2, muzzle, NULL, NULL, trace.endpos, ent->s.number, MASK_SHOT );
		VectorCopy (trace2.endpos, muzzle);

		weapon_railgun_fire( ent, count );
	}

	VectorCopy (oldmuzzle, muzzle);
}


Only one more line to change now. Scroll down a bit further, and edit the FireWeapon function. All we need to do here is make sure an inital value of 0 railgun shots (ie. through walls) is passed to weapon_railgun_fire. Add in the blue code and take out the old code:

/*
===============
FireWeapon
===============
*/
void FireWeapon( gentity_t *ent ) {
	if (ent->client->ps.powerups[PW_QUAD] ) {
		s_quadFactor = g_quadfactor.value;
	} else {
		s_quadFactor = 1;
	}

	// track shots taken for accuracy tracking.  Grapple is not a weapon and gauntet is just not tracked
	if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) {
		ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++;
	}

	// set aiming directions
	AngleVectors (ent->client->ps.viewangles, forward, right, up);

	CalcMuzzlePoint ( ent, forward, right, up, muzzle );

	// fire the specific weapon
	switch( ent->s.weapon ) {
	case WP_GAUNTLET:
		Weapon_Gauntlet( ent );
		break;
	case WP_LIGHTNING:
		Weapon_LightningFire( ent );
		break;
	case WP_SHOTGUN:
		weapon_supershotgun_fire( ent );
		break;
	case WP_MACHINEGUN:
		if ( g_gametype.integer != GT_TEAM ) {
			Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
		} else {
			Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
		}
		break;
	case WP_GRENADE_LAUNCHER:
		weapon_grenadelauncher_fire( ent );
		break;
	case WP_ROCKET_LAUNCHER:
		Weapon_RocketLauncher_Fire( ent );
		break;
	case WP_PLASMAGUN:
		Weapon_Plasmagun_Fire( ent );
		break;
	case WP_RAILGUN:
		weapon_railgun_fire( ent );
		weapon_railgun_fire( ent, 0 );
		break;
	case WP_BFG:
		BFG_Fire( ent );
		break;
	case WP_GRAPPLING_HOOK:
		Weapon_GrapplingHook_Fire( ent );
		break;
	default:
// FIXME		G_Error( "Bad ent->s.weapon" );
		break;
	}
}

Well, have fun gibbing things through those thin Quake walls :)

-- Credits:
   Tutorial by (nobody)
   Return to QS Tutorials

-- Important:
   If you do use something from QuakeStyle in your mod, please give us credit.
   Our code is copyrighted, but we give permission to everyone to use it in any way they see fit, as long as we are recognized.