|
Quake Style - Quake 3 Tutorials Weapon Mods - Mega-Railgun You can't hide behind walls no more! |
/*
=================
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);
}
/*
===============
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;
}
}