aboutsummaryrefslogtreecommitdiffstats
path: root/code/game/g_weapon.c
diff options
context:
space:
mode:
authorzakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-08-26 17:39:27 +0000
committerzakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-08-26 17:39:27 +0000
commit6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch)
treee3eda937a05d7db42de725b7013bd0344b987f34 /code/game/g_weapon.c
parent872d4d7f55af706737ffb361bb76ad13e7496770 (diff)
downloadioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz
ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/game/g_weapon.c')
-rwxr-xr-xcode/game/g_weapon.c2290
1 files changed, 1145 insertions, 1145 deletions
diff --git a/code/game/g_weapon.c b/code/game/g_weapon.c
index 5a3fe5e..579a16c 100755
--- a/code/game/g_weapon.c
+++ b/code/game/g_weapon.c
@@ -1,1145 +1,1145 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-//
-// g_weapon.c
-// perform the server side effects of a weapon firing
-
-#include "g_local.h"
-
-static float s_quadFactor;
-static vec3_t forward, right, up;
-static vec3_t muzzle;
-
-#define NUM_NAILSHOTS 15
-
-/*
-================
-G_BounceProjectile
-================
-*/
-void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
- vec3_t v, newv;
- float dot;
-
- VectorSubtract( impact, start, v );
- dot = DotProduct( v, dir );
- VectorMA( v, -2*dot, dir, newv );
-
- VectorNormalize(newv);
- VectorMA(impact, 8192, newv, endout);
-}
-
-
-/*
-======================================================================
-
-GAUNTLET
-
-======================================================================
-*/
-
-void Weapon_Gauntlet( gentity_t *ent ) {
-
-}
-
-/*
-===============
-CheckGauntletAttack
-===============
-*/
-qboolean CheckGauntletAttack( gentity_t *ent ) {
- trace_t tr;
- vec3_t end;
- gentity_t *tent;
- gentity_t *traceEnt;
- int damage;
-
- // set aiming directions
- AngleVectors (ent->client->ps.viewangles, forward, right, up);
-
- CalcMuzzlePoint ( ent, forward, right, up, muzzle );
-
- VectorMA (muzzle, 32, forward, end);
-
- trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
- if ( tr.surfaceFlags & SURF_NOIMPACT ) {
- return qfalse;
- }
-
- traceEnt = &g_entities[ tr.entityNum ];
-
- // send blood impact
- if ( traceEnt->takedamage && traceEnt->client ) {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- }
-
- if ( !traceEnt->takedamage) {
- return qfalse;
- }
-
- if (ent->client->ps.powerups[PW_QUAD] ) {
- G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
- s_quadFactor = g_quadfactor.value;
- } else {
- s_quadFactor = 1;
- }
-#ifdef MISSIONPACK
- if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
- s_quadFactor *= 2;
- }
-#endif
-
- damage = 50 * s_quadFactor;
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- damage, 0, MOD_GAUNTLET );
-
- return qtrue;
-}
-
-
-/*
-======================================================================
-
-MACHINEGUN
-
-======================================================================
-*/
-
-/*
-======================
-SnapVectorTowards
-
-Round a vector to integers for more efficient network
-transmission, but make sure that it rounds towards a given point
-rather than blindly truncating. This prevents it from truncating
-into a wall.
-======================
-*/
-void SnapVectorTowards( vec3_t v, vec3_t to ) {
- int i;
-
- for ( i = 0 ; i < 3 ; i++ ) {
- if ( to[i] <= v[i] ) {
- v[i] = (int)v[i];
- } else {
- v[i] = (int)v[i] + 1;
- }
- }
-}
-
-#ifdef MISSIONPACK
-#define CHAINGUN_SPREAD 600
-#endif
-#define MACHINEGUN_SPREAD 200
-#define MACHINEGUN_DAMAGE 7
-#define MACHINEGUN_TEAM_DAMAGE 5 // wimpier MG in teamplay
-
-void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
- trace_t tr;
- vec3_t end;
-#ifdef MISSIONPACK
- vec3_t impactpoint, bouncedir;
-#endif
- float r;
- float u;
- gentity_t *tent;
- gentity_t *traceEnt;
- int i, passent;
-
- damage *= s_quadFactor;
-
- r = random() * M_PI * 2.0f;
- u = sin(r) * crandom() * spread * 16;
- r = cos(r) * crandom() * spread * 16;
- VectorMA (muzzle, 8192*16, forward, end);
- VectorMA (end, r, right, end);
- VectorMA (end, u, up, end);
-
- passent = ent->s.number;
- for (i = 0; i < 10; i++) {
-
- trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
- if ( tr.surfaceFlags & SURF_NOIMPACT ) {
- return;
- }
-
- traceEnt = &g_entities[ tr.entityNum ];
-
- // snap the endpos to integers, but nudged towards the line
- SnapVectorTowards( tr.endpos, muzzle );
-
- // send bullet impact
- if ( traceEnt->takedamage && traceEnt->client ) {
- tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
- tent->s.eventParm = traceEnt->s.number;
- if( LogAccuracyHit( traceEnt, ent ) ) {
- ent->client->accuracy_hits++;
- }
- } else {
- tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
- tent->s.eventParm = DirToByte( tr.plane.normal );
- }
- tent->s.otherEntityNum = ent->s.number;
-
- if ( traceEnt->takedamage) {
-#ifdef MISSIONPACK
- if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
- if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
- G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
- VectorCopy( impactpoint, muzzle );
- // the player can hit him/herself with the bounced rail
- passent = ENTITYNUM_NONE;
- }
- else {
- VectorCopy( tr.endpos, muzzle );
- passent = traceEnt->s.number;
- }
- continue;
- }
- else {
-#endif
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- damage, 0, MOD_MACHINEGUN);
-#ifdef MISSIONPACK
- }
-#endif
- }
- break;
- }
-}
-
-
-/*
-======================================================================
-
-BFG
-
-======================================================================
-*/
-
-void BFG_Fire ( gentity_t *ent ) {
- gentity_t *m;
-
- m = fire_bfg (ent, muzzle, forward);
- m->damage *= s_quadFactor;
- m->splashDamage *= s_quadFactor;
-
-// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
-}
-
-
-/*
-======================================================================
-
-SHOTGUN
-
-======================================================================
-*/
-
-// DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because
-// client predicts same spreads
-#define DEFAULT_SHOTGUN_DAMAGE 10
-
-qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
- trace_t tr;
- int damage, i, passent;
- gentity_t *traceEnt;
-#ifdef MISSIONPACK
- vec3_t impactpoint, bouncedir;
-#endif
- vec3_t tr_start, tr_end;
-
- passent = ent->s.number;
- VectorCopy( start, tr_start );
- VectorCopy( end, tr_end );
- for (i = 0; i < 10; i++) {
- trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT);
- traceEnt = &g_entities[ tr.entityNum ];
-
- // send bullet impact
- if ( tr.surfaceFlags & SURF_NOIMPACT ) {
- return qfalse;
- }
-
- if ( traceEnt->takedamage) {
- damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
-#ifdef MISSIONPACK
- if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
- if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
- G_BounceProjectile( tr_start, impactpoint, bouncedir, tr_end );
- VectorCopy( impactpoint, tr_start );
- // the player can hit him/herself with the bounced rail
- passent = ENTITYNUM_NONE;
- }
- else {
- VectorCopy( tr.endpos, tr_start );
- passent = traceEnt->s.number;
- }
- continue;
- }
- else {
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- damage, 0, MOD_SHOTGUN);
- if( LogAccuracyHit( traceEnt, ent ) ) {
- return qtrue;
- }
- }
-#else
- G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN);
- if( LogAccuracyHit( traceEnt, ent ) ) {
- return qtrue;
- }
-#endif
- }
- return qfalse;
- }
- return qfalse;
-}
-
-// this should match CG_ShotgunPattern
-void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
- int i;
- float r, u;
- vec3_t end;
- vec3_t forward, right, up;
- int oldScore;
- qboolean hitClient = qfalse;
-
- // derive the right and up vectors from the forward vector, because
- // the client won't have any other information
- VectorNormalize2( origin2, forward );
- PerpendicularVector( right, forward );
- CrossProduct( forward, right, up );
-
- oldScore = ent->client->ps.persistant[PERS_SCORE];
-
- // generate the "random" spread pattern
- for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
- r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
- u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
- VectorMA( origin, 8192 * 16, forward, end);
- VectorMA (end, r, right, end);
- VectorMA (end, u, up, end);
- if( ShotgunPellet( origin, end, ent ) && !hitClient ) {
- hitClient = qtrue;
- ent->client->accuracy_hits++;
- }
- }
-}
-
-
-void weapon_supershotgun_fire (gentity_t *ent) {
- gentity_t *tent;
-
- // send shotgun blast
- tent = G_TempEntity( muzzle, EV_SHOTGUN );
- VectorScale( forward, 4096, tent->s.origin2 );
- SnapVector( tent->s.origin2 );
- tent->s.eventParm = rand() & 255; // seed for spread pattern
- tent->s.otherEntityNum = ent->s.number;
-
- ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
-}
-
-
-/*
-======================================================================
-
-GRENADE LAUNCHER
-
-======================================================================
-*/
-
-void weapon_grenadelauncher_fire (gentity_t *ent) {
- gentity_t *m;
-
- // extra vertical velocity
- forward[2] += 0.2f;
- VectorNormalize( forward );
-
- m = fire_grenade (ent, muzzle, forward);
- m->damage *= s_quadFactor;
- m->splashDamage *= s_quadFactor;
-
-// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
-}
-
-/*
-======================================================================
-
-ROCKET
-
-======================================================================
-*/
-
-void Weapon_RocketLauncher_Fire (gentity_t *ent) {
- gentity_t *m;
-
- m = fire_rocket (ent, muzzle, forward);
- m->damage *= s_quadFactor;
- m->splashDamage *= s_quadFactor;
-
-// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
-}
-
-
-/*
-======================================================================
-
-PLASMA GUN
-
-======================================================================
-*/
-
-void Weapon_Plasmagun_Fire (gentity_t *ent) {
- gentity_t *m;
-
- m = fire_plasma (ent, muzzle, forward);
- m->damage *= s_quadFactor;
- m->splashDamage *= s_quadFactor;
-
-// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
-}
-
-/*
-======================================================================
-
-RAILGUN
-
-======================================================================
-*/
-
-
-/*
-=================
-weapon_railgun_fire
-=================
-*/
-#define MAX_RAIL_HITS 4
-void weapon_railgun_fire (gentity_t *ent) {
- vec3_t end;
-#ifdef MISSIONPACK
- vec3_t impactpoint, bouncedir;
-#endif
- trace_t trace;
- gentity_t *tent;
- gentity_t *traceEnt;
- int damage;
- int i;
- int hits;
- int unlinked;
- int passent;
- gentity_t *unlinkedEntities[MAX_RAIL_HITS];
-
- damage = 100 * s_quadFactor;
-
- VectorMA (muzzle, 8192, forward, end);
-
- // trace only against the solids, so the railgun will go through people
- unlinked = 0;
- hits = 0;
- passent = ent->s.number;
- do {
- trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
- if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
- break;
- }
- traceEnt = &g_entities[ trace.entityNum ];
- if ( traceEnt->takedamage ) {
-#ifdef MISSIONPACK
- if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
- if ( G_InvulnerabilityEffect( traceEnt, forward, trace.endpos, impactpoint, bouncedir ) ) {
- G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
- // 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 );
- tent->s.eventParm = 255; // don't make the explosion at the end
- //
- VectorCopy( impactpoint, muzzle );
- // the player can hit him/herself with the bounced rail
- passent = ENTITYNUM_NONE;
- }
- }
- else {
- if( LogAccuracyHit( traceEnt, ent ) ) {
- hits++;
- }
- G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
- }
-#else
- if( LogAccuracyHit( traceEnt, ent ) ) {
- hits++;
- }
- G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
-#endif
- }
- 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_IMPRESSIVE_COUNT]++;
- // add the sprite over the player's head
- ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
- ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
- ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
- }
- ent->client->accuracy_hits++;
- }
-
-}
-
-
-/*
-======================================================================
-
-GRAPPLING HOOK
-
-======================================================================
-*/
-
-void Weapon_GrapplingHook_Fire (gentity_t *ent)
-{
- if (!ent->client->fireHeld && !ent->client->hook)
- fire_grapple (ent, muzzle, forward);
-
- ent->client->fireHeld = qtrue;
-}
-
-void Weapon_HookFree (gentity_t *ent)
-{
- ent->parent->client->hook = NULL;
- ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
- G_FreeEntity( ent );
-}
-
-void Weapon_HookThink (gentity_t *ent)
-{
- if (ent->enemy) {
- vec3_t v, oldorigin;
-
- VectorCopy(ent->r.currentOrigin, oldorigin);
- v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5;
- v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5;
- v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5;
- SnapVectorTowards( v, oldorigin ); // save net bandwidth
-
- G_SetOrigin( ent, v );
- }
-
- VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
-}
-
-/*
-======================================================================
-
-LIGHTNING GUN
-
-======================================================================
-*/
-
-void Weapon_LightningFire( gentity_t *ent ) {
- trace_t tr;
- vec3_t end;
-#ifdef MISSIONPACK
- vec3_t impactpoint, bouncedir;
-#endif
- gentity_t *traceEnt, *tent;
- int damage, i, passent;
-
- damage = 8 * s_quadFactor;
-
- passent = ent->s.number;
- for (i = 0; i < 10; i++) {
- VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
-
- trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
-
-#ifdef MISSIONPACK
- // if not the first trace (the lightning bounced of an invulnerability sphere)
- if (i) {
- // add bounced off lightning bolt temp entity
- // the first lightning bolt is a cgame only visual
- //
- tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT );
- VectorCopy( tr.endpos, end );
- SnapVector( end );
- VectorCopy( end, tent->s.origin2 );
- }
-#endif
- if ( tr.entityNum == ENTITYNUM_NONE ) {
- return;
- }
-
- traceEnt = &g_entities[ tr.entityNum ];
-
- if ( traceEnt->takedamage) {
-#ifdef MISSIONPACK
- if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
- if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
- G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
- VectorCopy( impactpoint, muzzle );
- VectorSubtract( end, impactpoint, forward );
- VectorNormalize(forward);
- // the player can hit him/herself with the bounced lightning
- passent = ENTITYNUM_NONE;
- }
- else {
- VectorCopy( tr.endpos, muzzle );
- passent = traceEnt->s.number;
- }
- continue;
- }
- else {
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- damage, 0, MOD_LIGHTNING);
- }
-#else
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- damage, 0, MOD_LIGHTNING);
-#endif
- }
-
- if ( traceEnt->takedamage && traceEnt->client ) {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- if( LogAccuracyHit( traceEnt, ent ) ) {
- ent->client->accuracy_hits++;
- }
- } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
- tent->s.eventParm = DirToByte( tr.plane.normal );
- }
-
- break;
- }
-}
-
-#ifdef MISSIONPACK
-/*
-======================================================================
-
-NAILGUN
-
-======================================================================
-*/
-
-void Weapon_Nailgun_Fire (gentity_t *ent) {
- gentity_t *m;
- int count;
-
- for( count = 0; count < NUM_NAILSHOTS; count++ ) {
- m = fire_nail (ent, muzzle, forward, right, up );
- m->damage *= s_quadFactor;
- m->splashDamage *= s_quadFactor;
- }
-
-// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
-}
-
-
-/*
-======================================================================
-
-PROXIMITY MINE LAUNCHER
-
-======================================================================
-*/
-
-void weapon_proxlauncher_fire (gentity_t *ent) {
- gentity_t *m;
-
- // extra vertical velocity
- forward[2] += 0.2f;
- VectorNormalize( forward );
-
- m = fire_prox (ent, muzzle, forward);
- m->damage *= s_quadFactor;
- m->splashDamage *= s_quadFactor;
-
-// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
-}
-
-#endif
-
-//======================================================================
-
-
-/*
-===============
-LogAccuracyHit
-===============
-*/
-qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
- if( !target->takedamage ) {
- return qfalse;
- }
-
- if ( target == attacker ) {
- return qfalse;
- }
-
- if( !target->client ) {
- return qfalse;
- }
-
- if( !attacker->client ) {
- return qfalse;
- }
-
- if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
- return qfalse;
- }
-
- if ( OnSameTeam( target, attacker ) ) {
- return qfalse;
- }
-
- return qtrue;
-}
-
-
-/*
-===============
-CalcMuzzlePoint
-
-set muzzle location relative to pivoting eye
-===============
-*/
-void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
- VectorCopy( ent->s.pos.trBase, muzzlePoint );
- muzzlePoint[2] += ent->client->ps.viewheight;
- VectorMA( muzzlePoint, 14, forward, muzzlePoint );
- // snap to integer coordinates for more efficient network bandwidth usage
- SnapVector( muzzlePoint );
-}
-
-/*
-===============
-CalcMuzzlePointOrigin
-
-set muzzle location relative to pivoting eye
-===============
-*/
-void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
- VectorCopy( ent->s.pos.trBase, muzzlePoint );
- muzzlePoint[2] += ent->client->ps.viewheight;
- VectorMA( muzzlePoint, 14, forward, muzzlePoint );
- // snap to integer coordinates for more efficient network bandwidth usage
- SnapVector( muzzlePoint );
-}
-
-
-
-/*
-===============
-FireWeapon
-===============
-*/
-void FireWeapon( gentity_t *ent ) {
- if (ent->client->ps.powerups[PW_QUAD] ) {
- s_quadFactor = g_quadfactor.value;
- } else {
- s_quadFactor = 1;
- }
-#ifdef MISSIONPACK
- if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
- s_quadFactor *= 2;
- }
-#endif
-
- // 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 ) {
-#ifdef MISSIONPACK
- if( ent->s.weapon == WP_NAILGUN ) {
- ent->client->accuracy_shots += NUM_NAILSHOTS;
- } else {
- ent->client->accuracy_shots++;
- }
-#else
- ent->client->accuracy_shots++;
-#endif
- }
-
- // set aiming directions
- AngleVectors (ent->client->ps.viewangles, forward, right, up);
-
- CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, 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 );
- break;
- case WP_BFG:
- BFG_Fire( ent );
- break;
- case WP_GRAPPLING_HOOK:
- Weapon_GrapplingHook_Fire( ent );
- break;
-#ifdef MISSIONPACK
- case WP_NAILGUN:
- Weapon_Nailgun_Fire( ent );
- break;
- case WP_PROX_LAUNCHER:
- weapon_proxlauncher_fire( ent );
- break;
- case WP_CHAINGUN:
- Bullet_Fire( ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE );
- break;
-#endif
- default:
-// FIXME G_Error( "Bad ent->s.weapon" );
- break;
- }
-}
-
-
-#ifdef MISSIONPACK
-
-/*
-===============
-KamikazeRadiusDamage
-===============
-*/
-static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius ) {
- float dist;
- gentity_t *ent;
- int entityList[MAX_GENTITIES];
- int numListedEntities;
- vec3_t mins, maxs;
- vec3_t v;
- vec3_t dir;
- int i, e;
-
- if ( radius < 1 ) {
- radius = 1;
- }
-
- for ( i = 0 ; i < 3 ; i++ ) {
- mins[i] = origin[i] - radius;
- maxs[i] = origin[i] + radius;
- }
-
- numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
-
- for ( e = 0 ; e < numListedEntities ; e++ ) {
- ent = &g_entities[entityList[ e ]];
-
- if (!ent->takedamage) {
- continue;
- }
-
- // dont hit things we have already hit
- if( ent->kamikazeTime > level.time ) {
- continue;
- }
-
- // find the distance from the edge of the bounding box
- for ( i = 0 ; i < 3 ; i++ ) {
- if ( origin[i] < ent->r.absmin[i] ) {
- v[i] = ent->r.absmin[i] - origin[i];
- } else if ( origin[i] > ent->r.absmax[i] ) {
- v[i] = origin[i] - ent->r.absmax[i];
- } else {
- v[i] = 0;
- }
- }
-
- dist = VectorLength( v );
- if ( dist >= radius ) {
- continue;
- }
-
-// if( CanDamage (ent, origin) ) {
- VectorSubtract (ent->r.currentOrigin, origin, dir);
- // push the center of mass higher than the origin so players
- // get knocked into the air more
- dir[2] += 24;
- G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
- ent->kamikazeTime = level.time + 3000;
-// }
- }
-}
-
-/*
-===============
-KamikazeShockWave
-===============
-*/
-static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, float push, float radius ) {
- float dist;
- gentity_t *ent;
- int entityList[MAX_GENTITIES];
- int numListedEntities;
- vec3_t mins, maxs;
- vec3_t v;
- vec3_t dir;
- int i, e;
-
- if ( radius < 1 )
- radius = 1;
-
- for ( i = 0 ; i < 3 ; i++ ) {
- mins[i] = origin[i] - radius;
- maxs[i] = origin[i] + radius;
- }
-
- numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
-
- for ( e = 0 ; e < numListedEntities ; e++ ) {
- ent = &g_entities[entityList[ e ]];
-
- // dont hit things we have already hit
- if( ent->kamikazeShockTime > level.time ) {
- continue;
- }
-
- // find the distance from the edge of the bounding box
- for ( i = 0 ; i < 3 ; i++ ) {
- if ( origin[i] < ent->r.absmin[i] ) {
- v[i] = ent->r.absmin[i] - origin[i];
- } else if ( origin[i] > ent->r.absmax[i] ) {
- v[i] = origin[i] - ent->r.absmax[i];
- } else {
- v[i] = 0;
- }
- }
-
- dist = VectorLength( v );
- if ( dist >= radius ) {
- continue;
- }
-
-// if( CanDamage (ent, origin) ) {
- VectorSubtract (ent->r.currentOrigin, origin, dir);
- dir[2] += 24;
- G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
- //
- dir[2] = 0;
- VectorNormalize(dir);
- if ( ent->client ) {
- ent->client->ps.velocity[0] = dir[0] * push;
- ent->client->ps.velocity[1] = dir[1] * push;
- ent->client->ps.velocity[2] = 100;
- }
- ent->kamikazeShockTime = level.time + 3000;
-// }
- }
-}
-
-/*
-===============
-KamikazeDamage
-===============
-*/
-static void KamikazeDamage( gentity_t *self ) {
- int i;
- float t;
- gentity_t *ent;
- vec3_t newangles;
-
- self->count += 100;
-
- if (self->count >= KAMI_SHOCKWAVE_STARTTIME) {
- // shockwave push back
- t = self->count - KAMI_SHOCKWAVE_STARTTIME;
- KamikazeShockWave(self->s.pos.trBase, self->activator, 25, 400, (int) (float) t * KAMI_SHOCKWAVE_MAXRADIUS / (KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME) );
- }
- //
- if (self->count >= KAMI_EXPLODE_STARTTIME) {
- // do our damage
- t = self->count - KAMI_EXPLODE_STARTTIME;
- KamikazeRadiusDamage( self->s.pos.trBase, self->activator, 400, (int) (float) t * KAMI_BOOMSPHERE_MAXRADIUS / (KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME) );
- }
-
- // either cycle or kill self
- if( self->count >= KAMI_SHOCKWAVE_ENDTIME ) {
- G_FreeEntity( self );
- return;
- }
- self->nextthink = level.time + 100;
-
- // add earth quake effect
- newangles[0] = crandom() * 2;
- newangles[1] = crandom() * 2;
- newangles[2] = 0;
- for (i = 0; i < MAX_CLIENTS; i++)
- {
- ent = &g_entities[i];
- if (!ent->inuse)
- continue;
- if (!ent->client)
- continue;
-
- if (ent->client->ps.groundEntityNum != ENTITYNUM_NONE) {
- ent->client->ps.velocity[0] += crandom() * 120;
- ent->client->ps.velocity[1] += crandom() * 120;
- ent->client->ps.velocity[2] = 30 + random() * 25;
- }
-
- ent->client->ps.delta_angles[0] += ANGLE2SHORT(newangles[0] - self->movedir[0]);
- ent->client->ps.delta_angles[1] += ANGLE2SHORT(newangles[1] - self->movedir[1]);
- ent->client->ps.delta_angles[2] += ANGLE2SHORT(newangles[2] - self->movedir[2]);
- }
- VectorCopy(newangles, self->movedir);
-}
-
-/*
-===============
-G_StartKamikaze
-===============
-*/
-void G_StartKamikaze( gentity_t *ent ) {
- gentity_t *explosion;
- gentity_t *te;
- vec3_t snapped;
-
- // start up the explosion logic
- explosion = G_Spawn();
-
- explosion->s.eType = ET_EVENTS + EV_KAMIKAZE;
- explosion->eventTime = level.time;
-
- if ( ent->client ) {
- VectorCopy( ent->s.pos.trBase, snapped );
- }
- else {
- VectorCopy( ent->activator->s.pos.trBase, snapped );
- }
- SnapVector( snapped ); // save network bandwidth
- G_SetOrigin( explosion, snapped );
-
- explosion->classname = "kamikaze";
- explosion->s.pos.trType = TR_STATIONARY;
-
- explosion->kamikazeTime = level.time;
-
- explosion->think = KamikazeDamage;
- explosion->nextthink = level.time + 100;
- explosion->count = 0;
- VectorClear(explosion->movedir);
-
- trap_LinkEntity( explosion );
-
- if (ent->client) {
- //
- explosion->activator = ent;
- //
- ent->s.eFlags &= ~EF_KAMIKAZE;
- // nuke the guy that used it
- G_Damage( ent, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_KAMIKAZE );
- }
- else {
- if ( !strcmp(ent->activator->classname, "bodyque") ) {
- explosion->activator = &g_entities[ent->activator->r.ownerNum];
- }
- else {
- explosion->activator = ent->activator;
- }
- }
-
- // play global sound at all clients
- te = G_TempEntity(snapped, EV_GLOBAL_TEAM_SOUND );
- te->r.svFlags |= SVF_BROADCAST;
- te->s.eventParm = GTS_KAMIKAZE;
-}
-#endif
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// g_weapon.c
+// perform the server side effects of a weapon firing
+
+#include "g_local.h"
+
+static float s_quadFactor;
+static vec3_t forward, right, up;
+static vec3_t muzzle;
+
+#define NUM_NAILSHOTS 15
+
+/*
+================
+G_BounceProjectile
+================
+*/
+void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
+ vec3_t v, newv;
+ float dot;
+
+ VectorSubtract( impact, start, v );
+ dot = DotProduct( v, dir );
+ VectorMA( v, -2*dot, dir, newv );
+
+ VectorNormalize(newv);
+ VectorMA(impact, 8192, newv, endout);
+}
+
+
+/*
+======================================================================
+
+GAUNTLET
+
+======================================================================
+*/
+
+void Weapon_Gauntlet( gentity_t *ent ) {
+
+}
+
+/*
+===============
+CheckGauntletAttack
+===============
+*/
+qboolean CheckGauntletAttack( gentity_t *ent ) {
+ trace_t tr;
+ vec3_t end;
+ gentity_t *tent;
+ gentity_t *traceEnt;
+ int damage;
+
+ // set aiming directions
+ AngleVectors (ent->client->ps.viewangles, forward, right, up);
+
+ CalcMuzzlePoint ( ent, forward, right, up, muzzle );
+
+ VectorMA (muzzle, 32, forward, end);
+
+ trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
+ if ( tr.surfaceFlags & SURF_NOIMPACT ) {
+ return qfalse;
+ }
+
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ // send blood impact
+ if ( traceEnt->takedamage && traceEnt->client ) {
+ tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
+ tent->s.otherEntityNum = traceEnt->s.number;
+ tent->s.eventParm = DirToByte( tr.plane.normal );
+ tent->s.weapon = ent->s.weapon;
+ }
+
+ if ( !traceEnt->takedamage) {
+ return qfalse;
+ }
+
+ if (ent->client->ps.powerups[PW_QUAD] ) {
+ G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
+ s_quadFactor = g_quadfactor.value;
+ } else {
+ s_quadFactor = 1;
+ }
+#ifdef MISSIONPACK
+ if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
+ s_quadFactor *= 2;
+ }
+#endif
+
+ damage = 50 * s_quadFactor;
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_GAUNTLET );
+
+ return qtrue;
+}
+
+
+/*
+======================================================================
+
+MACHINEGUN
+
+======================================================================
+*/
+
+/*
+======================
+SnapVectorTowards
+
+Round a vector to integers for more efficient network
+transmission, but make sure that it rounds towards a given point
+rather than blindly truncating. This prevents it from truncating
+into a wall.
+======================
+*/
+void SnapVectorTowards( vec3_t v, vec3_t to ) {
+ int i;
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ if ( to[i] <= v[i] ) {
+ v[i] = (int)v[i];
+ } else {
+ v[i] = (int)v[i] + 1;
+ }
+ }
+}
+
+#ifdef MISSIONPACK
+#define CHAINGUN_SPREAD 600
+#endif
+#define MACHINEGUN_SPREAD 200
+#define MACHINEGUN_DAMAGE 7
+#define MACHINEGUN_TEAM_DAMAGE 5 // wimpier MG in teamplay
+
+void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
+ trace_t tr;
+ vec3_t end;
+#ifdef MISSIONPACK
+ vec3_t impactpoint, bouncedir;
+#endif
+ float r;
+ float u;
+ gentity_t *tent;
+ gentity_t *traceEnt;
+ int i, passent;
+
+ damage *= s_quadFactor;
+
+ r = random() * M_PI * 2.0f;
+ u = sin(r) * crandom() * spread * 16;
+ r = cos(r) * crandom() * spread * 16;
+ VectorMA (muzzle, 8192*16, forward, end);
+ VectorMA (end, r, right, end);
+ VectorMA (end, u, up, end);
+
+ passent = ent->s.number;
+ for (i = 0; i < 10; i++) {
+
+ trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
+ if ( tr.surfaceFlags & SURF_NOIMPACT ) {
+ return;
+ }
+
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ // snap the endpos to integers, but nudged towards the line
+ SnapVectorTowards( tr.endpos, muzzle );
+
+ // send bullet impact
+ if ( traceEnt->takedamage && traceEnt->client ) {
+ tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
+ tent->s.eventParm = traceEnt->s.number;
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ ent->client->accuracy_hits++;
+ }
+ } else {
+ tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
+ tent->s.eventParm = DirToByte( tr.plane.normal );
+ }
+ tent->s.otherEntityNum = ent->s.number;
+
+ if ( traceEnt->takedamage) {
+#ifdef MISSIONPACK
+ if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
+ if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
+ G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
+ VectorCopy( impactpoint, muzzle );
+ // the player can hit him/herself with the bounced rail
+ passent = ENTITYNUM_NONE;
+ }
+ else {
+ VectorCopy( tr.endpos, muzzle );
+ passent = traceEnt->s.number;
+ }
+ continue;
+ }
+ else {
+#endif
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_MACHINEGUN);
+#ifdef MISSIONPACK
+ }
+#endif
+ }
+ break;
+ }
+}
+
+
+/*
+======================================================================
+
+BFG
+
+======================================================================
+*/
+
+void BFG_Fire ( gentity_t *ent ) {
+ gentity_t *m;
+
+ m = fire_bfg (ent, muzzle, forward);
+ m->damage *= s_quadFactor;
+ m->splashDamage *= s_quadFactor;
+
+// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
+}
+
+
+/*
+======================================================================
+
+SHOTGUN
+
+======================================================================
+*/
+
+// DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because
+// client predicts same spreads
+#define DEFAULT_SHOTGUN_DAMAGE 10
+
+qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
+ trace_t tr;
+ int damage, i, passent;
+ gentity_t *traceEnt;
+#ifdef MISSIONPACK
+ vec3_t impactpoint, bouncedir;
+#endif
+ vec3_t tr_start, tr_end;
+
+ passent = ent->s.number;
+ VectorCopy( start, tr_start );
+ VectorCopy( end, tr_end );
+ for (i = 0; i < 10; i++) {
+ trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT);
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ // send bullet impact
+ if ( tr.surfaceFlags & SURF_NOIMPACT ) {
+ return qfalse;
+ }
+
+ if ( traceEnt->takedamage) {
+ damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
+#ifdef MISSIONPACK
+ if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
+ if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
+ G_BounceProjectile( tr_start, impactpoint, bouncedir, tr_end );
+ VectorCopy( impactpoint, tr_start );
+ // the player can hit him/herself with the bounced rail
+ passent = ENTITYNUM_NONE;
+ }
+ else {
+ VectorCopy( tr.endpos, tr_start );
+ passent = traceEnt->s.number;
+ }
+ continue;
+ }
+ else {
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_SHOTGUN);
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ return qtrue;
+ }
+ }
+#else
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN);
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ return qtrue;
+ }
+#endif
+ }
+ return qfalse;
+ }
+ return qfalse;
+}
+
+// this should match CG_ShotgunPattern
+void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
+ int i;
+ float r, u;
+ vec3_t end;
+ vec3_t forward, right, up;
+ int oldScore;
+ qboolean hitClient = qfalse;
+
+ // derive the right and up vectors from the forward vector, because
+ // the client won't have any other information
+ VectorNormalize2( origin2, forward );
+ PerpendicularVector( right, forward );
+ CrossProduct( forward, right, up );
+
+ oldScore = ent->client->ps.persistant[PERS_SCORE];
+
+ // generate the "random" spread pattern
+ for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
+ r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
+ u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
+ VectorMA( origin, 8192 * 16, forward, end);
+ VectorMA (end, r, right, end);
+ VectorMA (end, u, up, end);
+ if( ShotgunPellet( origin, end, ent ) && !hitClient ) {
+ hitClient = qtrue;
+ ent->client->accuracy_hits++;
+ }
+ }
+}
+
+
+void weapon_supershotgun_fire (gentity_t *ent) {
+ gentity_t *tent;
+
+ // send shotgun blast
+ tent = G_TempEntity( muzzle, EV_SHOTGUN );
+ VectorScale( forward, 4096, tent->s.origin2 );
+ SnapVector( tent->s.origin2 );
+ tent->s.eventParm = rand() & 255; // seed for spread pattern
+ tent->s.otherEntityNum = ent->s.number;
+
+ ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
+}
+
+
+/*
+======================================================================
+
+GRENADE LAUNCHER
+
+======================================================================
+*/
+
+void weapon_grenadelauncher_fire (gentity_t *ent) {
+ gentity_t *m;
+
+ // extra vertical velocity
+ forward[2] += 0.2f;
+ VectorNormalize( forward );
+
+ m = fire_grenade (ent, muzzle, forward);
+ m->damage *= s_quadFactor;
+ m->splashDamage *= s_quadFactor;
+
+// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
+}
+
+/*
+======================================================================
+
+ROCKET
+
+======================================================================
+*/
+
+void Weapon_RocketLauncher_Fire (gentity_t *ent) {
+ gentity_t *m;
+
+ m = fire_rocket (ent, muzzle, forward);
+ m->damage *= s_quadFactor;
+ m->splashDamage *= s_quadFactor;
+
+// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
+}
+
+
+/*
+======================================================================
+
+PLASMA GUN
+
+======================================================================
+*/
+
+void Weapon_Plasmagun_Fire (gentity_t *ent) {
+ gentity_t *m;
+
+ m = fire_plasma (ent, muzzle, forward);
+ m->damage *= s_quadFactor;
+ m->splashDamage *= s_quadFactor;
+
+// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
+}
+
+/*
+======================================================================
+
+RAILGUN
+
+======================================================================
+*/
+
+
+/*
+=================
+weapon_railgun_fire
+=================
+*/
+#define MAX_RAIL_HITS 4
+void weapon_railgun_fire (gentity_t *ent) {
+ vec3_t end;
+#ifdef MISSIONPACK
+ vec3_t impactpoint, bouncedir;
+#endif
+ trace_t trace;
+ gentity_t *tent;
+ gentity_t *traceEnt;
+ int damage;
+ int i;
+ int hits;
+ int unlinked;
+ int passent;
+ gentity_t *unlinkedEntities[MAX_RAIL_HITS];
+
+ damage = 100 * s_quadFactor;
+
+ VectorMA (muzzle, 8192, forward, end);
+
+ // trace only against the solids, so the railgun will go through people
+ unlinked = 0;
+ hits = 0;
+ passent = ent->s.number;
+ do {
+ trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
+ if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
+ break;
+ }
+ traceEnt = &g_entities[ trace.entityNum ];
+ if ( traceEnt->takedamage ) {
+#ifdef MISSIONPACK
+ if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
+ if ( G_InvulnerabilityEffect( traceEnt, forward, trace.endpos, impactpoint, bouncedir ) ) {
+ G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
+ // 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 );
+ tent->s.eventParm = 255; // don't make the explosion at the end
+ //
+ VectorCopy( impactpoint, muzzle );
+ // the player can hit him/herself with the bounced rail
+ passent = ENTITYNUM_NONE;
+ }
+ }
+ else {
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ hits++;
+ }
+ G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
+ }
+#else
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ hits++;
+ }
+ G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
+#endif
+ }
+ 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_IMPRESSIVE_COUNT]++;
+ // add the sprite over the player's head
+ ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
+ ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
+ ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
+ }
+ ent->client->accuracy_hits++;
+ }
+
+}
+
+
+/*
+======================================================================
+
+GRAPPLING HOOK
+
+======================================================================
+*/
+
+void Weapon_GrapplingHook_Fire (gentity_t *ent)
+{
+ if (!ent->client->fireHeld && !ent->client->hook)
+ fire_grapple (ent, muzzle, forward);
+
+ ent->client->fireHeld = qtrue;
+}
+
+void Weapon_HookFree (gentity_t *ent)
+{
+ ent->parent->client->hook = NULL;
+ ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
+ G_FreeEntity( ent );
+}
+
+void Weapon_HookThink (gentity_t *ent)
+{
+ if (ent->enemy) {
+ vec3_t v, oldorigin;
+
+ VectorCopy(ent->r.currentOrigin, oldorigin);
+ v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5;
+ v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5;
+ v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5;
+ SnapVectorTowards( v, oldorigin ); // save net bandwidth
+
+ G_SetOrigin( ent, v );
+ }
+
+ VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
+}
+
+/*
+======================================================================
+
+LIGHTNING GUN
+
+======================================================================
+*/
+
+void Weapon_LightningFire( gentity_t *ent ) {
+ trace_t tr;
+ vec3_t end;
+#ifdef MISSIONPACK
+ vec3_t impactpoint, bouncedir;
+#endif
+ gentity_t *traceEnt, *tent;
+ int damage, i, passent;
+
+ damage = 8 * s_quadFactor;
+
+ passent = ent->s.number;
+ for (i = 0; i < 10; i++) {
+ VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
+
+ trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
+
+#ifdef MISSIONPACK
+ // if not the first trace (the lightning bounced of an invulnerability sphere)
+ if (i) {
+ // add bounced off lightning bolt temp entity
+ // the first lightning bolt is a cgame only visual
+ //
+ tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT );
+ VectorCopy( tr.endpos, end );
+ SnapVector( end );
+ VectorCopy( end, tent->s.origin2 );
+ }
+#endif
+ if ( tr.entityNum == ENTITYNUM_NONE ) {
+ return;
+ }
+
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ if ( traceEnt->takedamage) {
+#ifdef MISSIONPACK
+ if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
+ if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
+ G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
+ VectorCopy( impactpoint, muzzle );
+ VectorSubtract( end, impactpoint, forward );
+ VectorNormalize(forward);
+ // the player can hit him/herself with the bounced lightning
+ passent = ENTITYNUM_NONE;
+ }
+ else {
+ VectorCopy( tr.endpos, muzzle );
+ passent = traceEnt->s.number;
+ }
+ continue;
+ }
+ else {
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_LIGHTNING);
+ }
+#else
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos,
+ damage, 0, MOD_LIGHTNING);
+#endif
+ }
+
+ if ( traceEnt->takedamage && traceEnt->client ) {
+ tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
+ tent->s.otherEntityNum = traceEnt->s.number;
+ tent->s.eventParm = DirToByte( tr.plane.normal );
+ tent->s.weapon = ent->s.weapon;
+ if( LogAccuracyHit( traceEnt, ent ) ) {
+ ent->client->accuracy_hits++;
+ }
+ } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
+ tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
+ tent->s.eventParm = DirToByte( tr.plane.normal );
+ }
+
+ break;
+ }
+}
+
+#ifdef MISSIONPACK
+/*
+======================================================================
+
+NAILGUN
+
+======================================================================
+*/
+
+void Weapon_Nailgun_Fire (gentity_t *ent) {
+ gentity_t *m;
+ int count;
+
+ for( count = 0; count < NUM_NAILSHOTS; count++ ) {
+ m = fire_nail (ent, muzzle, forward, right, up );
+ m->damage *= s_quadFactor;
+ m->splashDamage *= s_quadFactor;
+ }
+
+// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
+}
+
+
+/*
+======================================================================
+
+PROXIMITY MINE LAUNCHER
+
+======================================================================
+*/
+
+void weapon_proxlauncher_fire (gentity_t *ent) {
+ gentity_t *m;
+
+ // extra vertical velocity
+ forward[2] += 0.2f;
+ VectorNormalize( forward );
+
+ m = fire_prox (ent, muzzle, forward);
+ m->damage *= s_quadFactor;
+ m->splashDamage *= s_quadFactor;
+
+// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics
+}
+
+#endif
+
+//======================================================================
+
+
+/*
+===============
+LogAccuracyHit
+===============
+*/
+qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
+ if( !target->takedamage ) {
+ return qfalse;
+ }
+
+ if ( target == attacker ) {
+ return qfalse;
+ }
+
+ if( !target->client ) {
+ return qfalse;
+ }
+
+ if( !attacker->client ) {
+ return qfalse;
+ }
+
+ if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
+ return qfalse;
+ }
+
+ if ( OnSameTeam( target, attacker ) ) {
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+
+/*
+===============
+CalcMuzzlePoint
+
+set muzzle location relative to pivoting eye
+===============
+*/
+void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
+ VectorCopy( ent->s.pos.trBase, muzzlePoint );
+ muzzlePoint[2] += ent->client->ps.viewheight;
+ VectorMA( muzzlePoint, 14, forward, muzzlePoint );
+ // snap to integer coordinates for more efficient network bandwidth usage
+ SnapVector( muzzlePoint );
+}
+
+/*
+===============
+CalcMuzzlePointOrigin
+
+set muzzle location relative to pivoting eye
+===============
+*/
+void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
+ VectorCopy( ent->s.pos.trBase, muzzlePoint );
+ muzzlePoint[2] += ent->client->ps.viewheight;
+ VectorMA( muzzlePoint, 14, forward, muzzlePoint );
+ // snap to integer coordinates for more efficient network bandwidth usage
+ SnapVector( muzzlePoint );
+}
+
+
+
+/*
+===============
+FireWeapon
+===============
+*/
+void FireWeapon( gentity_t *ent ) {
+ if (ent->client->ps.powerups[PW_QUAD] ) {
+ s_quadFactor = g_quadfactor.value;
+ } else {
+ s_quadFactor = 1;
+ }
+#ifdef MISSIONPACK
+ if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
+ s_quadFactor *= 2;
+ }
+#endif
+
+ // 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 ) {
+#ifdef MISSIONPACK
+ if( ent->s.weapon == WP_NAILGUN ) {
+ ent->client->accuracy_shots += NUM_NAILSHOTS;
+ } else {
+ ent->client->accuracy_shots++;
+ }
+#else
+ ent->client->accuracy_shots++;
+#endif
+ }
+
+ // set aiming directions
+ AngleVectors (ent->client->ps.viewangles, forward, right, up);
+
+ CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, 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 );
+ break;
+ case WP_BFG:
+ BFG_Fire( ent );
+ break;
+ case WP_GRAPPLING_HOOK:
+ Weapon_GrapplingHook_Fire( ent );
+ break;
+#ifdef MISSIONPACK
+ case WP_NAILGUN:
+ Weapon_Nailgun_Fire( ent );
+ break;
+ case WP_PROX_LAUNCHER:
+ weapon_proxlauncher_fire( ent );
+ break;
+ case WP_CHAINGUN:
+ Bullet_Fire( ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE );
+ break;
+#endif
+ default:
+// FIXME G_Error( "Bad ent->s.weapon" );
+ break;
+ }
+}
+
+
+#ifdef MISSIONPACK
+
+/*
+===============
+KamikazeRadiusDamage
+===============
+*/
+static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius ) {
+ float dist;
+ gentity_t *ent;
+ int entityList[MAX_GENTITIES];
+ int numListedEntities;
+ vec3_t mins, maxs;
+ vec3_t v;
+ vec3_t dir;
+ int i, e;
+
+ if ( radius < 1 ) {
+ radius = 1;
+ }
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ mins[i] = origin[i] - radius;
+ maxs[i] = origin[i] + radius;
+ }
+
+ numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+
+ for ( e = 0 ; e < numListedEntities ; e++ ) {
+ ent = &g_entities[entityList[ e ]];
+
+ if (!ent->takedamage) {
+ continue;
+ }
+
+ // dont hit things we have already hit
+ if( ent->kamikazeTime > level.time ) {
+ continue;
+ }
+
+ // find the distance from the edge of the bounding box
+ for ( i = 0 ; i < 3 ; i++ ) {
+ if ( origin[i] < ent->r.absmin[i] ) {
+ v[i] = ent->r.absmin[i] - origin[i];
+ } else if ( origin[i] > ent->r.absmax[i] ) {
+ v[i] = origin[i] - ent->r.absmax[i];
+ } else {
+ v[i] = 0;
+ }
+ }
+
+ dist = VectorLength( v );
+ if ( dist >= radius ) {
+ continue;
+ }
+
+// if( CanDamage (ent, origin) ) {
+ VectorSubtract (ent->r.currentOrigin, origin, dir);
+ // push the center of mass higher than the origin so players
+ // get knocked into the air more
+ dir[2] += 24;
+ G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
+ ent->kamikazeTime = level.time + 3000;
+// }
+ }
+}
+
+/*
+===============
+KamikazeShockWave
+===============
+*/
+static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, float push, float radius ) {
+ float dist;
+ gentity_t *ent;
+ int entityList[MAX_GENTITIES];
+ int numListedEntities;
+ vec3_t mins, maxs;
+ vec3_t v;
+ vec3_t dir;
+ int i, e;
+
+ if ( radius < 1 )
+ radius = 1;
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ mins[i] = origin[i] - radius;
+ maxs[i] = origin[i] + radius;
+ }
+
+ numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+
+ for ( e = 0 ; e < numListedEntities ; e++ ) {
+ ent = &g_entities[entityList[ e ]];
+
+ // dont hit things we have already hit
+ if( ent->kamikazeShockTime > level.time ) {
+ continue;
+ }
+
+ // find the distance from the edge of the bounding box
+ for ( i = 0 ; i < 3 ; i++ ) {
+ if ( origin[i] < ent->r.absmin[i] ) {
+ v[i] = ent->r.absmin[i] - origin[i];
+ } else if ( origin[i] > ent->r.absmax[i] ) {
+ v[i] = origin[i] - ent->r.absmax[i];
+ } else {
+ v[i] = 0;
+ }
+ }
+
+ dist = VectorLength( v );
+ if ( dist >= radius ) {
+ continue;
+ }
+
+// if( CanDamage (ent, origin) ) {
+ VectorSubtract (ent->r.currentOrigin, origin, dir);
+ dir[2] += 24;
+ G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
+ //
+ dir[2] = 0;
+ VectorNormalize(dir);
+ if ( ent->client ) {
+ ent->client->ps.velocity[0] = dir[0] * push;
+ ent->client->ps.velocity[1] = dir[1] * push;
+ ent->client->ps.velocity[2] = 100;
+ }
+ ent->kamikazeShockTime = level.time + 3000;
+// }
+ }
+}
+
+/*
+===============
+KamikazeDamage
+===============
+*/
+static void KamikazeDamage( gentity_t *self ) {
+ int i;
+ float t;
+ gentity_t *ent;
+ vec3_t newangles;
+
+ self->count += 100;
+
+ if (self->count >= KAMI_SHOCKWAVE_STARTTIME) {
+ // shockwave push back
+ t = self->count - KAMI_SHOCKWAVE_STARTTIME;
+ KamikazeShockWave(self->s.pos.trBase, self->activator, 25, 400, (int) (float) t * KAMI_SHOCKWAVE_MAXRADIUS / (KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME) );
+ }
+ //
+ if (self->count >= KAMI_EXPLODE_STARTTIME) {
+ // do our damage
+ t = self->count - KAMI_EXPLODE_STARTTIME;
+ KamikazeRadiusDamage( self->s.pos.trBase, self->activator, 400, (int) (float) t * KAMI_BOOMSPHERE_MAXRADIUS / (KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME) );
+ }
+
+ // either cycle or kill self
+ if( self->count >= KAMI_SHOCKWAVE_ENDTIME ) {
+ G_FreeEntity( self );
+ return;
+ }
+ self->nextthink = level.time + 100;
+
+ // add earth quake effect
+ newangles[0] = crandom() * 2;
+ newangles[1] = crandom() * 2;
+ newangles[2] = 0;
+ for (i = 0; i < MAX_CLIENTS; i++)
+ {
+ ent = &g_entities[i];
+ if (!ent->inuse)
+ continue;
+ if (!ent->client)
+ continue;
+
+ if (ent->client->ps.groundEntityNum != ENTITYNUM_NONE) {
+ ent->client->ps.velocity[0] += crandom() * 120;
+ ent->client->ps.velocity[1] += crandom() * 120;
+ ent->client->ps.velocity[2] = 30 + random() * 25;
+ }
+
+ ent->client->ps.delta_angles[0] += ANGLE2SHORT(newangles[0] - self->movedir[0]);
+ ent->client->ps.delta_angles[1] += ANGLE2SHORT(newangles[1] - self->movedir[1]);
+ ent->client->ps.delta_angles[2] += ANGLE2SHORT(newangles[2] - self->movedir[2]);
+ }
+ VectorCopy(newangles, self->movedir);
+}
+
+/*
+===============
+G_StartKamikaze
+===============
+*/
+void G_StartKamikaze( gentity_t *ent ) {
+ gentity_t *explosion;
+ gentity_t *te;
+ vec3_t snapped;
+
+ // start up the explosion logic
+ explosion = G_Spawn();
+
+ explosion->s.eType = ET_EVENTS + EV_KAMIKAZE;
+ explosion->eventTime = level.time;
+
+ if ( ent->client ) {
+ VectorCopy( ent->s.pos.trBase, snapped );
+ }
+ else {
+ VectorCopy( ent->activator->s.pos.trBase, snapped );
+ }
+ SnapVector( snapped ); // save network bandwidth
+ G_SetOrigin( explosion, snapped );
+
+ explosion->classname = "kamikaze";
+ explosion->s.pos.trType = TR_STATIONARY;
+
+ explosion->kamikazeTime = level.time;
+
+ explosion->think = KamikazeDamage;
+ explosion->nextthink = level.time + 100;
+ explosion->count = 0;
+ VectorClear(explosion->movedir);
+
+ trap_LinkEntity( explosion );
+
+ if (ent->client) {
+ //
+ explosion->activator = ent;
+ //
+ ent->s.eFlags &= ~EF_KAMIKAZE;
+ // nuke the guy that used it
+ G_Damage( ent, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_KAMIKAZE );
+ }
+ else {
+ if ( !strcmp(ent->activator->classname, "bodyque") ) {
+ explosion->activator = &g_entities[ent->activator->r.ownerNum];
+ }
+ else {
+ explosion->activator = ent->activator;
+ }
+ }
+
+ // play global sound at all clients
+ te = G_TempEntity(snapped, EV_GLOBAL_TEAM_SOUND );
+ te->r.svFlags |= SVF_BROADCAST;
+ te->s.eventParm = GTS_KAMIKAZE;
+}
+#endif