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; } }