Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

g_weapon.c (29233B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 // g_weapon.c 
     24 // perform the server side effects of a weapon firing
     25 
     26 #include "g_local.h"
     27 
     28 static	float	s_quadFactor;
     29 static	vec3_t	forward, right, up;
     30 static	vec3_t	muzzle;
     31 
     32 #define NUM_NAILSHOTS 15
     33 
     34 /*
     35 ================
     36 G_BounceProjectile
     37 ================
     38 */
     39 void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
     40 	vec3_t v, newv;
     41 	float dot;
     42 
     43 	VectorSubtract( impact, start, v );
     44 	dot = DotProduct( v, dir );
     45 	VectorMA( v, -2*dot, dir, newv );
     46 
     47 	VectorNormalize(newv);
     48 	VectorMA(impact, 8192, newv, endout);
     49 }
     50 
     51 
     52 /*
     53 ======================================================================
     54 
     55 GAUNTLET
     56 
     57 ======================================================================
     58 */
     59 
     60 void Weapon_Gauntlet( gentity_t *ent ) {
     61 
     62 }
     63 
     64 /*
     65 ===============
     66 CheckGauntletAttack
     67 ===============
     68 */
     69 qboolean CheckGauntletAttack( gentity_t *ent ) {
     70 	trace_t		tr;
     71 	vec3_t		end;
     72 	gentity_t	*tent;
     73 	gentity_t	*traceEnt;
     74 	int			damage;
     75 
     76 	// set aiming directions
     77 	AngleVectors (ent->client->ps.viewangles, forward, right, up);
     78 
     79 	CalcMuzzlePoint ( ent, forward, right, up, muzzle );
     80 
     81 	VectorMA (muzzle, 32, forward, end);
     82 
     83 	trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
     84 	if ( tr.surfaceFlags & SURF_NOIMPACT ) {
     85 		return qfalse;
     86 	}
     87 
     88 	traceEnt = &g_entities[ tr.entityNum ];
     89 
     90 	// send blood impact
     91 	if ( traceEnt->takedamage && traceEnt->client ) {
     92 		tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
     93 		tent->s.otherEntityNum = traceEnt->s.number;
     94 		tent->s.eventParm = DirToByte( tr.plane.normal );
     95 		tent->s.weapon = ent->s.weapon;
     96 	}
     97 
     98 	if ( !traceEnt->takedamage) {
     99 		return qfalse;
    100 	}
    101 
    102 	if (ent->client->ps.powerups[PW_QUAD] ) {
    103 		G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
    104 		s_quadFactor = g_quadfactor.value;
    105 	} else {
    106 		s_quadFactor = 1;
    107 	}
    108 #ifdef MISSIONPACK
    109 	if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
    110 		s_quadFactor *= 2;
    111 	}
    112 #endif
    113 
    114 	damage = 50 * s_quadFactor;
    115 	G_Damage( traceEnt, ent, ent, forward, tr.endpos,
    116 		damage, 0, MOD_GAUNTLET );
    117 
    118 	return qtrue;
    119 }
    120 
    121 
    122 /*
    123 ======================================================================
    124 
    125 MACHINEGUN
    126 
    127 ======================================================================
    128 */
    129 
    130 /*
    131 ======================
    132 SnapVectorTowards
    133 
    134 Round a vector to integers for more efficient network
    135 transmission, but make sure that it rounds towards a given point
    136 rather than blindly truncating.  This prevents it from truncating 
    137 into a wall.
    138 ======================
    139 */
    140 void SnapVectorTowards( vec3_t v, vec3_t to ) {
    141 	int		i;
    142 
    143 	for ( i = 0 ; i < 3 ; i++ ) {
    144 		if ( to[i] <= v[i] ) {
    145 			v[i] = (int)v[i];
    146 		} else {
    147 			v[i] = (int)v[i] + 1;
    148 		}
    149 	}
    150 }
    151 
    152 #ifdef MISSIONPACK
    153 #define CHAINGUN_SPREAD		600
    154 #endif
    155 #define MACHINEGUN_SPREAD	200
    156 #define	MACHINEGUN_DAMAGE	7
    157 #define	MACHINEGUN_TEAM_DAMAGE	5		// wimpier MG in teamplay
    158 
    159 void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
    160 	trace_t		tr;
    161 	vec3_t		end;
    162 #ifdef MISSIONPACK
    163 	vec3_t		impactpoint, bouncedir;
    164 #endif
    165 	float		r;
    166 	float		u;
    167 	gentity_t	*tent;
    168 	gentity_t	*traceEnt;
    169 	int			i, passent;
    170 
    171 	damage *= s_quadFactor;
    172 
    173 	r = random() * M_PI * 2.0f;
    174 	u = sin(r) * crandom() * spread * 16;
    175 	r = cos(r) * crandom() * spread * 16;
    176 	VectorMA (muzzle, 8192*16, forward, end);
    177 	VectorMA (end, r, right, end);
    178 	VectorMA (end, u, up, end);
    179 
    180 	passent = ent->s.number;
    181 	for (i = 0; i < 10; i++) {
    182 
    183 		trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
    184 		if ( tr.surfaceFlags & SURF_NOIMPACT ) {
    185 			return;
    186 		}
    187 
    188 		traceEnt = &g_entities[ tr.entityNum ];
    189 
    190 		// snap the endpos to integers, but nudged towards the line
    191 		SnapVectorTowards( tr.endpos, muzzle );
    192 
    193 		// send bullet impact
    194 		if ( traceEnt->takedamage && traceEnt->client ) {
    195 			tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
    196 			tent->s.eventParm = traceEnt->s.number;
    197 			if( LogAccuracyHit( traceEnt, ent ) ) {
    198 				ent->client->accuracy_hits++;
    199 			}
    200 		} else {
    201 			tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
    202 			tent->s.eventParm = DirToByte( tr.plane.normal );
    203 		}
    204 		tent->s.otherEntityNum = ent->s.number;
    205 
    206 		if ( traceEnt->takedamage) {
    207 #ifdef MISSIONPACK
    208 			if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
    209 				if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
    210 					G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
    211 					VectorCopy( impactpoint, muzzle );
    212 					// the player can hit him/herself with the bounced rail
    213 					passent = ENTITYNUM_NONE;
    214 				}
    215 				else {
    216 					VectorCopy( tr.endpos, muzzle );
    217 					passent = traceEnt->s.number;
    218 				}
    219 				continue;
    220 			}
    221 			else {
    222 #endif
    223 				G_Damage( traceEnt, ent, ent, forward, tr.endpos,
    224 					damage, 0, MOD_MACHINEGUN);
    225 #ifdef MISSIONPACK
    226 			}
    227 #endif
    228 		}
    229 		break;
    230 	}
    231 }
    232 
    233 
    234 /*
    235 ======================================================================
    236 
    237 BFG
    238 
    239 ======================================================================
    240 */
    241 
    242 void BFG_Fire ( gentity_t *ent ) {
    243 	gentity_t	*m;
    244 
    245 	m = fire_bfg (ent, muzzle, forward);
    246 	m->damage *= s_quadFactor;
    247 	m->splashDamage *= s_quadFactor;
    248 
    249 //	VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );	// "real" physics
    250 }
    251 
    252 
    253 /*
    254 ======================================================================
    255 
    256 SHOTGUN
    257 
    258 ======================================================================
    259 */
    260 
    261 // DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT	are in bg_public.h, because
    262 // client predicts same spreads
    263 #define	DEFAULT_SHOTGUN_DAMAGE	10
    264 
    265 qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
    266 	trace_t		tr;
    267 	int			damage, i, passent;
    268 	gentity_t	*traceEnt;
    269 #ifdef MISSIONPACK
    270 	vec3_t		impactpoint, bouncedir;
    271 #endif
    272 	vec3_t		tr_start, tr_end;
    273 
    274 	passent = ent->s.number;
    275 	VectorCopy( start, tr_start );
    276 	VectorCopy( end, tr_end );
    277 	for (i = 0; i < 10; i++) {
    278 		trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT);
    279 		traceEnt = &g_entities[ tr.entityNum ];
    280 
    281 		// send bullet impact
    282 		if (  tr.surfaceFlags & SURF_NOIMPACT ) {
    283 			return qfalse;
    284 		}
    285 
    286 		if ( traceEnt->takedamage) {
    287 			damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
    288 #ifdef MISSIONPACK
    289 			if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
    290 				if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
    291 					G_BounceProjectile( tr_start, impactpoint, bouncedir, tr_end );
    292 					VectorCopy( impactpoint, tr_start );
    293 					// the player can hit him/herself with the bounced rail
    294 					passent = ENTITYNUM_NONE;
    295 				}
    296 				else {
    297 					VectorCopy( tr.endpos, tr_start );
    298 					passent = traceEnt->s.number;
    299 				}
    300 				continue;
    301 			}
    302 			else {
    303 				G_Damage( traceEnt, ent, ent, forward, tr.endpos,
    304 					damage, 0, MOD_SHOTGUN);
    305 				if( LogAccuracyHit( traceEnt, ent ) ) {
    306 					return qtrue;
    307 				}
    308 			}
    309 #else
    310 			G_Damage( traceEnt, ent, ent, forward, tr.endpos,	damage, 0, MOD_SHOTGUN);
    311 				if( LogAccuracyHit( traceEnt, ent ) ) {
    312 					return qtrue;
    313 				}
    314 #endif
    315 		}
    316 		return qfalse;
    317 	}
    318 	return qfalse;
    319 }
    320 
    321 // this should match CG_ShotgunPattern
    322 void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
    323 	int			i;
    324 	float		r, u;
    325 	vec3_t		end;
    326 	vec3_t		forward, right, up;
    327 	int			oldScore;
    328 	qboolean	hitClient = qfalse;
    329 
    330 	// derive the right and up vectors from the forward vector, because
    331 	// the client won't have any other information
    332 	VectorNormalize2( origin2, forward );
    333 	PerpendicularVector( right, forward );
    334 	CrossProduct( forward, right, up );
    335 
    336 	oldScore = ent->client->ps.persistant[PERS_SCORE];
    337 
    338 	// generate the "random" spread pattern
    339 	for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
    340 		r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
    341 		u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
    342 		VectorMA( origin, 8192 * 16, forward, end);
    343 		VectorMA (end, r, right, end);
    344 		VectorMA (end, u, up, end);
    345 		if( ShotgunPellet( origin, end, ent ) && !hitClient ) {
    346 			hitClient = qtrue;
    347 			ent->client->accuracy_hits++;
    348 		}
    349 	}
    350 }
    351 
    352 
    353 void weapon_supershotgun_fire (gentity_t *ent) {
    354 	gentity_t		*tent;
    355 
    356 	// send shotgun blast
    357 	tent = G_TempEntity( muzzle, EV_SHOTGUN );
    358 	VectorScale( forward, 4096, tent->s.origin2 );
    359 	SnapVector( tent->s.origin2 );
    360 	tent->s.eventParm = rand() & 255;		// seed for spread pattern
    361 	tent->s.otherEntityNum = ent->s.number;
    362 
    363 	ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
    364 }
    365 
    366 
    367 /*
    368 ======================================================================
    369 
    370 GRENADE LAUNCHER
    371 
    372 ======================================================================
    373 */
    374 
    375 void weapon_grenadelauncher_fire (gentity_t *ent) {
    376 	gentity_t	*m;
    377 
    378 	// extra vertical velocity
    379 	forward[2] += 0.2f;
    380 	VectorNormalize( forward );
    381 
    382 	m = fire_grenade (ent, muzzle, forward);
    383 	m->damage *= s_quadFactor;
    384 	m->splashDamage *= s_quadFactor;
    385 
    386 //	VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );	// "real" physics
    387 }
    388 
    389 /*
    390 ======================================================================
    391 
    392 ROCKET
    393 
    394 ======================================================================
    395 */
    396 
    397 void Weapon_RocketLauncher_Fire (gentity_t *ent) {
    398 	gentity_t	*m;
    399 
    400 	m = fire_rocket (ent, muzzle, forward);
    401 	m->damage *= s_quadFactor;
    402 	m->splashDamage *= s_quadFactor;
    403 
    404 //	VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );	// "real" physics
    405 }
    406 
    407 
    408 /*
    409 ======================================================================
    410 
    411 PLASMA GUN
    412 
    413 ======================================================================
    414 */
    415 
    416 void Weapon_Plasmagun_Fire (gentity_t *ent) {
    417 	gentity_t	*m;
    418 
    419 	m = fire_plasma (ent, muzzle, forward);
    420 	m->damage *= s_quadFactor;
    421 	m->splashDamage *= s_quadFactor;
    422 
    423 //	VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );	// "real" physics
    424 }
    425 
    426 /*
    427 ======================================================================
    428 
    429 RAILGUN
    430 
    431 ======================================================================
    432 */
    433 
    434 
    435 /*
    436 =================
    437 weapon_railgun_fire
    438 =================
    439 */
    440 #define	MAX_RAIL_HITS	4
    441 void weapon_railgun_fire (gentity_t *ent) {
    442 	vec3_t		end;
    443 #ifdef MISSIONPACK
    444 	vec3_t impactpoint, bouncedir;
    445 #endif
    446 	trace_t		trace;
    447 	gentity_t	*tent;
    448 	gentity_t	*traceEnt;
    449 	int			damage;
    450 	int			i;
    451 	int			hits;
    452 	int			unlinked;
    453 	int			passent;
    454 	gentity_t	*unlinkedEntities[MAX_RAIL_HITS];
    455 
    456 	damage = 100 * s_quadFactor;
    457 
    458 	VectorMA (muzzle, 8192, forward, end);
    459 
    460 	// trace only against the solids, so the railgun will go through people
    461 	unlinked = 0;
    462 	hits = 0;
    463 	passent = ent->s.number;
    464 	do {
    465 		trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
    466 		if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
    467 			break;
    468 		}
    469 		traceEnt = &g_entities[ trace.entityNum ];
    470 		if ( traceEnt->takedamage ) {
    471 #ifdef MISSIONPACK
    472 			if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
    473 				if ( G_InvulnerabilityEffect( traceEnt, forward, trace.endpos, impactpoint, bouncedir ) ) {
    474 					G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
    475 					// snap the endpos to integers to save net bandwidth, but nudged towards the line
    476 					SnapVectorTowards( trace.endpos, muzzle );
    477 					// send railgun beam effect
    478 					tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
    479 					// set player number for custom colors on the railtrail
    480 					tent->s.clientNum = ent->s.clientNum;
    481 					VectorCopy( muzzle, tent->s.origin2 );
    482 					// move origin a bit to come closer to the drawn gun muzzle
    483 					VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
    484 					VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
    485 					tent->s.eventParm = 255;	// don't make the explosion at the end
    486 					//
    487 					VectorCopy( impactpoint, muzzle );
    488 					// the player can hit him/herself with the bounced rail
    489 					passent = ENTITYNUM_NONE;
    490 				}
    491 			}
    492 			else {
    493 				if( LogAccuracyHit( traceEnt, ent ) ) {
    494 					hits++;
    495 				}
    496 				G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
    497 			}
    498 #else
    499 				if( LogAccuracyHit( traceEnt, ent ) ) {
    500 					hits++;
    501 				}
    502 				G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
    503 #endif
    504 		}
    505 		if ( trace.contents & CONTENTS_SOLID ) {
    506 			break;		// we hit something solid enough to stop the beam
    507 		}
    508 		// unlink this entity, so the next trace will go past it
    509 		trap_UnlinkEntity( traceEnt );
    510 		unlinkedEntities[unlinked] = traceEnt;
    511 		unlinked++;
    512 	} while ( unlinked < MAX_RAIL_HITS );
    513 
    514 	// link back in any entities we unlinked
    515 	for ( i = 0 ; i < unlinked ; i++ ) {
    516 		trap_LinkEntity( unlinkedEntities[i] );
    517 	}
    518 
    519 	// the final trace endpos will be the terminal point of the rail trail
    520 
    521 	// snap the endpos to integers to save net bandwidth, but nudged towards the line
    522 	SnapVectorTowards( trace.endpos, muzzle );
    523 
    524 	// send railgun beam effect
    525 	tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
    526 
    527 	// set player number for custom colors on the railtrail
    528 	tent->s.clientNum = ent->s.clientNum;
    529 
    530 	VectorCopy( muzzle, tent->s.origin2 );
    531 	// move origin a bit to come closer to the drawn gun muzzle
    532 	VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
    533 	VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
    534 
    535 	// no explosion at end if SURF_NOIMPACT, but still make the trail
    536 	if ( trace.surfaceFlags & SURF_NOIMPACT ) {
    537 		tent->s.eventParm = 255;	// don't make the explosion at the end
    538 	} else {
    539 		tent->s.eventParm = DirToByte( trace.plane.normal );
    540 	}
    541 	tent->s.clientNum = ent->s.clientNum;
    542 
    543 	// give the shooter a reward sound if they have made two railgun hits in a row
    544 	if ( hits == 0 ) {
    545 		// complete miss
    546 		ent->client->accurateCount = 0;
    547 	} else {
    548 		// check for "impressive" reward sound
    549 		ent->client->accurateCount += hits;
    550 		if ( ent->client->accurateCount >= 2 ) {
    551 			ent->client->accurateCount -= 2;
    552 			ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
    553 			// add the sprite over the player's head
    554 			ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
    555 			ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
    556 			ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
    557 		}
    558 		ent->client->accuracy_hits++;
    559 	}
    560 
    561 }
    562 
    563 
    564 /*
    565 ======================================================================
    566 
    567 GRAPPLING HOOK
    568 
    569 ======================================================================
    570 */
    571 
    572 void Weapon_GrapplingHook_Fire (gentity_t *ent)
    573 {
    574 	if (!ent->client->fireHeld && !ent->client->hook)
    575 		fire_grapple (ent, muzzle, forward);
    576 
    577 	ent->client->fireHeld = qtrue;
    578 }
    579 
    580 void Weapon_HookFree (gentity_t *ent)
    581 {
    582 	ent->parent->client->hook = NULL;
    583 	ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
    584 	G_FreeEntity( ent );
    585 }
    586 
    587 void Weapon_HookThink (gentity_t *ent)
    588 {
    589 	if (ent->enemy) {
    590 		vec3_t v, oldorigin;
    591 
    592 		VectorCopy(ent->r.currentOrigin, oldorigin);
    593 		v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5;
    594 		v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5;
    595 		v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5;
    596 		SnapVectorTowards( v, oldorigin );	// save net bandwidth
    597 
    598 		G_SetOrigin( ent, v );
    599 	}
    600 
    601 	VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
    602 }
    603 
    604 /*
    605 ======================================================================
    606 
    607 LIGHTNING GUN
    608 
    609 ======================================================================
    610 */
    611 
    612 void Weapon_LightningFire( gentity_t *ent ) {
    613 	trace_t		tr;
    614 	vec3_t		end;
    615 #ifdef MISSIONPACK
    616 	vec3_t impactpoint, bouncedir;
    617 #endif
    618 	gentity_t	*traceEnt, *tent;
    619 	int			damage, i, passent;
    620 
    621 	damage = 8 * s_quadFactor;
    622 
    623 	passent = ent->s.number;
    624 	for (i = 0; i < 10; i++) {
    625 		VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
    626 
    627 		trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
    628 
    629 #ifdef MISSIONPACK
    630 		// if not the first trace (the lightning bounced of an invulnerability sphere)
    631 		if (i) {
    632 			// add bounced off lightning bolt temp entity
    633 			// the first lightning bolt is a cgame only visual
    634 			//
    635 			tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT );
    636 			VectorCopy( tr.endpos, end );
    637 			SnapVector( end );
    638 			VectorCopy( end, tent->s.origin2 );
    639 		}
    640 #endif
    641 		if ( tr.entityNum == ENTITYNUM_NONE ) {
    642 			return;
    643 		}
    644 
    645 		traceEnt = &g_entities[ tr.entityNum ];
    646 
    647 		if ( traceEnt->takedamage) {
    648 #ifdef MISSIONPACK
    649 			if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
    650 				if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
    651 					G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
    652 					VectorCopy( impactpoint, muzzle );
    653 					VectorSubtract( end, impactpoint, forward );
    654 					VectorNormalize(forward);
    655 					// the player can hit him/herself with the bounced lightning
    656 					passent = ENTITYNUM_NONE;
    657 				}
    658 				else {
    659 					VectorCopy( tr.endpos, muzzle );
    660 					passent = traceEnt->s.number;
    661 				}
    662 				continue;
    663 			}
    664 			else {
    665 				G_Damage( traceEnt, ent, ent, forward, tr.endpos,
    666 					damage, 0, MOD_LIGHTNING);
    667 			}
    668 #else
    669 				G_Damage( traceEnt, ent, ent, forward, tr.endpos,
    670 					damage, 0, MOD_LIGHTNING);
    671 #endif
    672 		}
    673 
    674 		if ( traceEnt->takedamage && traceEnt->client ) {
    675 			tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
    676 			tent->s.otherEntityNum = traceEnt->s.number;
    677 			tent->s.eventParm = DirToByte( tr.plane.normal );
    678 			tent->s.weapon = ent->s.weapon;
    679 			if( LogAccuracyHit( traceEnt, ent ) ) {
    680 				ent->client->accuracy_hits++;
    681 			}
    682 		} else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
    683 			tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
    684 			tent->s.eventParm = DirToByte( tr.plane.normal );
    685 		}
    686 
    687 		break;
    688 	}
    689 }
    690 
    691 #ifdef MISSIONPACK
    692 /*
    693 ======================================================================
    694 
    695 NAILGUN
    696 
    697 ======================================================================
    698 */
    699 
    700 void Weapon_Nailgun_Fire (gentity_t *ent) {
    701 	gentity_t	*m;
    702 	int			count;
    703 
    704 	for( count = 0; count < NUM_NAILSHOTS; count++ ) {
    705 		m = fire_nail (ent, muzzle, forward, right, up );
    706 		m->damage *= s_quadFactor;
    707 		m->splashDamage *= s_quadFactor;
    708 	}
    709 
    710 //	VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );	// "real" physics
    711 }
    712 
    713 
    714 /*
    715 ======================================================================
    716 
    717 PROXIMITY MINE LAUNCHER
    718 
    719 ======================================================================
    720 */
    721 
    722 void weapon_proxlauncher_fire (gentity_t *ent) {
    723 	gentity_t	*m;
    724 
    725 	// extra vertical velocity
    726 	forward[2] += 0.2f;
    727 	VectorNormalize( forward );
    728 
    729 	m = fire_prox (ent, muzzle, forward);
    730 	m->damage *= s_quadFactor;
    731 	m->splashDamage *= s_quadFactor;
    732 
    733 //	VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );	// "real" physics
    734 }
    735 
    736 #endif
    737 
    738 //======================================================================
    739 
    740 
    741 /*
    742 ===============
    743 LogAccuracyHit
    744 ===============
    745 */
    746 qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
    747 	if( !target->takedamage ) {
    748 		return qfalse;
    749 	}
    750 
    751 	if ( target == attacker ) {
    752 		return qfalse;
    753 	}
    754 
    755 	if( !target->client ) {
    756 		return qfalse;
    757 	}
    758 
    759 	if( !attacker->client ) {
    760 		return qfalse;
    761 	}
    762 
    763 	if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
    764 		return qfalse;
    765 	}
    766 
    767 	if ( OnSameTeam( target, attacker ) ) {
    768 		return qfalse;
    769 	}
    770 
    771 	return qtrue;
    772 }
    773 
    774 
    775 /*
    776 ===============
    777 CalcMuzzlePoint
    778 
    779 set muzzle location relative to pivoting eye
    780 ===============
    781 */
    782 void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
    783 	VectorCopy( ent->s.pos.trBase, muzzlePoint );
    784 	muzzlePoint[2] += ent->client->ps.viewheight;
    785 	VectorMA( muzzlePoint, 14, forward, muzzlePoint );
    786 	// snap to integer coordinates for more efficient network bandwidth usage
    787 	SnapVector( muzzlePoint );
    788 }
    789 
    790 /*
    791 ===============
    792 CalcMuzzlePointOrigin
    793 
    794 set muzzle location relative to pivoting eye
    795 ===============
    796 */
    797 void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
    798 	VectorCopy( ent->s.pos.trBase, muzzlePoint );
    799 	muzzlePoint[2] += ent->client->ps.viewheight;
    800 	VectorMA( muzzlePoint, 14, forward, muzzlePoint );
    801 	// snap to integer coordinates for more efficient network bandwidth usage
    802 	SnapVector( muzzlePoint );
    803 }
    804 
    805 
    806 
    807 /*
    808 ===============
    809 FireWeapon
    810 ===============
    811 */
    812 void FireWeapon( gentity_t *ent ) {
    813 	if (ent->client->ps.powerups[PW_QUAD] ) {
    814 		s_quadFactor = g_quadfactor.value;
    815 	} else {
    816 		s_quadFactor = 1;
    817 	}
    818 #ifdef MISSIONPACK
    819 	if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
    820 		s_quadFactor *= 2;
    821 	}
    822 #endif
    823 
    824 	// track shots taken for accuracy tracking.  Grapple is not a weapon and gauntet is just not tracked
    825 	if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) {
    826 #ifdef MISSIONPACK
    827 		if( ent->s.weapon == WP_NAILGUN ) {
    828 			ent->client->accuracy_shots += NUM_NAILSHOTS;
    829 		} else {
    830 			ent->client->accuracy_shots++;
    831 		}
    832 #else
    833 		ent->client->accuracy_shots++;
    834 #endif
    835 	}
    836 
    837 	// set aiming directions
    838 	AngleVectors (ent->client->ps.viewangles, forward, right, up);
    839 
    840 	CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, forward, right, up, muzzle );
    841 
    842 	// fire the specific weapon
    843 	switch( ent->s.weapon ) {
    844 	case WP_GAUNTLET:
    845 		Weapon_Gauntlet( ent );
    846 		break;
    847 	case WP_LIGHTNING:
    848 		Weapon_LightningFire( ent );
    849 		break;
    850 	case WP_SHOTGUN:
    851 		weapon_supershotgun_fire( ent );
    852 		break;
    853 	case WP_MACHINEGUN:
    854 		if ( g_gametype.integer != GT_TEAM ) {
    855 			Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
    856 		} else {
    857 			Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
    858 		}
    859 		break;
    860 	case WP_GRENADE_LAUNCHER:
    861 		weapon_grenadelauncher_fire( ent );
    862 		break;
    863 	case WP_ROCKET_LAUNCHER:
    864 		Weapon_RocketLauncher_Fire( ent );
    865 		break;
    866 	case WP_PLASMAGUN:
    867 		Weapon_Plasmagun_Fire( ent );
    868 		break;
    869 	case WP_RAILGUN:
    870 		weapon_railgun_fire( ent );
    871 		break;
    872 	case WP_BFG:
    873 		BFG_Fire( ent );
    874 		break;
    875 	case WP_GRAPPLING_HOOK:
    876 		Weapon_GrapplingHook_Fire( ent );
    877 		break;
    878 #ifdef MISSIONPACK
    879 	case WP_NAILGUN:
    880 		Weapon_Nailgun_Fire( ent );
    881 		break;
    882 	case WP_PROX_LAUNCHER:
    883 		weapon_proxlauncher_fire( ent );
    884 		break;
    885 	case WP_CHAINGUN:
    886 		Bullet_Fire( ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE );
    887 		break;
    888 #endif
    889 	default:
    890 // FIXME		G_Error( "Bad ent->s.weapon" );
    891 		break;
    892 	}
    893 }
    894 
    895 
    896 #ifdef MISSIONPACK
    897 
    898 /*
    899 ===============
    900 KamikazeRadiusDamage
    901 ===============
    902 */
    903 static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius ) {
    904 	float		dist;
    905 	gentity_t	*ent;
    906 	int			entityList[MAX_GENTITIES];
    907 	int			numListedEntities;
    908 	vec3_t		mins, maxs;
    909 	vec3_t		v;
    910 	vec3_t		dir;
    911 	int			i, e;
    912 
    913 	if ( radius < 1 ) {
    914 		radius = 1;
    915 	}
    916 
    917 	for ( i = 0 ; i < 3 ; i++ ) {
    918 		mins[i] = origin[i] - radius;
    919 		maxs[i] = origin[i] + radius;
    920 	}
    921 
    922 	numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
    923 
    924 	for ( e = 0 ; e < numListedEntities ; e++ ) {
    925 		ent = &g_entities[entityList[ e ]];
    926 
    927 		if (!ent->takedamage) {
    928 			continue;
    929 		}
    930 
    931 		// dont hit things we have already hit
    932 		if( ent->kamikazeTime > level.time ) {
    933 			continue;
    934 		}
    935 
    936 		// find the distance from the edge of the bounding box
    937 		for ( i = 0 ; i < 3 ; i++ ) {
    938 			if ( origin[i] < ent->r.absmin[i] ) {
    939 				v[i] = ent->r.absmin[i] - origin[i];
    940 			} else if ( origin[i] > ent->r.absmax[i] ) {
    941 				v[i] = origin[i] - ent->r.absmax[i];
    942 			} else {
    943 				v[i] = 0;
    944 			}
    945 		}
    946 
    947 		dist = VectorLength( v );
    948 		if ( dist >= radius ) {
    949 			continue;
    950 		}
    951 
    952 //		if( CanDamage (ent, origin) ) {
    953 			VectorSubtract (ent->r.currentOrigin, origin, dir);
    954 			// push the center of mass higher than the origin so players
    955 			// get knocked into the air more
    956 			dir[2] += 24;
    957 			G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
    958 			ent->kamikazeTime = level.time + 3000;
    959 //		}
    960 	}
    961 }
    962 
    963 /*
    964 ===============
    965 KamikazeShockWave
    966 ===============
    967 */
    968 static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, float push, float radius ) {
    969 	float		dist;
    970 	gentity_t	*ent;
    971 	int			entityList[MAX_GENTITIES];
    972 	int			numListedEntities;
    973 	vec3_t		mins, maxs;
    974 	vec3_t		v;
    975 	vec3_t		dir;
    976 	int			i, e;
    977 
    978 	if ( radius < 1 )
    979 		radius = 1;
    980 
    981 	for ( i = 0 ; i < 3 ; i++ ) {
    982 		mins[i] = origin[i] - radius;
    983 		maxs[i] = origin[i] + radius;
    984 	}
    985 
    986 	numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
    987 
    988 	for ( e = 0 ; e < numListedEntities ; e++ ) {
    989 		ent = &g_entities[entityList[ e ]];
    990 
    991 		// dont hit things we have already hit
    992 		if( ent->kamikazeShockTime > level.time ) {
    993 			continue;
    994 		}
    995 
    996 		// find the distance from the edge of the bounding box
    997 		for ( i = 0 ; i < 3 ; i++ ) {
    998 			if ( origin[i] < ent->r.absmin[i] ) {
    999 				v[i] = ent->r.absmin[i] - origin[i];
   1000 			} else if ( origin[i] > ent->r.absmax[i] ) {
   1001 				v[i] = origin[i] - ent->r.absmax[i];
   1002 			} else {
   1003 				v[i] = 0;
   1004 			}
   1005 		}
   1006 
   1007 		dist = VectorLength( v );
   1008 		if ( dist >= radius ) {
   1009 			continue;
   1010 		}
   1011 
   1012 //		if( CanDamage (ent, origin) ) {
   1013 			VectorSubtract (ent->r.currentOrigin, origin, dir);
   1014 			dir[2] += 24;
   1015 			G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
   1016 			//
   1017 			dir[2] = 0;
   1018 			VectorNormalize(dir);
   1019 			if ( ent->client ) {
   1020 				ent->client->ps.velocity[0] = dir[0] * push;
   1021 				ent->client->ps.velocity[1] = dir[1] * push;
   1022 				ent->client->ps.velocity[2] = 100;
   1023 			}
   1024 			ent->kamikazeShockTime = level.time + 3000;
   1025 //		}
   1026 	}
   1027 }
   1028 
   1029 /*
   1030 ===============
   1031 KamikazeDamage
   1032 ===============
   1033 */
   1034 static void KamikazeDamage( gentity_t *self ) {
   1035 	int i;
   1036 	float t;
   1037 	gentity_t *ent;
   1038 	vec3_t newangles;
   1039 
   1040 	self->count += 100;
   1041 
   1042 	if (self->count >= KAMI_SHOCKWAVE_STARTTIME) {
   1043 		// shockwave push back
   1044 		t = self->count - KAMI_SHOCKWAVE_STARTTIME;
   1045 		KamikazeShockWave(self->s.pos.trBase, self->activator, 25, 400,	(int) (float) t * KAMI_SHOCKWAVE_MAXRADIUS / (KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME) );
   1046 	}
   1047 	//
   1048 	if (self->count >= KAMI_EXPLODE_STARTTIME) {
   1049 		// do our damage
   1050 		t = self->count - KAMI_EXPLODE_STARTTIME;
   1051 		KamikazeRadiusDamage( self->s.pos.trBase, self->activator, 400,	(int) (float) t * KAMI_BOOMSPHERE_MAXRADIUS / (KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME) );
   1052 	}
   1053 
   1054 	// either cycle or kill self
   1055 	if( self->count >= KAMI_SHOCKWAVE_ENDTIME ) {
   1056 		G_FreeEntity( self );
   1057 		return;
   1058 	}
   1059 	self->nextthink = level.time + 100;
   1060 
   1061 	// add earth quake effect
   1062 	newangles[0] = crandom() * 2;
   1063 	newangles[1] = crandom() * 2;
   1064 	newangles[2] = 0;
   1065 	for (i = 0; i < MAX_CLIENTS; i++)
   1066 	{
   1067 		ent = &g_entities[i];
   1068 		if (!ent->inuse)
   1069 			continue;
   1070 		if (!ent->client)
   1071 			continue;
   1072 
   1073 		if (ent->client->ps.groundEntityNum != ENTITYNUM_NONE) {
   1074 			ent->client->ps.velocity[0] += crandom() * 120;
   1075 			ent->client->ps.velocity[1] += crandom() * 120;
   1076 			ent->client->ps.velocity[2] = 30 + random() * 25;
   1077 		}
   1078 
   1079 		ent->client->ps.delta_angles[0] += ANGLE2SHORT(newangles[0] - self->movedir[0]);
   1080 		ent->client->ps.delta_angles[1] += ANGLE2SHORT(newangles[1] - self->movedir[1]);
   1081 		ent->client->ps.delta_angles[2] += ANGLE2SHORT(newangles[2] - self->movedir[2]);
   1082 	}
   1083 	VectorCopy(newangles, self->movedir);
   1084 }
   1085 
   1086 /*
   1087 ===============
   1088 G_StartKamikaze
   1089 ===============
   1090 */
   1091 void G_StartKamikaze( gentity_t *ent ) {
   1092 	gentity_t	*explosion;
   1093 	gentity_t	*te;
   1094 	vec3_t		snapped;
   1095 
   1096 	// start up the explosion logic
   1097 	explosion = G_Spawn();
   1098 
   1099 	explosion->s.eType = ET_EVENTS + EV_KAMIKAZE;
   1100 	explosion->eventTime = level.time;
   1101 
   1102 	if ( ent->client ) {
   1103 		VectorCopy( ent->s.pos.trBase, snapped );
   1104 	}
   1105 	else {
   1106 		VectorCopy( ent->activator->s.pos.trBase, snapped );
   1107 	}
   1108 	SnapVector( snapped );		// save network bandwidth
   1109 	G_SetOrigin( explosion, snapped );
   1110 
   1111 	explosion->classname = "kamikaze";
   1112 	explosion->s.pos.trType = TR_STATIONARY;
   1113 
   1114 	explosion->kamikazeTime = level.time;
   1115 
   1116 	explosion->think = KamikazeDamage;
   1117 	explosion->nextthink = level.time + 100;
   1118 	explosion->count = 0;
   1119 	VectorClear(explosion->movedir);
   1120 
   1121 	trap_LinkEntity( explosion );
   1122 
   1123 	if (ent->client) {
   1124 		//
   1125 		explosion->activator = ent;
   1126 		//
   1127 		ent->s.eFlags &= ~EF_KAMIKAZE;
   1128 		// nuke the guy that used it
   1129 		G_Damage( ent, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_KAMIKAZE );
   1130 	}
   1131 	else {
   1132 		if ( !strcmp(ent->activator->classname, "bodyque") ) {
   1133 			explosion->activator = &g_entities[ent->activator->r.ownerNum];
   1134 		}
   1135 		else {
   1136 			explosion->activator = ent->activator;
   1137 		}
   1138 	}
   1139 
   1140 	// play global sound at all clients
   1141 	te = G_TempEntity(snapped, EV_GLOBAL_TEAM_SOUND );
   1142 	te->r.svFlags |= SVF_BROADCAST;
   1143 	te->s.eventParm = GTS_KAMIKAZE;
   1144 }
   1145 #endif