Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

g_weapon.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 // g_weapon.c 
00024 // perform the server side effects of a weapon firing
00025 
00026 #include "g_local.h"
00027 
00028 static  float   s_quadFactor;
00029 static  vec3_t  forward, right, up;
00030 static  vec3_t  muzzle;
00031 
00032 #define NUM_NAILSHOTS 15
00033 
00034 /*
00035 ================
00036 G_BounceProjectile
00037 ================
00038 */
00039 void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
00040     vec3_t v, newv;
00041     float dot;
00042 
00043     VectorSubtract( impact, start, v );
00044     dot = DotProduct( v, dir );
00045     VectorMA( v, -2*dot, dir, newv );
00046 
00047     VectorNormalize(newv);
00048     VectorMA(impact, 8192, newv, endout);
00049 }
00050 
00051 
00052 /*
00053 ======================================================================
00054 
00055 GAUNTLET
00056 
00057 ======================================================================
00058 */
00059 
00060 void Weapon_Gauntlet( gentity_t *ent ) {
00061 
00062 }
00063 
00064 /*
00065 ===============
00066 CheckGauntletAttack
00067 ===============
00068 */
00069 qboolean CheckGauntletAttack( gentity_t *ent ) {
00070     trace_t     tr;
00071     vec3_t      end;
00072     gentity_t   *tent;
00073     gentity_t   *traceEnt;
00074     int         damage;
00075 
00076     // set aiming directions
00077     AngleVectors (ent->client->ps.viewangles, forward, right, up);
00078 
00079     CalcMuzzlePoint ( ent, forward, right, up, muzzle );
00080 
00081     VectorMA (muzzle, 32, forward, end);
00082 
00083     trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
00084     if ( tr.surfaceFlags & SURF_NOIMPACT ) {
00085         return qfalse;
00086     }
00087 
00088     traceEnt = &g_entities[ tr.entityNum ];
00089 
00090     // send blood impact
00091     if ( traceEnt->takedamage && traceEnt->client ) {
00092         tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
00093         tent->s.otherEntityNum = traceEnt->s.number;
00094         tent->s.eventParm = DirToByte( tr.plane.normal );
00095         tent->s.weapon = ent->s.weapon;
00096     }
00097 
00098     if ( !traceEnt->takedamage) {
00099         return qfalse;
00100     }
00101 
00102     if (ent->client->ps.powerups[PW_QUAD] ) {
00103         G_AddEvent( ent, EV_POWERUP_QUAD, 0 );
00104         s_quadFactor = g_quadfactor.value;
00105     } else {
00106         s_quadFactor = 1;
00107     }
00108 #ifdef MISSIONPACK
00109     if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
00110         s_quadFactor *= 2;
00111     }
00112 #endif
00113 
00114     damage = 50 * s_quadFactor;
00115     G_Damage( traceEnt, ent, ent, forward, tr.endpos,
00116         damage, 0, MOD_GAUNTLET );
00117 
00118     return qtrue;
00119 }
00120 
00121 
00122 /*
00123 ======================================================================
00124 
00125 MACHINEGUN
00126 
00127 ======================================================================
00128 */
00129 
00130 /*
00131 ======================
00132 SnapVectorTowards
00133 
00134 Round a vector to integers for more efficient network
00135 transmission, but make sure that it rounds towards a given point
00136 rather than blindly truncating.  This prevents it from truncating 
00137 into a wall.
00138 ======================
00139 */
00140 void SnapVectorTowards( vec3_t v, vec3_t to ) {
00141     int     i;
00142 
00143     for ( i = 0 ; i < 3 ; i++ ) {
00144         if ( to[i] <= v[i] ) {
00145             v[i] = (int)v[i];
00146         } else {
00147             v[i] = (int)v[i] + 1;
00148         }
00149     }
00150 }
00151 
00152 #ifdef MISSIONPACK
00153 #define CHAINGUN_SPREAD     600
00154 #endif
00155 #define MACHINEGUN_SPREAD   200
00156 #define MACHINEGUN_DAMAGE   7
00157 #define MACHINEGUN_TEAM_DAMAGE  5       // wimpier MG in teamplay
00158 
00159 void Bullet_Fire (gentity_t *ent, float spread, int damage ) {
00160     trace_t     tr;
00161     vec3_t      end;
00162 #ifdef MISSIONPACK
00163     vec3_t      impactpoint, bouncedir;
00164 #endif
00165     float       r;
00166     float       u;
00167     gentity_t   *tent;
00168     gentity_t   *traceEnt;
00169     int         i, passent;
00170 
00171     damage *= s_quadFactor;
00172 
00173     r = random() * M_PI * 2.0f;
00174     u = sin(r) * crandom() * spread * 16;
00175     r = cos(r) * crandom() * spread * 16;
00176     VectorMA (muzzle, 8192*16, forward, end);
00177     VectorMA (end, r, right, end);
00178     VectorMA (end, u, up, end);
00179 
00180     passent = ent->s.number;
00181     for (i = 0; i < 10; i++) {
00182 
00183         trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
00184         if ( tr.surfaceFlags & SURF_NOIMPACT ) {
00185             return;
00186         }
00187 
00188         traceEnt = &g_entities[ tr.entityNum ];
00189 
00190         // snap the endpos to integers, but nudged towards the line
00191         SnapVectorTowards( tr.endpos, muzzle );
00192 
00193         // send bullet impact
00194         if ( traceEnt->takedamage && traceEnt->client ) {
00195             tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
00196             tent->s.eventParm = traceEnt->s.number;
00197             if( LogAccuracyHit( traceEnt, ent ) ) {
00198                 ent->client->accuracy_hits++;
00199             }
00200         } else {
00201             tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
00202             tent->s.eventParm = DirToByte( tr.plane.normal );
00203         }
00204         tent->s.otherEntityNum = ent->s.number;
00205 
00206         if ( traceEnt->takedamage) {
00207 #ifdef MISSIONPACK
00208             if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
00209                 if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
00210                     G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
00211                     VectorCopy( impactpoint, muzzle );
00212                     // the player can hit him/herself with the bounced rail
00213                     passent = ENTITYNUM_NONE;
00214                 }
00215                 else {
00216                     VectorCopy( tr.endpos, muzzle );
00217                     passent = traceEnt->s.number;
00218                 }
00219                 continue;
00220             }
00221             else {
00222 #endif
00223                 G_Damage( traceEnt, ent, ent, forward, tr.endpos,
00224                     damage, 0, MOD_MACHINEGUN);
00225 #ifdef MISSIONPACK
00226             }
00227 #endif
00228         }
00229         break;
00230     }
00231 }
00232 
00233 
00234 /*
00235 ======================================================================
00236 
00237 BFG
00238 
00239 ======================================================================
00240 */
00241 
00242 void BFG_Fire ( gentity_t *ent ) {
00243     gentity_t   *m;
00244 
00245     m = fire_bfg (ent, muzzle, forward);
00246     m->damage *= s_quadFactor;
00247     m->splashDamage *= s_quadFactor;
00248 
00249 //  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
00250 }
00251 
00252 
00253 /*
00254 ======================================================================
00255 
00256 SHOTGUN
00257 
00258 ======================================================================
00259 */
00260 
00261 // DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because
00262 // client predicts same spreads
00263 #define DEFAULT_SHOTGUN_DAMAGE  10
00264 
00265 qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) {
00266     trace_t     tr;
00267     int         damage, i, passent;
00268     gentity_t   *traceEnt;
00269 #ifdef MISSIONPACK
00270     vec3_t      impactpoint, bouncedir;
00271 #endif
00272     vec3_t      tr_start, tr_end;
00273 
00274     passent = ent->s.number;
00275     VectorCopy( start, tr_start );
00276     VectorCopy( end, tr_end );
00277     for (i = 0; i < 10; i++) {
00278         trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT);
00279         traceEnt = &g_entities[ tr.entityNum ];
00280 
00281         // send bullet impact
00282         if (  tr.surfaceFlags & SURF_NOIMPACT ) {
00283             return qfalse;
00284         }
00285 
00286         if ( traceEnt->takedamage) {
00287             damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor;
00288 #ifdef MISSIONPACK
00289             if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
00290                 if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
00291                     G_BounceProjectile( tr_start, impactpoint, bouncedir, tr_end );
00292                     VectorCopy( impactpoint, tr_start );
00293                     // the player can hit him/herself with the bounced rail
00294                     passent = ENTITYNUM_NONE;
00295                 }
00296                 else {
00297                     VectorCopy( tr.endpos, tr_start );
00298                     passent = traceEnt->s.number;
00299                 }
00300                 continue;
00301             }
00302             else {
00303                 G_Damage( traceEnt, ent, ent, forward, tr.endpos,
00304                     damage, 0, MOD_SHOTGUN);
00305                 if( LogAccuracyHit( traceEnt, ent ) ) {
00306                     return qtrue;
00307                 }
00308             }
00309 #else
00310             G_Damage( traceEnt, ent, ent, forward, tr.endpos,   damage, 0, MOD_SHOTGUN);
00311                 if( LogAccuracyHit( traceEnt, ent ) ) {
00312                     return qtrue;
00313                 }
00314 #endif
00315         }
00316         return qfalse;
00317     }
00318     return qfalse;
00319 }
00320 
00321 // this should match CG_ShotgunPattern
00322 void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) {
00323     int         i;
00324     float       r, u;
00325     vec3_t      end;
00326     vec3_t      forward, right, up;
00327     int         oldScore;
00328     qboolean    hitClient = qfalse;
00329 
00330     // derive the right and up vectors from the forward vector, because
00331     // the client won't have any other information
00332     VectorNormalize2( origin2, forward );
00333     PerpendicularVector( right, forward );
00334     CrossProduct( forward, right, up );
00335 
00336     oldScore = ent->client->ps.persistant[PERS_SCORE];
00337 
00338     // generate the "random" spread pattern
00339     for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
00340         r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
00341         u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16;
00342         VectorMA( origin, 8192 * 16, forward, end);
00343         VectorMA (end, r, right, end);
00344         VectorMA (end, u, up, end);
00345         if( ShotgunPellet( origin, end, ent ) && !hitClient ) {
00346             hitClient = qtrue;
00347             ent->client->accuracy_hits++;
00348         }
00349     }
00350 }
00351 
00352 
00353 void weapon_supershotgun_fire (gentity_t *ent) {
00354     gentity_t       *tent;
00355 
00356     // send shotgun blast
00357     tent = G_TempEntity( muzzle, EV_SHOTGUN );
00358     VectorScale( forward, 4096, tent->s.origin2 );
00359     SnapVector( tent->s.origin2 );
00360     tent->s.eventParm = rand() & 255;       // seed for spread pattern
00361     tent->s.otherEntityNum = ent->s.number;
00362 
00363     ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
00364 }
00365 
00366 
00367 /*
00368 ======================================================================
00369 
00370 GRENADE LAUNCHER
00371 
00372 ======================================================================
00373 */
00374 
00375 void weapon_grenadelauncher_fire (gentity_t *ent) {
00376     gentity_t   *m;
00377 
00378     // extra vertical velocity
00379     forward[2] += 0.2f;
00380     VectorNormalize( forward );
00381 
00382     m = fire_grenade (ent, muzzle, forward);
00383     m->damage *= s_quadFactor;
00384     m->splashDamage *= s_quadFactor;
00385 
00386 //  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
00387 }
00388 
00389 /*
00390 ======================================================================
00391 
00392 ROCKET
00393 
00394 ======================================================================
00395 */
00396 
00397 void Weapon_RocketLauncher_Fire (gentity_t *ent) {
00398     gentity_t   *m;
00399 
00400     m = fire_rocket (ent, muzzle, forward);
00401     m->damage *= s_quadFactor;
00402     m->splashDamage *= s_quadFactor;
00403 
00404 //  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
00405 }
00406 
00407 
00408 /*
00409 ======================================================================
00410 
00411 PLASMA GUN
00412 
00413 ======================================================================
00414 */
00415 
00416 void Weapon_Plasmagun_Fire (gentity_t *ent) {
00417     gentity_t   *m;
00418 
00419     m = fire_plasma (ent, muzzle, forward);
00420     m->damage *= s_quadFactor;
00421     m->splashDamage *= s_quadFactor;
00422 
00423 //  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
00424 }
00425 
00426 /*
00427 ======================================================================
00428 
00429 RAILGUN
00430 
00431 ======================================================================
00432 */
00433 
00434 
00435 /*
00436 =================
00437 weapon_railgun_fire
00438 =================
00439 */
00440 #define MAX_RAIL_HITS   4
00441 void weapon_railgun_fire (gentity_t *ent) {
00442     vec3_t      end;
00443 #ifdef MISSIONPACK
00444     vec3_t impactpoint, bouncedir;
00445 #endif
00446     trace_t     trace;
00447     gentity_t   *tent;
00448     gentity_t   *traceEnt;
00449     int         damage;
00450     int         i;
00451     int         hits;
00452     int         unlinked;
00453     int         passent;
00454     gentity_t   *unlinkedEntities[MAX_RAIL_HITS];
00455 
00456     damage = 100 * s_quadFactor;
00457 
00458     VectorMA (muzzle, 8192, forward, end);
00459 
00460     // trace only against the solids, so the railgun will go through people
00461     unlinked = 0;
00462     hits = 0;
00463     passent = ent->s.number;
00464     do {
00465         trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
00466         if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
00467             break;
00468         }
00469         traceEnt = &g_entities[ trace.entityNum ];
00470         if ( traceEnt->takedamage ) {
00471 #ifdef MISSIONPACK
00472             if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
00473                 if ( G_InvulnerabilityEffect( traceEnt, forward, trace.endpos, impactpoint, bouncedir ) ) {
00474                     G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
00475                     // snap the endpos to integers to save net bandwidth, but nudged towards the line
00476                     SnapVectorTowards( trace.endpos, muzzle );
00477                     // send railgun beam effect
00478                     tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
00479                     // set player number for custom colors on the railtrail
00480                     tent->s.clientNum = ent->s.clientNum;
00481                     VectorCopy( muzzle, tent->s.origin2 );
00482                     // move origin a bit to come closer to the drawn gun muzzle
00483                     VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
00484                     VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
00485                     tent->s.eventParm = 255;    // don't make the explosion at the end
00486                     //
00487                     VectorCopy( impactpoint, muzzle );
00488                     // the player can hit him/herself with the bounced rail
00489                     passent = ENTITYNUM_NONE;
00490                 }
00491             }
00492             else {
00493                 if( LogAccuracyHit( traceEnt, ent ) ) {
00494                     hits++;
00495                 }
00496                 G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
00497             }
00498 #else
00499                 if( LogAccuracyHit( traceEnt, ent ) ) {
00500                     hits++;
00501                 }
00502                 G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
00503 #endif
00504         }
00505         if ( trace.contents & CONTENTS_SOLID ) {
00506             break;      // we hit something solid enough to stop the beam
00507         }
00508         // unlink this entity, so the next trace will go past it
00509         trap_UnlinkEntity( traceEnt );
00510         unlinkedEntities[unlinked] = traceEnt;
00511         unlinked++;
00512     } while ( unlinked < MAX_RAIL_HITS );
00513 
00514     // link back in any entities we unlinked
00515     for ( i = 0 ; i < unlinked ; i++ ) {
00516         trap_LinkEntity( unlinkedEntities[i] );
00517     }
00518 
00519     // the final trace endpos will be the terminal point of the rail trail
00520 
00521     // snap the endpos to integers to save net bandwidth, but nudged towards the line
00522     SnapVectorTowards( trace.endpos, muzzle );
00523 
00524     // send railgun beam effect
00525     tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );
00526 
00527     // set player number for custom colors on the railtrail
00528     tent->s.clientNum = ent->s.clientNum;
00529 
00530     VectorCopy( muzzle, tent->s.origin2 );
00531     // move origin a bit to come closer to the drawn gun muzzle
00532     VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
00533     VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );
00534 
00535     // no explosion at end if SURF_NOIMPACT, but still make the trail
00536     if ( trace.surfaceFlags & SURF_NOIMPACT ) {
00537         tent->s.eventParm = 255;    // don't make the explosion at the end
00538     } else {
00539         tent->s.eventParm = DirToByte( trace.plane.normal );
00540     }
00541     tent->s.clientNum = ent->s.clientNum;
00542 
00543     // give the shooter a reward sound if they have made two railgun hits in a row
00544     if ( hits == 0 ) {
00545         // complete miss
00546         ent->client->accurateCount = 0;
00547     } else {
00548         // check for "impressive" reward sound
00549         ent->client->accurateCount += hits;
00550         if ( ent->client->accurateCount >= 2 ) {
00551             ent->client->accurateCount -= 2;
00552             ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
00553             // add the sprite over the player's head
00554             ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
00555             ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE;
00556             ent->client->rewardTime = level.time + REWARD_SPRITE_TIME;
00557         }
00558         ent->client->accuracy_hits++;
00559     }
00560 
00561 }
00562 
00563 
00564 /*
00565 ======================================================================
00566 
00567 GRAPPLING HOOK
00568 
00569 ======================================================================
00570 */
00571 
00572 void Weapon_GrapplingHook_Fire (gentity_t *ent)
00573 {
00574     if (!ent->client->fireHeld && !ent->client->hook)
00575         fire_grapple (ent, muzzle, forward);
00576 
00577     ent->client->fireHeld = qtrue;
00578 }
00579 
00580 void Weapon_HookFree (gentity_t *ent)
00581 {
00582     ent->parent->client->hook = NULL;
00583     ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL;
00584     G_FreeEntity( ent );
00585 }
00586 
00587 void Weapon_HookThink (gentity_t *ent)
00588 {
00589     if (ent->enemy) {
00590         vec3_t v, oldorigin;
00591 
00592         VectorCopy(ent->r.currentOrigin, oldorigin);
00593         v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5;
00594         v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5;
00595         v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5;
00596         SnapVectorTowards( v, oldorigin );  // save net bandwidth
00597 
00598         G_SetOrigin( ent, v );
00599     }
00600 
00601     VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
00602 }
00603 
00604 /*
00605 ======================================================================
00606 
00607 LIGHTNING GUN
00608 
00609 ======================================================================
00610 */
00611 
00612 void Weapon_LightningFire( gentity_t *ent ) {
00613     trace_t     tr;
00614     vec3_t      end;
00615 #ifdef MISSIONPACK
00616     vec3_t impactpoint, bouncedir;
00617 #endif
00618     gentity_t   *traceEnt, *tent;
00619     int         damage, i, passent;
00620 
00621     damage = 8 * s_quadFactor;
00622 
00623     passent = ent->s.number;
00624     for (i = 0; i < 10; i++) {
00625         VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
00626 
00627         trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );
00628 
00629 #ifdef MISSIONPACK
00630         // if not the first trace (the lightning bounced of an invulnerability sphere)
00631         if (i) {
00632             // add bounced off lightning bolt temp entity
00633             // the first lightning bolt is a cgame only visual
00634             //
00635             tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT );
00636             VectorCopy( tr.endpos, end );
00637             SnapVector( end );
00638             VectorCopy( end, tent->s.origin2 );
00639         }
00640 #endif
00641         if ( tr.entityNum == ENTITYNUM_NONE ) {
00642             return;
00643         }
00644 
00645         traceEnt = &g_entities[ tr.entityNum ];
00646 
00647         if ( traceEnt->takedamage) {
00648 #ifdef MISSIONPACK
00649             if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
00650                 if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
00651                     G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
00652                     VectorCopy( impactpoint, muzzle );
00653                     VectorSubtract( end, impactpoint, forward );
00654                     VectorNormalize(forward);
00655                     // the player can hit him/herself with the bounced lightning
00656                     passent = ENTITYNUM_NONE;
00657                 }
00658                 else {
00659                     VectorCopy( tr.endpos, muzzle );
00660                     passent = traceEnt->s.number;
00661                 }
00662                 continue;
00663             }
00664             else {
00665                 G_Damage( traceEnt, ent, ent, forward, tr.endpos,
00666                     damage, 0, MOD_LIGHTNING);
00667             }
00668 #else
00669                 G_Damage( traceEnt, ent, ent, forward, tr.endpos,
00670                     damage, 0, MOD_LIGHTNING);
00671 #endif
00672         }
00673 
00674         if ( traceEnt->takedamage && traceEnt->client ) {
00675             tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
00676             tent->s.otherEntityNum = traceEnt->s.number;
00677             tent->s.eventParm = DirToByte( tr.plane.normal );
00678             tent->s.weapon = ent->s.weapon;
00679             if( LogAccuracyHit( traceEnt, ent ) ) {
00680                 ent->client->accuracy_hits++;
00681             }
00682         } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
00683             tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
00684             tent->s.eventParm = DirToByte( tr.plane.normal );
00685         }
00686 
00687         break;
00688     }
00689 }
00690 
00691 #ifdef MISSIONPACK
00692 /*
00693 ======================================================================
00694 
00695 NAILGUN
00696 
00697 ======================================================================
00698 */
00699 
00700 void Weapon_Nailgun_Fire (gentity_t *ent) {
00701     gentity_t   *m;
00702     int         count;
00703 
00704     for( count = 0; count < NUM_NAILSHOTS; count++ ) {
00705         m = fire_nail (ent, muzzle, forward, right, up );
00706         m->damage *= s_quadFactor;
00707         m->splashDamage *= s_quadFactor;
00708     }
00709 
00710 //  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
00711 }
00712 
00713 
00714 /*
00715 ======================================================================
00716 
00717 PROXIMITY MINE LAUNCHER
00718 
00719 ======================================================================
00720 */
00721 
00722 void weapon_proxlauncher_fire (gentity_t *ent) {
00723     gentity_t   *m;
00724 
00725     // extra vertical velocity
00726     forward[2] += 0.2f;
00727     VectorNormalize( forward );
00728 
00729     m = fire_prox (ent, muzzle, forward);
00730     m->damage *= s_quadFactor;
00731     m->splashDamage *= s_quadFactor;
00732 
00733 //  VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta );  // "real" physics
00734 }
00735 
00736 #endif
00737 
00738 //======================================================================
00739 
00740 
00741 /*
00742 ===============
00743 LogAccuracyHit
00744 ===============
00745 */
00746 qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
00747     if( !target->takedamage ) {
00748         return qfalse;
00749     }
00750 
00751     if ( target == attacker ) {
00752         return qfalse;
00753     }
00754 
00755     if( !target->client ) {
00756         return qfalse;
00757     }
00758 
00759     if( !attacker->client ) {
00760         return qfalse;
00761     }
00762 
00763     if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
00764         return qfalse;
00765     }
00766 
00767     if ( OnSameTeam( target, attacker ) ) {
00768         return qfalse;
00769     }
00770 
00771     return qtrue;
00772 }
00773 
00774 
00775 /*
00776 ===============
00777 CalcMuzzlePoint
00778 
00779 set muzzle location relative to pivoting eye
00780 ===============
00781 */
00782 void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
00783     VectorCopy( ent->s.pos.trBase, muzzlePoint );
00784     muzzlePoint[2] += ent->client->ps.viewheight;
00785     VectorMA( muzzlePoint, 14, forward, muzzlePoint );
00786     // snap to integer coordinates for more efficient network bandwidth usage
00787     SnapVector( muzzlePoint );
00788 }
00789 
00790 /*
00791 ===============
00792 CalcMuzzlePointOrigin
00793 
00794 set muzzle location relative to pivoting eye
00795 ===============
00796 */
00797 void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
00798     VectorCopy( ent->s.pos.trBase, muzzlePoint );
00799     muzzlePoint[2] += ent->client->ps.viewheight;
00800     VectorMA( muzzlePoint, 14, forward, muzzlePoint );
00801     // snap to integer coordinates for more efficient network bandwidth usage
00802     SnapVector( muzzlePoint );
00803 }
00804 
00805 
00806 
00807 /*
00808 ===============
00809 FireWeapon
00810 ===============
00811 */
00812 void FireWeapon( gentity_t *ent ) {
00813     if (ent->client->ps.powerups[PW_QUAD] ) {
00814         s_quadFactor = g_quadfactor.value;
00815     } else {
00816         s_quadFactor = 1;
00817     }
00818 #ifdef MISSIONPACK
00819     if( ent->client->persistantPowerup && ent->client->persistantPowerup->item && ent->client->persistantPowerup->item->giTag == PW_DOUBLER ) {
00820         s_quadFactor *= 2;
00821     }
00822 #endif
00823 
00824     // track shots taken for accuracy tracking.  Grapple is not a weapon and gauntet is just not tracked
00825     if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) {
00826 #ifdef MISSIONPACK
00827         if( ent->s.weapon == WP_NAILGUN ) {
00828             ent->client->accuracy_shots += NUM_NAILSHOTS;
00829         } else {
00830             ent->client->accuracy_shots++;
00831         }
00832 #else
00833         ent->client->accuracy_shots++;
00834 #endif
00835     }
00836 
00837     // set aiming directions
00838     AngleVectors (ent->client->ps.viewangles, forward, right, up);
00839 
00840     CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, forward, right, up, muzzle );
00841 
00842     // fire the specific weapon
00843     switch( ent->s.weapon ) {
00844     case WP_GAUNTLET:
00845         Weapon_Gauntlet( ent );
00846         break;
00847     case WP_LIGHTNING:
00848         Weapon_LightningFire( ent );
00849         break;
00850     case WP_SHOTGUN:
00851         weapon_supershotgun_fire( ent );
00852         break;
00853     case WP_MACHINEGUN:
00854         if ( g_gametype.integer != GT_TEAM ) {
00855             Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE );
00856         } else {
00857             Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE );
00858         }
00859         break;
00860     case WP_GRENADE_LAUNCHER:
00861         weapon_grenadelauncher_fire( ent );
00862         break;
00863     case WP_ROCKET_LAUNCHER:
00864         Weapon_RocketLauncher_Fire( ent );
00865         break;
00866     case WP_PLASMAGUN:
00867         Weapon_Plasmagun_Fire( ent );
00868         break;
00869     case WP_RAILGUN:
00870         weapon_railgun_fire( ent );
00871         break;
00872     case WP_BFG:
00873         BFG_Fire( ent );
00874         break;
00875     case WP_GRAPPLING_HOOK:
00876         Weapon_GrapplingHook_Fire( ent );
00877         break;
00878 #ifdef MISSIONPACK
00879     case WP_NAILGUN:
00880         Weapon_Nailgun_Fire( ent );
00881         break;
00882     case WP_PROX_LAUNCHER:
00883         weapon_proxlauncher_fire( ent );
00884         break;
00885     case WP_CHAINGUN:
00886         Bullet_Fire( ent, CHAINGUN_SPREAD, MACHINEGUN_DAMAGE );
00887         break;
00888 #endif
00889     default:
00890 // FIXME        G_Error( "Bad ent->s.weapon" );
00891         break;
00892     }
00893 }
00894 
00895 
00896 #ifdef MISSIONPACK
00897 
00898 /*
00899 ===============
00900 KamikazeRadiusDamage
00901 ===============
00902 */
00903 static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius ) {
00904     float       dist;
00905     gentity_t   *ent;
00906     int         entityList[MAX_GENTITIES];
00907     int         numListedEntities;
00908     vec3_t      mins, maxs;
00909     vec3_t      v;
00910     vec3_t      dir;
00911     int         i, e;
00912 
00913     if ( radius < 1 ) {
00914         radius = 1;
00915     }
00916 
00917     for ( i = 0 ; i < 3 ; i++ ) {
00918         mins[i] = origin[i] - radius;
00919         maxs[i] = origin[i] + radius;
00920     }
00921 
00922     numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
00923 
00924     for ( e = 0 ; e < numListedEntities ; e++ ) {
00925         ent = &g_entities[entityList[ e ]];
00926 
00927         if (!ent->takedamage) {
00928             continue;
00929         }
00930 
00931         // dont hit things we have already hit
00932         if( ent->kamikazeTime > level.time ) {
00933             continue;
00934         }
00935 
00936         // find the distance from the edge of the bounding box
00937         for ( i = 0 ; i < 3 ; i++ ) {
00938             if ( origin[i] < ent->r.absmin[i] ) {
00939                 v[i] = ent->r.absmin[i] - origin[i];
00940             } else if ( origin[i] > ent->r.absmax[i] ) {
00941                 v[i] = origin[i] - ent->r.absmax[i];
00942             } else {
00943                 v[i] = 0;
00944             }
00945         }
00946 
00947         dist = VectorLength( v );
00948         if ( dist >= radius ) {
00949             continue;
00950         }
00951 
00952 //      if( CanDamage (ent, origin) ) {
00953             VectorSubtract (ent->r.currentOrigin, origin, dir);
00954             // push the center of mass higher than the origin so players
00955             // get knocked into the air more
00956             dir[2] += 24;
00957             G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
00958             ent->kamikazeTime = level.time + 3000;
00959 //      }
00960     }
00961 }
00962 
00963 /*
00964 ===============
00965 KamikazeShockWave
00966 ===============
00967 */
00968 static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, float push, float radius ) {
00969     float       dist;
00970     gentity_t   *ent;
00971     int         entityList[MAX_GENTITIES];
00972     int         numListedEntities;
00973     vec3_t      mins, maxs;
00974     vec3_t      v;
00975     vec3_t      dir;
00976     int         i, e;
00977 
00978     if ( radius < 1 )
00979         radius = 1;
00980 
00981     for ( i = 0 ; i < 3 ; i++ ) {
00982         mins[i] = origin[i] - radius;
00983         maxs[i] = origin[i] + radius;
00984     }
00985 
00986     numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
00987 
00988     for ( e = 0 ; e < numListedEntities ; e++ ) {
00989         ent = &g_entities[entityList[ e ]];
00990 
00991         // dont hit things we have already hit
00992         if( ent->kamikazeShockTime > level.time ) {
00993             continue;
00994         }
00995 
00996         // find the distance from the edge of the bounding box
00997         for ( i = 0 ; i < 3 ; i++ ) {
00998             if ( origin[i] < ent->r.absmin[i] ) {
00999                 v[i] = ent->r.absmin[i] - origin[i];
01000             } else if ( origin[i] > ent->r.absmax[i] ) {
01001                 v[i] = origin[i] - ent->r.absmax[i];
01002             } else {
01003                 v[i] = 0;
01004             }
01005         }
01006 
01007         dist = VectorLength( v );
01008         if ( dist >= radius ) {
01009             continue;
01010         }
01011 
01012 //      if( CanDamage (ent, origin) ) {
01013             VectorSubtract (ent->r.currentOrigin, origin, dir);
01014             dir[2] += 24;
01015             G_Damage( ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE );
01016             //
01017             dir[2] = 0;
01018             VectorNormalize(dir);
01019             if ( ent->client ) {
01020                 ent->client->ps.velocity[0] = dir[0] * push;
01021                 ent->client->ps.velocity[1] = dir[1] * push;
01022                 ent->client->ps.velocity[2] = 100;
01023             }
01024             ent->kamikazeShockTime = level.time + 3000;
01025 //      }
01026     }
01027 }
01028 
01029 /*
01030 ===============
01031 KamikazeDamage
01032 ===============
01033 */
01034 static void KamikazeDamage( gentity_t *self ) {
01035     int i;
01036     float t;
01037     gentity_t *ent;
01038     vec3_t newangles;
01039 
01040     self->count += 100;
01041 
01042     if (self->count >= KAMI_SHOCKWAVE_STARTTIME) {
01043         // shockwave push back
01044         t = self->count - KAMI_SHOCKWAVE_STARTTIME;
01045         KamikazeShockWave(self->s.pos.trBase, self->activator, 25, 400, (int) (float) t * KAMI_SHOCKWAVE_MAXRADIUS / (KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME) );
01046     }
01047     //
01048     if (self->count >= KAMI_EXPLODE_STARTTIME) {
01049         // do our damage
01050         t = self->count - KAMI_EXPLODE_STARTTIME;
01051         KamikazeRadiusDamage( self->s.pos.trBase, self->activator, 400, (int) (float) t * KAMI_BOOMSPHERE_MAXRADIUS / (KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME) );
01052     }
01053 
01054     // either cycle or kill self
01055     if( self->count >= KAMI_SHOCKWAVE_ENDTIME ) {
01056         G_FreeEntity( self );
01057         return;
01058     }
01059     self->nextthink = level.time + 100;
01060 
01061     // add earth quake effect
01062     newangles[0] = crandom() * 2;
01063     newangles[1] = crandom() * 2;
01064     newangles[2] = 0;
01065     for (i = 0; i < MAX_CLIENTS; i++)
01066     {
01067         ent = &g_entities[i];
01068         if (!ent->inuse)
01069             continue;
01070         if (!ent->client)
01071             continue;
01072 
01073         if (ent->client->ps.groundEntityNum != ENTITYNUM_NONE) {
01074             ent->client->ps.velocity[0] += crandom() * 120;
01075             ent->client->ps.velocity[1] += crandom() * 120;
01076             ent->client->ps.velocity[2] = 30 + random() * 25;
01077         }
01078 
01079         ent->client->ps.delta_angles[0] += ANGLE2SHORT(newangles[0] - self->movedir[0]);
01080         ent->client->ps.delta_angles[1] += ANGLE2SHORT(newangles[1] - self->movedir[1]);
01081         ent->client->ps.delta_angles[2] += ANGLE2SHORT(newangles[2] - self->movedir[2]);
01082     }
01083     VectorCopy(newangles, self->movedir);
01084 }
01085 
01086 /*
01087 ===============
01088 G_StartKamikaze
01089 ===============
01090 */
01091 void G_StartKamikaze( gentity_t *ent ) {
01092     gentity_t   *explosion;
01093     gentity_t   *te;
01094     vec3_t      snapped;
01095 
01096     // start up the explosion logic
01097     explosion = G_Spawn();
01098 
01099     explosion->s.eType = ET_EVENTS + EV_KAMIKAZE;
01100     explosion->eventTime = level.time;
01101 
01102     if ( ent->client ) {
01103         VectorCopy( ent->s.pos.trBase, snapped );
01104     }
01105     else {
01106         VectorCopy( ent->activator->s.pos.trBase, snapped );
01107     }
01108     SnapVector( snapped );      // save network bandwidth
01109     G_SetOrigin( explosion, snapped );
01110 
01111     explosion->classname = "kamikaze";
01112     explosion->s.pos.trType = TR_STATIONARY;
01113 
01114     explosion->kamikazeTime = level.time;
01115 
01116     explosion->think = KamikazeDamage;
01117     explosion->nextthink = level.time + 100;
01118     explosion->count = 0;
01119     VectorClear(explosion->movedir);
01120 
01121     trap_LinkEntity( explosion );
01122 
01123     if (ent->client) {
01124         //
01125         explosion->activator = ent;
01126         //
01127         ent->s.eFlags &= ~EF_KAMIKAZE;
01128         // nuke the guy that used it
01129         G_Damage( ent, ent, ent, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_KAMIKAZE );
01130     }
01131     else {
01132         if ( !strcmp(ent->activator->classname, "bodyque") ) {
01133             explosion->activator = &g_entities[ent->activator->r.ownerNum];
01134         }
01135         else {
01136             explosion->activator = ent->activator;
01137         }
01138     }
01139 
01140     // play global sound at all clients
01141     te = G_TempEntity(snapped, EV_GLOBAL_TEAM_SOUND );
01142     te->r.svFlags |= SVF_BROADCAST;
01143     te->s.eventParm = GTS_KAMIKAZE;
01144 }
01145 #endif

Generated on Thu Aug 25 12:37:33 2005 for Quake III Arena by  doxygen 1.3.9.1