codemp/game/g_weapon.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // g_weapon.c 
00004 // perform the server side effects of a weapon firing
00005 
00006 #include "g_local.h"
00007 #include "be_aas.h"
00008 #include "bg_saga.h"
00009 #include "../ghoul2/G2.h"
00010 #include "q_shared.h"
00011 
00012 static  float   s_quadFactor;
00013 static  vec3_t  forward, vright, up;
00014 static  vec3_t  muzzle;
00015 
00016 // Bryar Pistol
00017 //--------
00018 #define BRYAR_PISTOL_VEL                        1600
00019 #define BRYAR_PISTOL_DAMAGE                     10
00020 #define BRYAR_CHARGE_UNIT                       200.0f  // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
00021 #define BRYAR_ALT_SIZE                          1.0f
00022 
00023 // E11 Blaster
00024 //---------
00025 #define BLASTER_SPREAD                          1.6f//1.2f
00026 #define BLASTER_VELOCITY                        2300
00027 #define BLASTER_DAMAGE                          20
00028 
00029 // Tenloss Disruptor
00030 //----------
00031 #define DISRUPTOR_MAIN_DAMAGE                   30 //40
00032 #define DISRUPTOR_MAIN_DAMAGE_SIEGE             50
00033 #define DISRUPTOR_NPC_MAIN_DAMAGE_CUT   0.25f
00034 
00035 #define DISRUPTOR_ALT_DAMAGE                    100 //125
00036 #define DISRUPTOR_NPC_ALT_DAMAGE_CUT    0.2f
00037 #define DISRUPTOR_ALT_TRACES                    3               // can go through a max of 3 damageable(sp?) entities
00038 #define DISRUPTOR_CHARGE_UNIT                   50.0f   // distruptor charging gives us one more unit every 50ms--if you change this, you'll have to do the same in bg_pmove
00039 
00040 // Wookiee Bowcaster
00041 //----------
00042 #define BOWCASTER_DAMAGE                        50
00043 #define BOWCASTER_VELOCITY                      1300
00044 #define BOWCASTER_SPLASH_DAMAGE         0
00045 #define BOWCASTER_SPLASH_RADIUS         0
00046 #define BOWCASTER_SIZE                          2
00047 
00048 #define BOWCASTER_ALT_SPREAD            5.0f
00049 #define BOWCASTER_VEL_RANGE                     0.3f
00050 #define BOWCASTER_CHARGE_UNIT           200.0f  // bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
00051 
00052 // Heavy Repeater
00053 //----------
00054 #define REPEATER_SPREAD                         1.4f
00055 #define REPEATER_DAMAGE                         14
00056 #define REPEATER_VELOCITY                       1600
00057 
00058 #define REPEATER_ALT_SIZE                               3       // half of bbox size
00059 #define REPEATER_ALT_DAMAGE                             60
00060 #define REPEATER_ALT_SPLASH_DAMAGE              60
00061 #define REPEATER_ALT_SPLASH_RADIUS              128
00062 #define REPEATER_ALT_SPLASH_RAD_SIEGE   80
00063 #define REPEATER_ALT_VELOCITY                   1100
00064 
00065 // DEMP2
00066 //----------
00067 #define DEMP2_DAMAGE                            35
00068 #define DEMP2_VELOCITY                          1800
00069 #define DEMP2_SIZE                                      2               // half of bbox size
00070 
00071 #define DEMP2_ALT_DAMAGE                        8 //12          // does 12, 36, 84 at each of the 3 charge levels.
00072 #define DEMP2_CHARGE_UNIT                       700.0f  // demp2 charging gives us one more unit every 700ms--if you change this, you'll have to do the same in bg_weapons
00073 #define DEMP2_ALT_RANGE                         4096
00074 #define DEMP2_ALT_SPLASHRADIUS          256
00075 
00076 // Golan Arms Flechette
00077 //---------
00078 #define FLECHETTE_SHOTS                         5
00079 #define FLECHETTE_SPREAD                        4.0f
00080 #define FLECHETTE_DAMAGE                        12//15
00081 #define FLECHETTE_VEL                           3500
00082 #define FLECHETTE_SIZE                          1
00083 #define FLECHETTE_MINE_RADIUS_CHECK     256
00084 #define FLECHETTE_ALT_DAMAGE            60
00085 #define FLECHETTE_ALT_SPLASH_DAM        60
00086 #define FLECHETTE_ALT_SPLASH_RAD        128
00087 
00088 // Personal Rocket Launcher
00089 //---------
00090 #define ROCKET_VELOCITY                         900
00091 #define ROCKET_DAMAGE                           100
00092 #define ROCKET_SPLASH_DAMAGE            100
00093 #define ROCKET_SPLASH_RADIUS            160
00094 #define ROCKET_SIZE                                     3
00095 #define ROCKET_ALT_THINK_TIME           100
00096 
00097 // Concussion Rifle
00098 //---------
00099 //primary
00100 //man, this thing is too absurdly powerful. having to
00101 //slash the values way down from sp.
00102 #define CONC_VELOCITY                           3000
00103 #define CONC_DAMAGE                                     75 //150
00104 #define CONC_NPC_DAMAGE_EASY            40
00105 #define CONC_NPC_DAMAGE_NORMAL          80
00106 #define CONC_NPC_DAMAGE_HARD            100
00107 #define CONC_SPLASH_DAMAGE                      40 //50
00108 #define CONC_SPLASH_RADIUS                      200 //300
00109 //alt
00110 #define CONC_ALT_DAMAGE                         25 //100
00111 #define CONC_ALT_NPC_DAMAGE_EASY        20
00112 #define CONC_ALT_NPC_DAMAGE_MEDIUM      35
00113 #define CONC_ALT_NPC_DAMAGE_HARD        50
00114 
00115 // Stun Baton
00116 //--------------
00117 #define STUN_BATON_DAMAGE                       20
00118 #define STUN_BATON_ALT_DAMAGE           20
00119 #define STUN_BATON_RANGE                        8
00120 
00121 // Melee
00122 //--------------
00123 #define MELEE_SWING1_DAMAGE                     10
00124 #define MELEE_SWING2_DAMAGE                     12
00125 #define MELEE_RANGE                                     8
00126 
00127 // ATST Main Gun
00128 //--------------
00129 #define ATST_MAIN_VEL                           4000    // 
00130 #define ATST_MAIN_DAMAGE                        25              // 
00131 #define ATST_MAIN_SIZE                          3               // make it easier to hit things
00132 
00133 // ATST Side Gun
00134 //---------------
00135 #define ATST_SIDE_MAIN_DAMAGE                           75
00136 #define ATST_SIDE_MAIN_VELOCITY                         1300
00137 #define ATST_SIDE_MAIN_NPC_DAMAGE_EASY          30
00138 #define ATST_SIDE_MAIN_NPC_DAMAGE_NORMAL        40
00139 #define ATST_SIDE_MAIN_NPC_DAMAGE_HARD          50
00140 #define ATST_SIDE_MAIN_SIZE                                     4
00141 #define ATST_SIDE_MAIN_SPLASH_DAMAGE            10      // yeah, pretty small, either zero out or make it worth having?
00142 #define ATST_SIDE_MAIN_SPLASH_RADIUS            16      // yeah, pretty small, either zero out or make it worth having?
00143 
00144 #define ATST_SIDE_ALT_VELOCITY                          1100
00145 #define ATST_SIDE_ALT_NPC_VELOCITY                      600
00146 #define ATST_SIDE_ALT_DAMAGE                            130
00147 
00148 #define ATST_SIDE_ROCKET_NPC_DAMAGE_EASY        30
00149 #define ATST_SIDE_ROCKET_NPC_DAMAGE_NORMAL      50
00150 #define ATST_SIDE_ROCKET_NPC_DAMAGE_HARD        90
00151 
00152 #define ATST_SIDE_ALT_SPLASH_DAMAGE                     130
00153 #define ATST_SIDE_ALT_SPLASH_RADIUS                     200
00154 #define ATST_SIDE_ALT_ROCKET_SIZE                       5
00155 #define ATST_SIDE_ALT_ROCKET_SPLASH_SCALE       0.5f    // scales splash for NPC's
00156 
00157 extern qboolean G_BoxInBounds( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t boundsMins, vec3_t boundsMaxs );
00158 extern qboolean G_HeavyMelee( gentity_t *attacker );
00159 extern void Jedi_Decloak( gentity_t *self );
00160 
00161 static void WP_FireEmplaced( gentity_t *ent, qboolean altFire );
00162 
00163 void laserTrapStick( gentity_t *ent, vec3_t endpos, vec3_t normal );
00164 
00165 void touch_NULL( gentity_t *ent, gentity_t *other, trace_t *trace )
00166 {
00167 
00168 }
00169 
00170 void laserTrapExplode( gentity_t *self );
00171 void RocketDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod);
00172 
00173 //We should really organize weapon data into tables or parse from the ext data so we have accurate info for this,
00174 float WP_SpeedOfMissileForWeapon( int wp, qboolean alt_fire )
00175 {
00176         return 500;
00177 }
00178 
00179 //-----------------------------------------------------------------------------
00180 void W_TraceSetStart( gentity_t *ent, vec3_t start, vec3_t mins, vec3_t maxs )
00181 //-----------------------------------------------------------------------------
00182 {
00183         //make sure our start point isn't on the other side of a wall
00184         trace_t tr;
00185         vec3_t  entMins;
00186         vec3_t  entMaxs;
00187         vec3_t  eyePoint;
00188 
00189         VectorAdd( ent->r.currentOrigin, ent->r.mins, entMins );
00190         VectorAdd( ent->r.currentOrigin, ent->r.maxs, entMaxs );
00191 
00192         if ( G_BoxInBounds( start, mins, maxs, entMins, entMaxs ) )
00193         {
00194                 return;
00195         }
00196 
00197         if ( !ent->client )
00198         {
00199                 return;
00200         }
00201 
00202         VectorCopy( ent->s.pos.trBase, eyePoint);
00203         eyePoint[2] += ent->client->ps.viewheight;
00204                 
00205         trap_Trace( &tr, eyePoint, mins, maxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP );
00206 
00207         if ( tr.startsolid || tr.allsolid )
00208         {
00209                 return;
00210         }
00211 
00212         if ( tr.fraction < 1.0f )
00213         {
00214                 VectorCopy( tr.endpos, start );
00215         }
00216 }
00217 
00218 
00219 /*
00220 ----------------------------------------------
00221         PLAYER WEAPONS
00222 ----------------------------------------------
00223 */
00224 
00225 /*
00226 ======================================================================
00227 
00228 BRYAR PISTOL
00229 
00230 ======================================================================
00231 */
00232 
00233 //----------------------------------------------
00234 static void WP_FireBryarPistol( gentity_t *ent, qboolean altFire )
00235 //---------------------------------------------------------
00236 {
00237         int damage = BRYAR_PISTOL_DAMAGE;
00238         int count;
00239 
00240         gentity_t       *missile = CreateMissile( muzzle, forward, BRYAR_PISTOL_VEL, 10000, ent, altFire );
00241 
00242         missile->classname = "bryar_proj";
00243         missile->s.weapon = WP_BRYAR_PISTOL;
00244 
00245         if ( altFire )
00246         {
00247                 float boxSize = 0;
00248 
00249                 count = ( level.time - ent->client->ps.weaponChargeTime ) / BRYAR_CHARGE_UNIT;
00250 
00251                 if ( count < 1 )
00252                 {
00253                         count = 1;
00254                 }
00255                 else if ( count > 5 )
00256                 {
00257                         count = 5;
00258                 }
00259 
00260                 if (count > 1)
00261                 {
00262                         damage *= (count*1.7);
00263                 }
00264                 else
00265                 {
00266                         damage *= (count*1.5);
00267                 }
00268 
00269                 missile->s.generic1 = count; // The missile will then render according to the charge level.
00270 
00271                 boxSize = BRYAR_ALT_SIZE*(count*0.5);
00272 
00273                 VectorSet( missile->r.maxs, boxSize, boxSize, boxSize );
00274                 VectorSet( missile->r.mins, -boxSize, -boxSize, -boxSize );
00275         }
00276 
00277         missile->damage = damage;
00278         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00279         if (altFire)
00280         {
00281                 missile->methodOfDeath = MOD_BRYAR_PISTOL_ALT;
00282         }
00283         else
00284         {
00285                 missile->methodOfDeath = MOD_BRYAR_PISTOL;
00286         }
00287         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00288 
00289         // we don't want it to bounce forever
00290         missile->bounceCount = 8;
00291 }
00292 
00293 /*
00294 ======================================================================
00295 
00296 GENERIC
00297 
00298 ======================================================================
00299 */
00300 
00301 //---------------------------------------------------------
00302 void WP_FireTurretMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire, int damage, int velocity, int mod, gentity_t *ignore )
00303 //---------------------------------------------------------
00304 {
00305         gentity_t *missile;
00306 
00307         missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00308 
00309         missile->classname = "generic_proj";
00310         missile->s.weapon = WP_TURRET;
00311 
00312         missile->damage = damage;
00313         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00314         missile->methodOfDeath = mod;
00315         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00316 
00317         if (ignore)
00318         {
00319                 missile->passThroughNum = ignore->s.number+1;
00320         }
00321 
00322         // we don't want it to bounce forever
00323         missile->bounceCount = 8;
00324 }
00325 
00326 //Currently only the seeker drone uses this, but it might be useful for other things as well.
00327 
00328 //---------------------------------------------------------
00329 void WP_FireGenericBlasterMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire, int damage, int velocity, int mod )
00330 //---------------------------------------------------------
00331 {
00332         gentity_t *missile;
00333 
00334         missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00335 
00336         missile->classname = "generic_proj";
00337         missile->s.weapon = WP_BRYAR_PISTOL;
00338 
00339         missile->damage = damage;
00340         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00341         missile->methodOfDeath = mod;
00342         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00343 
00344         // we don't want it to bounce forever
00345         missile->bounceCount = 8;
00346 }
00347 
00348 /*
00349 ======================================================================
00350 
00351 BLASTER
00352 
00353 ======================================================================
00354 */
00355 
00356 //---------------------------------------------------------
00357 void WP_FireBlasterMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire )
00358 //---------------------------------------------------------
00359 {
00360         int velocity    = BLASTER_VELOCITY;
00361         int     damage          = BLASTER_DAMAGE;
00362         gentity_t *missile;
00363 
00364         if (ent->s.eType == ET_NPC)
00365         { //animent
00366                 damage = 10;
00367         }
00368 
00369         missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00370 
00371         missile->classname = "blaster_proj";
00372         missile->s.weapon = WP_BLASTER;
00373 
00374         missile->damage = damage;
00375         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00376         missile->methodOfDeath = MOD_BLASTER;
00377         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00378 
00379         // we don't want it to bounce forever
00380         missile->bounceCount = 8;
00381 }
00382 
00383 //---------------------------------------------------------
00384 void WP_FireTurboLaserMissile( gentity_t *ent, vec3_t start, vec3_t dir )
00385 //---------------------------------------------------------
00386 {
00387         int velocity    = ent->mass; //FIXME: externalize
00388         gentity_t *missile;
00389 
00390         missile = CreateMissile( start, dir, velocity, 10000, ent, qfalse );
00391         
00392         //use a custom shot effect
00393         missile->s.otherEntityNum2 = ent->genericValue14;
00394         //use a custom impact effect
00395         missile->s.emplacedOwner = ent->genericValue15;
00396 
00397         missile->classname = "turbo_proj";
00398         missile->s.weapon = WP_TURRET;
00399 
00400         missile->damage = ent->damage;          //FIXME: externalize
00401         missile->splashDamage = ent->splashDamage;      //FIXME: externalize
00402         missile->splashRadius = ent->splashRadius;      //FIXME: externalize
00403         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00404         missile->methodOfDeath = MOD_TURBLAST; //count as a heavy weap
00405         missile->splashMethodOfDeath = MOD_TURBLAST;// ?SPLASH;
00406         missile->clipmask = MASK_SHOT;
00407 
00408         // we don't want it to bounce forever
00409         missile->bounceCount = 8;
00410 
00411         //set veh as cgame side owner for purpose of fx overrides
00412         missile->s.owner = ent->s.number;
00413 
00414         //don't let them last forever
00415         missile->think = G_FreeEntity;
00416         missile->nextthink = level.time + 5000;//at 20000 speed, that should be more than enough
00417 }
00418 
00419 //---------------------------------------------------------
00420 void WP_FireEmplacedMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire, gentity_t *ignore )
00421 //---------------------------------------------------------
00422 {
00423         int velocity    = BLASTER_VELOCITY;
00424         int     damage          = BLASTER_DAMAGE;
00425         gentity_t *missile;
00426 
00427         missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
00428 
00429         missile->classname = "emplaced_gun_proj";
00430         missile->s.weapon = WP_TURRET;//WP_EMPLACED_GUN;
00431 
00432         missile->activator = ignore;
00433 
00434         missile->damage = damage;
00435         missile->dflags = (DAMAGE_DEATH_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS);
00436         missile->methodOfDeath = MOD_VEHICLE;
00437         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00438 
00439         if (ignore)
00440         {
00441                 missile->passThroughNum = ignore->s.number+1;
00442         }
00443 
00444         // we don't want it to bounce forever
00445         missile->bounceCount = 8;
00446 }
00447 
00448 //---------------------------------------------------------
00449 static void WP_FireBlaster( gentity_t *ent, qboolean altFire )
00450 //---------------------------------------------------------
00451 {
00452         vec3_t  dir, angs;
00453 
00454         vectoangles( forward, angs );
00455 
00456         if ( altFire )
00457         {
00458                 // add some slop to the alt-fire direction
00459                 angs[PITCH] += crandom() * BLASTER_SPREAD;
00460                 angs[YAW]       += crandom() * BLASTER_SPREAD;
00461         }
00462 
00463         AngleVectors( angs, dir, NULL, NULL );
00464 
00465         // FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
00466         WP_FireBlasterMissile( ent, muzzle, dir, altFire );
00467 }
00468 
00469 
00470 
00471 int G_GetHitLocation(gentity_t *target, vec3_t ppoint);
00472 
00473 /*
00474 ======================================================================
00475 
00476 DISRUPTOR
00477 
00478 ======================================================================
00479 */
00480 //---------------------------------------------------------
00481 static void WP_DisruptorMainFire( gentity_t *ent )
00482 //---------------------------------------------------------
00483 {
00484         int                     damage = DISRUPTOR_MAIN_DAMAGE;
00485         qboolean        render_impact = qtrue;
00486         vec3_t          start, end;
00487         trace_t         tr;
00488         gentity_t       *traceEnt, *tent;
00489         float           shotRange = 8192;
00490         int                     ignore, traces;
00491 
00492         if ( g_gametype.integer == GT_SIEGE )
00493         {
00494                 damage = DISRUPTOR_MAIN_DAMAGE_SIEGE;
00495         }
00496 
00497         memset(&tr, 0, sizeof(tr)); //to shut the compiler up
00498 
00499         VectorCopy( ent->client->ps.origin, start );
00500         start[2] += ent->client->ps.viewheight;//By eyes
00501 
00502         VectorMA( start, shotRange, forward, end );
00503 
00504         ignore = ent->s.number;
00505         traces = 0;
00506         while ( traces < 10 )
00507         {//need to loop this in case we hit a Jedi who dodges the shot
00508                 if (d_projectileGhoul2Collision.integer)
00509                 {
00510                         trap_G2Trace( &tr, start, NULL, NULL, end, ignore, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
00511                 }
00512                 else
00513                 {
00514                         trap_Trace( &tr, start, NULL, NULL, end, ignore, MASK_SHOT );
00515                 }
00516 
00517                 traceEnt = &g_entities[tr.entityNum];
00518 
00519                 if (d_projectileGhoul2Collision.integer && traceEnt->inuse && traceEnt->client)
00520                 { //g2 collision checks -rww
00521                         if (traceEnt->inuse && traceEnt->client && traceEnt->ghoul2)
00522                         { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with.
00523                                 traceEnt->client->g2LastSurfaceHit = tr.surfaceFlags;
00524                                 traceEnt->client->g2LastSurfaceTime = level.time;
00525                         }
00526 
00527                         if (traceEnt->ghoul2)
00528                         {
00529                                 tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here.
00530                         }
00531                 }
00532 
00533                 if (traceEnt && traceEnt->client && traceEnt->client->ps.duelInProgress &&
00534                         traceEnt->client->ps.duelIndex != ent->s.number)
00535                 {
00536                         VectorCopy( tr.endpos, start );
00537                         ignore = tr.entityNum;
00538                         traces++;
00539                         continue;
00540                 }
00541 
00542                 if ( Jedi_DodgeEvasion( traceEnt, ent, &tr, G_GetHitLocation(traceEnt, tr.endpos) ) )
00543                 {//act like we didn't even hit him
00544                         VectorCopy( tr.endpos, start );
00545                         ignore = tr.entityNum;
00546                         traces++;
00547                         continue;
00548                 }
00549                 else if (traceEnt && traceEnt->client && traceEnt->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] >= FORCE_LEVEL_3)
00550                 {
00551                         if (WP_SaberCanBlock(traceEnt, tr.endpos, 0, MOD_DISRUPTOR, qtrue, 0))
00552                         { //broadcast and stop the shot because it was blocked
00553                                 gentity_t *te = NULL;
00554 
00555                                 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT );
00556                                 VectorCopy( muzzle, tent->s.origin2 );
00557                                 tent->s.eventParm = ent->s.number;
00558 
00559                                 te = G_TempEntity( tr.endpos, EV_SABER_BLOCK );
00560                                 VectorCopy(tr.endpos, te->s.origin);
00561                                 VectorCopy(tr.plane.normal, te->s.angles);
00562                                 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
00563                                 {
00564                                         te->s.angles[1] = 1;
00565                                 }
00566                                 te->s.eventParm = 0;
00567                                 te->s.weapon = 0;//saberNum
00568                                 te->s.legsAnim = 0;//bladeNum
00569 
00570                                 return;
00571                         }
00572                 }
00573                 else if ( (traceEnt->flags&FL_SHIELDED) )
00574                 {//stopped cold
00575                         return;
00576                 }
00577                 //a Jedi is not dodging this shot
00578                 break;
00579         }
00580 
00581         if ( tr.surfaceFlags & SURF_NOIMPACT ) 
00582         {
00583                 render_impact = qfalse;
00584         }
00585 
00586         // always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
00587         tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT );
00588         VectorCopy( muzzle, tent->s.origin2 );
00589         tent->s.eventParm = ent->s.number;
00590 
00591         traceEnt = &g_entities[tr.entityNum];
00592 
00593         if ( render_impact )
00594         {
00595                 if ( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage )
00596                 {
00597                         if ( traceEnt->client && LogAccuracyHit( traceEnt, ent )) 
00598                         {
00599                                 ent->client->accuracy_hits++;
00600                         } 
00601 
00602                         G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NORMAL, MOD_DISRUPTOR );
00603                         
00604                         tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_HIT );
00605                         tent->s.eventParm = DirToByte( tr.plane.normal );
00606                         if (traceEnt->client)
00607                         {
00608                                 tent->s.weapon = 1;
00609                         }
00610                 }
00611                 else 
00612                 {
00613                          // Hmmm, maybe don't make any marks on things that could break
00614                         tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS );
00615                         tent->s.eventParm = DirToByte( tr.plane.normal );
00616                         tent->s.weapon = 1;
00617                 }
00618         }
00619 }
00620 
00621 
00622 qboolean G_CanDisruptify(gentity_t *ent)
00623 {
00624         if (!ent || !ent->inuse || !ent->client || ent->s.eType != ET_NPC ||
00625                 ent->s.NPC_class != CLASS_VEHICLE || !ent->m_pVehicle)
00626         { //not vehicle
00627                 return qtrue;
00628         }
00629 
00630         if (ent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
00631         { //animal is only type that can be disintigeiteigerated
00632                 return qtrue;
00633         }
00634 
00635         //don't do it to any other veh
00636         return qfalse;
00637 }
00638 
00639 //---------------------------------------------------------
00640 void WP_DisruptorAltFire( gentity_t *ent )
00641 //---------------------------------------------------------
00642 {
00643         int                     damage = 0, skip;
00644         qboolean        render_impact = qtrue;
00645         vec3_t          start, end;
00646         vec3_t          muzzle2;
00647         trace_t         tr;
00648         gentity_t       *traceEnt, *tent;
00649         float           shotRange = 8192.0f;
00650         int                     i;
00651         int                     count, maxCount = 60;
00652         int                     traces = DISRUPTOR_ALT_TRACES;
00653         qboolean        fullCharge = qfalse;
00654 
00655         damage = DISRUPTOR_ALT_DAMAGE-30;
00656 
00657         VectorCopy( muzzle, muzzle2 ); // making a backup copy
00658 
00659         if (ent->client)
00660         {
00661                 VectorCopy( ent->client->ps.origin, start );
00662                 start[2] += ent->client->ps.viewheight;//By eyes
00663 
00664                 count = ( level.time - ent->client->ps.weaponChargeTime ) / DISRUPTOR_CHARGE_UNIT;
00665                 if ( g_gametype.integer == GT_SIEGE )
00666                 {//maybe a full alt-charge should be a *bit* more dangerous in Siege mode?
00667                         //maxCount = ceil((200.0f-(float)damage)/2.0f);//cap at 200 damage total
00668                         maxCount = 200;//the previous line ALWAYS evaluated to 135 - was that on purpose?
00669                 }
00670         }
00671         else
00672         {
00673                 VectorCopy( ent->r.currentOrigin, start );
00674                 start[2] += 24;
00675 
00676                 count = ( 100 ) / DISRUPTOR_CHARGE_UNIT;
00677         }
00678 
00679         count *= 2;
00680 
00681         if ( count < 1 )
00682         {
00683                 count = 1;
00684         }
00685         else if ( count >= maxCount )
00686         {
00687                 count = maxCount;
00688                 fullCharge = qtrue;
00689         }
00690 
00691         // more powerful charges go through more things
00692         if ( count < 10 )
00693         {
00694                 traces = 1;
00695         }
00696         else if ( count < 20 )
00697         {
00698                 traces = 2;
00699         }
00700 
00701         damage += count;
00702         
00703         skip = ent->s.number;
00704 
00705         for (i = 0; i < traces; i++ )
00706         {
00707                 VectorMA( start, shotRange, forward, end );
00708 
00709                 if (d_projectileGhoul2Collision.integer)
00710                 {
00711                         trap_G2Trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
00712                 }
00713                 else
00714                 {
00715                         trap_Trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT );
00716                 }
00717 
00718                 traceEnt = &g_entities[tr.entityNum];
00719 
00720                 if (d_projectileGhoul2Collision.integer && traceEnt->inuse && traceEnt->client)
00721                 { //g2 collision checks -rww
00722                         if (traceEnt->inuse && traceEnt->client && traceEnt->ghoul2)
00723                         { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with.
00724                                 traceEnt->client->g2LastSurfaceHit = tr.surfaceFlags;
00725                                 traceEnt->client->g2LastSurfaceTime = level.time;
00726                         }
00727 
00728                         if (traceEnt->ghoul2)
00729                         {
00730                                 tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here.
00731                         }
00732                 }
00733 
00734                 if ( tr.surfaceFlags & SURF_NOIMPACT ) 
00735                 {
00736                         render_impact = qfalse;
00737                 }
00738 
00739                 if (traceEnt && traceEnt->client && traceEnt->client->ps.duelInProgress &&
00740                         traceEnt->client->ps.duelIndex != ent->s.number)
00741                 {
00742                         skip = tr.entityNum;
00743                         VectorCopy(tr.endpos, start);
00744                         continue;
00745                 }
00746 
00747                 if (Jedi_DodgeEvasion(traceEnt, ent, &tr, G_GetHitLocation(traceEnt, tr.endpos)))
00748                 {
00749                         skip = tr.entityNum;
00750                         VectorCopy(tr.endpos, start);
00751                         continue;
00752                 }
00753                 else if (traceEnt && traceEnt->client && traceEnt->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] >= FORCE_LEVEL_3)
00754                 {
00755                         if (WP_SaberCanBlock(traceEnt, tr.endpos, 0, MOD_DISRUPTOR_SNIPER, qtrue, 0))
00756                         { //broadcast and stop the shot because it was blocked
00757                                 gentity_t *te = NULL;
00758 
00759                                 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT );
00760                                 VectorCopy( muzzle, tent->s.origin2 );
00761                                 tent->s.shouldtarget = fullCharge;
00762                                 tent->s.eventParm = ent->s.number;
00763 
00764                                 te = G_TempEntity( tr.endpos, EV_SABER_BLOCK );
00765                                 VectorCopy(tr.endpos, te->s.origin);
00766                                 VectorCopy(tr.plane.normal, te->s.angles);
00767                                 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
00768                                 {
00769                                         te->s.angles[1] = 1;
00770                                 }
00771                                 te->s.eventParm = 0;
00772                                 te->s.weapon = 0;//saberNum
00773                                 te->s.legsAnim = 0;//bladeNum
00774 
00775                                 return;
00776                         }
00777                 }
00778 
00779                 // always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
00780                 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT );
00781                 VectorCopy( muzzle, tent->s.origin2 );
00782                 tent->s.shouldtarget = fullCharge;
00783                 tent->s.eventParm = ent->s.number;
00784 
00785                 // If the beam hits a skybox, etc. it would look foolish to add impact effects
00786                 if ( render_impact ) 
00787                 {
00788                         if ( traceEnt->takedamage && traceEnt->client )
00789                         {
00790                                 tent->s.otherEntityNum = traceEnt->s.number;
00791 
00792                                 // Create a simple impact type mark
00793                                 tent = G_TempEntity(tr.endpos, EV_MISSILE_MISS);
00794                                 tent->s.eventParm = DirToByte(tr.plane.normal);
00795                                 tent->s.eFlags |= EF_ALT_FIRING;
00796         
00797                                 if ( LogAccuracyHit( traceEnt, ent )) 
00798                                 {
00799                                         if (ent->client)
00800                                         {
00801                                                 ent->client->accuracy_hits++;
00802                                         }
00803                                 }
00804                         } 
00805                         else 
00806                         {
00807                                  if ( traceEnt->r.svFlags & SVF_GLASS_BRUSH 
00808                                                 || traceEnt->takedamage 
00809                                                 || traceEnt->s.eType == ET_MOVER )
00810                                  {
00811                                         if ( traceEnt->takedamage )
00812                                         {
00813                                                 G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 
00814                                                                 DAMAGE_NO_KNOCKBACK, MOD_DISRUPTOR_SNIPER );
00815 
00816                                                 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_HIT );
00817                                                 tent->s.eventParm = DirToByte( tr.plane.normal );
00818                                         }
00819                                  }
00820                                  else
00821                                  {
00822                                          // Hmmm, maybe don't make any marks on things that could break
00823                                         tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS );
00824                                         tent->s.eventParm = DirToByte( tr.plane.normal );
00825                                  }
00826                                 break; // and don't try any more traces
00827                         }
00828 
00829                         if ( (traceEnt->flags&FL_SHIELDED) )
00830                         {//stops us cold
00831                                 break;
00832                         }
00833 
00834                         if ( traceEnt->takedamage )
00835                         {
00836                                 vec3_t preAng;
00837                                 int preHealth = traceEnt->health;
00838                                 int preLegs = 0;
00839                                 int preTorso = 0;
00840 
00841                                 if (traceEnt->client)
00842                                 {
00843                                         preLegs = traceEnt->client->ps.legsAnim;
00844                                         preTorso = traceEnt->client->ps.torsoAnim;
00845                                         VectorCopy(traceEnt->client->ps.viewangles, preAng);
00846                                 }
00847 
00848                                 G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_DISRUPTOR_SNIPER );
00849 
00850                                 if (traceEnt->client && preHealth > 0 && traceEnt->health <= 0 && fullCharge &&
00851                                         G_CanDisruptify(traceEnt))
00852                                 { //was killed by a fully charged sniper shot, so disintegrate
00853                                         VectorCopy(preAng, traceEnt->client->ps.viewangles);
00854 
00855                                         traceEnt->client->ps.eFlags |= EF_DISINTEGRATION;
00856                                         VectorCopy(tr.endpos, traceEnt->client->ps.lastHitLoc);
00857 
00858                                         traceEnt->client->ps.legsAnim = preLegs;
00859                                         traceEnt->client->ps.torsoAnim = preTorso;
00860 
00861                                         traceEnt->r.contents = 0;
00862 
00863                                         VectorClear(traceEnt->client->ps.velocity);
00864                                 }
00865 
00866                                 tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_HIT );
00867                                 tent->s.eventParm = DirToByte( tr.plane.normal );
00868                                 if (traceEnt->client)
00869                                 {
00870                                         tent->s.weapon = 1;
00871                                 }
00872                         }
00873                 }
00874                 else // not rendering impact, must be a skybox or other similar thing?
00875                 {
00876                         break; // don't try anymore traces
00877                 }
00878 
00879                 // Get ready for an attempt to trace through another person
00880                 VectorCopy( tr.endpos, muzzle );
00881                 VectorCopy( tr.endpos, start );
00882                 skip = tr.entityNum;
00883         }
00884 }
00885 
00886 
00887 //---------------------------------------------------------
00888 static void WP_FireDisruptor( gentity_t *ent, qboolean altFire )
00889 //---------------------------------------------------------
00890 {
00891         if (!ent || !ent->client || ent->client->ps.zoomMode != 1)
00892         { //do not ever let it do the alt fire when not zoomed
00893                 altFire = qfalse;
00894         }
00895 
00896         if (ent && ent->s.eType == ET_NPC && !ent->client)
00897         { //special case for animents
00898                 WP_DisruptorAltFire( ent );
00899                 return;
00900         }
00901 
00902         if ( altFire )
00903         {
00904                 WP_DisruptorAltFire( ent );
00905         }
00906         else
00907         {
00908                 WP_DisruptorMainFire( ent );
00909         }
00910 }
00911 
00912 
00913 /*
00914 ======================================================================
00915 
00916 BOWCASTER
00917 
00918 ======================================================================
00919 */
00920 
00921 static void WP_BowcasterAltFire( gentity_t *ent )
00922 {
00923         int     damage  = BOWCASTER_DAMAGE;
00924 
00925         gentity_t *missile = CreateMissile( muzzle, forward, BOWCASTER_VELOCITY, 10000, ent, qfalse);
00926 
00927         missile->classname = "bowcaster_proj";
00928         missile->s.weapon = WP_BOWCASTER;
00929 
00930         VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
00931         VectorScale( missile->r.maxs, -1, missile->r.mins );
00932 
00933         missile->damage = damage;
00934         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00935         missile->methodOfDeath = MOD_BOWCASTER;
00936         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00937 
00938         missile->flags |= FL_BOUNCE;
00939         missile->bounceCount = 3;
00940 }
00941 
00942 //---------------------------------------------------------
00943 static void WP_BowcasterMainFire( gentity_t *ent )
00944 //---------------------------------------------------------
00945 {
00946         int                     damage  = BOWCASTER_DAMAGE, count;
00947         float           vel;
00948         vec3_t          angs, dir;
00949         gentity_t       *missile;
00950         int i;
00951 
00952         if (!ent->client)
00953         {
00954                 count = 1;
00955         }
00956         else
00957         {
00958                 count = ( level.time - ent->client->ps.weaponChargeTime ) / BOWCASTER_CHARGE_UNIT;
00959         }
00960 
00961         if ( count < 1 )
00962         {
00963                 count = 1;
00964         }
00965         else if ( count > 5 )
00966         {
00967                 count = 5;
00968         }
00969 
00970         if ( !(count & 1 ))
00971         {
00972                 // if we aren't odd, knock us down a level
00973                 count--;
00974         }
00975 
00976         //scale the damage down based on how many are about to be fired
00977         if (count <= 1)
00978         {
00979                 damage = 50;
00980         }
00981         else if (count == 2)
00982         {
00983                 damage = 45;
00984         }
00985         else if (count == 3)
00986         {
00987                 damage = 40;
00988         }
00989         else if (count == 4)
00990         {
00991                 damage = 35;
00992         }
00993         else
00994         {
00995                 damage = 30;
00996         }
00997 
00998         for (i = 0; i < count; i++ )
00999         {
01000                 // create a range of different velocities
01001                 vel = BOWCASTER_VELOCITY * ( crandom() * BOWCASTER_VEL_RANGE + 1.0f );
01002 
01003                 vectoangles( forward, angs );
01004 
01005                 // add some slop to the alt-fire direction
01006                 angs[PITCH] += crandom() * BOWCASTER_ALT_SPREAD * 0.2f;
01007                 angs[YAW]       += ((i+0.5f) * BOWCASTER_ALT_SPREAD - count * 0.5f * BOWCASTER_ALT_SPREAD );
01008                 
01009                 AngleVectors( angs, dir, NULL, NULL );
01010 
01011                 missile = CreateMissile( muzzle, dir, vel, 10000, ent, qtrue );
01012 
01013                 missile->classname = "bowcaster_alt_proj";
01014                 missile->s.weapon = WP_BOWCASTER;
01015 
01016                 VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
01017                 VectorScale( missile->r.maxs, -1, missile->r.mins );
01018 
01019                 missile->damage = damage;
01020                 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01021                 missile->methodOfDeath = MOD_BOWCASTER;
01022                 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01023 
01024                 // we don't want it to bounce
01025                 missile->bounceCount = 0;
01026         }
01027 }
01028 
01029 //---------------------------------------------------------
01030 static void WP_FireBowcaster( gentity_t *ent, qboolean altFire )
01031 //---------------------------------------------------------
01032 {
01033         if ( altFire )
01034         {
01035                 WP_BowcasterAltFire( ent );
01036         }
01037         else
01038         {
01039                 WP_BowcasterMainFire( ent );
01040         }
01041 }
01042 
01043 
01044 
01045 /*
01046 ======================================================================
01047 
01048 REPEATER
01049 
01050 ======================================================================
01051 */
01052 
01053 //---------------------------------------------------------
01054 static void WP_RepeaterMainFire( gentity_t *ent, vec3_t dir )
01055 //---------------------------------------------------------
01056 {
01057         int     damage  = REPEATER_DAMAGE;
01058 
01059         gentity_t *missile = CreateMissile( muzzle, dir, REPEATER_VELOCITY, 10000, ent, qfalse );
01060 
01061         missile->classname = "repeater_proj";
01062         missile->s.weapon = WP_REPEATER;
01063 
01064         missile->damage = damage;
01065         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01066         missile->methodOfDeath = MOD_REPEATER;
01067         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01068 
01069         // we don't want it to bounce forever
01070         missile->bounceCount = 8;
01071 }
01072 
01073 //---------------------------------------------------------
01074 static void WP_RepeaterAltFire( gentity_t *ent )
01075 //---------------------------------------------------------
01076 {
01077         int     damage  = REPEATER_ALT_DAMAGE;
01078 
01079         gentity_t *missile = CreateMissile( muzzle, forward, REPEATER_ALT_VELOCITY, 10000, ent, qtrue );
01080 
01081         missile->classname = "repeater_alt_proj";
01082         missile->s.weapon = WP_REPEATER;
01083 
01084         VectorSet( missile->r.maxs, REPEATER_ALT_SIZE, REPEATER_ALT_SIZE, REPEATER_ALT_SIZE );
01085         VectorScale( missile->r.maxs, -1, missile->r.mins );
01086         missile->s.pos.trType = TR_GRAVITY;
01087         missile->s.pos.trDelta[2] += 40.0f; //give a slight boost in the upward direction
01088         missile->damage = damage;
01089         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01090         missile->methodOfDeath = MOD_REPEATER_ALT;
01091         missile->splashMethodOfDeath = MOD_REPEATER_ALT_SPLASH;
01092         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01093         missile->splashDamage = REPEATER_ALT_SPLASH_DAMAGE;
01094         if ( g_gametype.integer == GT_SIEGE )   // we've been having problems with this being too hyper-potent because of it's radius
01095         {
01096                 missile->splashRadius = REPEATER_ALT_SPLASH_RAD_SIEGE;
01097         }
01098         else
01099         {
01100                 missile->splashRadius = REPEATER_ALT_SPLASH_RADIUS;
01101         }
01102 
01103         // we don't want it to bounce forever
01104         missile->bounceCount = 8;
01105 }
01106 
01107 //---------------------------------------------------------
01108 static void WP_FireRepeater( gentity_t *ent, qboolean altFire )
01109 //---------------------------------------------------------
01110 {
01111         vec3_t  dir, angs;
01112 
01113         vectoangles( forward, angs );
01114 
01115         if ( altFire )
01116         {
01117                 WP_RepeaterAltFire( ent );
01118         }
01119         else
01120         {
01121                 // add some slop to the alt-fire direction
01122                 angs[PITCH] += crandom() * REPEATER_SPREAD;
01123                 angs[YAW]       += crandom() * REPEATER_SPREAD;
01124 
01125                 AngleVectors( angs, dir, NULL, NULL );
01126 
01127                 WP_RepeaterMainFire( ent, dir );
01128         }
01129 }
01130 
01131 
01132 /*
01133 ======================================================================
01134 
01135 DEMP2
01136 
01137 ======================================================================
01138 */
01139 
01140 static void WP_DEMP2_MainFire( gentity_t *ent )
01141 {
01142         int     damage  = DEMP2_DAMAGE;
01143 
01144         gentity_t *missile = CreateMissile( muzzle, forward, DEMP2_VELOCITY, 10000, ent, qfalse);
01145 
01146         missile->classname = "demp2_proj";
01147         missile->s.weapon = WP_DEMP2;
01148 
01149         VectorSet( missile->r.maxs, DEMP2_SIZE, DEMP2_SIZE, DEMP2_SIZE );
01150         VectorScale( missile->r.maxs, -1, missile->r.mins );
01151         missile->damage = damage;
01152         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01153         missile->methodOfDeath = MOD_DEMP2;
01154         missile->clipmask = MASK_SHOT;
01155 
01156         // we don't want it to ever bounce
01157         missile->bounceCount = 0;
01158 }
01159 
01160 static gentity_t *ent_list[MAX_GENTITIES];
01161 
01162 void DEMP2_AltRadiusDamage( gentity_t *ent )
01163 {
01164         float           frac = ( level.time - ent->genericValue5 ) / 800.0f; // / 1600.0f; // synchronize with demp2 effect
01165         float           dist, radius, fact;
01166         gentity_t       *gent;
01167         int                     iEntityList[MAX_GENTITIES];
01168         gentity_t       *entityList[MAX_GENTITIES];
01169         gentity_t       *myOwner = NULL;
01170         int                     numListedEntities, i, e;
01171         vec3_t          mins, maxs;
01172         vec3_t          v, dir;
01173 
01174         if (ent->r.ownerNum >= 0 &&
01175                 ent->r.ownerNum < /*MAX_CLIENTS ... let npc's/shooters use it*/MAX_GENTITIES)
01176         {
01177                 myOwner = &g_entities[ent->r.ownerNum];
01178         }
01179 
01180         if (!myOwner || !myOwner->inuse || !myOwner->client)
01181         {
01182                 ent->think = G_FreeEntity;
01183                 ent->nextthink = level.time;
01184                 return;
01185         }
01186 
01187         frac *= frac * frac; // yes, this is completely ridiculous...but it causes the shell to grow slowly then "explode" at the end
01188         
01189         radius = frac * 200.0f; // 200 is max radius...the model is aprox. 100 units tall...the fx draw code mults. this by 2.
01190 
01191         fact = ent->count*0.6;
01192 
01193         if (fact < 1)
01194         {
01195                 fact = 1;
01196         }
01197 
01198         radius *= fact;
01199 
01200         for ( i = 0 ; i < 3 ; i++ ) 
01201         {
01202                 mins[i] = ent->r.currentOrigin[i] - radius;
01203                 maxs[i] = ent->r.currentOrigin[i] + radius;
01204         }
01205 
01206         numListedEntities = trap_EntitiesInBox( mins, maxs, iEntityList, MAX_GENTITIES );
01207 
01208         i = 0;
01209         while (i < numListedEntities)
01210         {
01211                 entityList[i] = &g_entities[iEntityList[i]];
01212                 i++;
01213         }
01214 
01215         for ( e = 0 ; e < numListedEntities ; e++ ) 
01216         {
01217                 gent = entityList[ e ];
01218 
01219                 if ( !gent || !gent->takedamage || !gent->r.contents )
01220                 {
01221                         continue;
01222                 }
01223 
01224                 // find the distance from the edge of the bounding box
01225                 for ( i = 0 ; i < 3 ; i++ ) 
01226                 {
01227                         if ( ent->r.currentOrigin[i] < gent->r.absmin[i] ) 
01228                         {
01229                                 v[i] = gent->r.absmin[i] - ent->r.currentOrigin[i];
01230                         } 
01231                         else if ( ent->r.currentOrigin[i] > gent->r.absmax[i] ) 
01232                         {
01233                                 v[i] = ent->r.currentOrigin[i] - gent->r.absmax[i];
01234                         } 
01235                         else 
01236                         {
01237                                 v[i] = 0;
01238                         }
01239                 }
01240 
01241                 // shape is an ellipsoid, so cut vertical distance in half`
01242                 v[2] *= 0.5f;
01243 
01244                 dist = VectorLength( v );
01245 
01246                 if ( dist >= radius ) 
01247                 {
01248                         // shockwave hasn't hit them yet
01249                         continue;
01250                 }
01251 
01252                 if (dist+(16*ent->count) < ent->genericValue6)
01253                 {
01254                         // shockwave has already hit this thing...
01255                         continue;
01256                 }
01257 
01258                 VectorCopy( gent->r.currentOrigin, v );
01259                 VectorSubtract( v, ent->r.currentOrigin, dir);
01260 
01261                 // push the center of mass higher than the origin so players get knocked into the air more
01262                 dir[2] += 12;
01263 
01264                 if (gent != myOwner)
01265                 {
01266                         G_Damage( gent, myOwner, myOwner, dir, ent->r.currentOrigin, ent->damage, DAMAGE_DEATH_KNOCKBACK, ent->splashMethodOfDeath );
01267                         if ( gent->takedamage 
01268                                 && gent->client ) 
01269                         {
01270                                 if ( gent->client->ps.electrifyTime < level.time )
01271                                 {//electrocution effect
01272                                         if (gent->s.eType == ET_NPC && gent->s.NPC_class == CLASS_VEHICLE &&
01273                                                 gent->m_pVehicle && (gent->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER || gent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER))
01274                                         { //do some extra stuff to speeders/walkers
01275                                                 gent->client->ps.electrifyTime = level.time + Q_irand( 3000, 4000 );
01276                                         }
01277                                         else if ( gent->s.NPC_class != CLASS_VEHICLE 
01278                                                 || (gent->m_pVehicle && gent->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER) )
01279                                         {//don't do this to fighters
01280                                                 gent->client->ps.electrifyTime = level.time + Q_irand( 300, 800 );
01281                                         }
01282                                 }
01283                                 if ( gent->client->ps.powerups[PW_CLOAKED] )
01284                                 {//disable cloak temporarily
01285                                         Jedi_Decloak( gent );
01286                                         gent->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
01287                                 }
01288                         }
01289                 }
01290         }
01291 
01292         // store the last fraction so that next time around we can test against those things that fall between that last point and where the current shockwave edge is
01293         ent->genericValue6 = radius;
01294 
01295         if ( frac < 1.0f )
01296         {
01297                 // shock is still happening so continue letting it expand
01298                 ent->nextthink = level.time + 50;
01299         }
01300         else
01301         { //don't just leave the entity around
01302                 ent->think = G_FreeEntity;
01303                 ent->nextthink = level.time;
01304         }
01305 }
01306 
01307 //---------------------------------------------------------
01308 void DEMP2_AltDetonate( gentity_t *ent )
01309 //---------------------------------------------------------
01310 {
01311         gentity_t *efEnt;
01312 
01313         G_SetOrigin( ent, ent->r.currentOrigin );
01314         if (!ent->pos1[0] && !ent->pos1[1] && !ent->pos1[2])
01315         { //don't play effect with a 0'd out directional vector
01316                 ent->pos1[1] = 1;
01317         }
01318         //Let's just save ourself some bandwidth and play both the effect and sphere spawn in 1 event
01319         efEnt = G_PlayEffect( EFFECT_EXPLOSION_DEMP2ALT, ent->r.currentOrigin, ent->pos1 );
01320 
01321         if (efEnt)
01322         {
01323                 efEnt->s.weapon = ent->count*2;
01324         }
01325 
01326         ent->genericValue5 = level.time;
01327         ent->genericValue6 = 0;
01328         ent->nextthink = level.time + 50;
01329         ent->think = DEMP2_AltRadiusDamage;
01330         ent->s.eType = ET_GENERAL; // make us a missile no longer
01331 }
01332 
01333 //---------------------------------------------------------
01334 static void WP_DEMP2_AltFire( gentity_t *ent )
01335 //---------------------------------------------------------
01336 {
01337         int             damage  = DEMP2_ALT_DAMAGE;
01338         int             count, origcount;
01339         float   fact;
01340         vec3_t  start, end;
01341         trace_t tr;
01342         gentity_t *missile;
01343 
01344         VectorCopy( muzzle, start );
01345 
01346         VectorMA( start, DEMP2_ALT_RANGE, forward, end );
01347 
01348         count = ( level.time - ent->client->ps.weaponChargeTime ) / DEMP2_CHARGE_UNIT;
01349 
01350         origcount = count;
01351 
01352         if ( count < 1 )
01353         {
01354                 count = 1;
01355         }
01356         else if ( count > 3 )
01357         {
01358                 count = 3;
01359         }
01360 
01361         fact = count*0.8;
01362         if (fact < 1)
01363         {
01364                 fact = 1;
01365         }
01366         damage *= fact;
01367 
01368         if (!origcount)
01369         { //this was just a tap-fire
01370                 damage = 1;
01371         }
01372 
01373         trap_Trace( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
01374 
01375         missile = G_Spawn();
01376         G_SetOrigin(missile, tr.endpos);
01377         //In SP the impact actually travels as a missile based on the trace fraction, but we're
01378         //just going to be instant. -rww
01379 
01380         VectorCopy( tr.plane.normal, missile->pos1 );
01381 
01382         missile->count = count;
01383 
01384         missile->classname = "demp2_alt_proj";
01385         missile->s.weapon = WP_DEMP2;
01386 
01387         missile->think = DEMP2_AltDetonate;
01388         missile->nextthink = level.time;
01389 
01390         missile->splashDamage = missile->damage = damage;
01391         missile->splashMethodOfDeath = missile->methodOfDeath = MOD_DEMP2;
01392         missile->splashRadius = DEMP2_ALT_SPLASHRADIUS;
01393 
01394         missile->r.ownerNum = ent->s.number;
01395 
01396         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01397         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01398 
01399         // we don't want it to ever bounce
01400         missile->bounceCount = 0;
01401 }
01402 
01403 //---------------------------------------------------------
01404 static void WP_FireDEMP2( gentity_t *ent, qboolean altFire )
01405 //---------------------------------------------------------
01406 {
01407         if ( altFire )
01408         {
01409                 WP_DEMP2_AltFire( ent );
01410         }
01411         else
01412         {
01413                 WP_DEMP2_MainFire( ent );
01414         }
01415 }
01416 
01417 
01418 
01419 /*
01420 ======================================================================
01421 
01422 FLECHETTE
01423 
01424 ======================================================================
01425 */
01426 
01427 //---------------------------------------------------------
01428 static void WP_FlechetteMainFire( gentity_t *ent )
01429 //---------------------------------------------------------
01430 {
01431         vec3_t          fwd, angs;
01432         gentity_t       *missile;
01433         int i;
01434 
01435         for (i = 0; i < FLECHETTE_SHOTS; i++ )
01436         {
01437                 vectoangles( forward, angs );
01438 
01439                 if (i != 0)
01440                 { //do nothing on the first shot, it will hit the crosshairs
01441                         angs[PITCH] += crandom() * FLECHETTE_SPREAD;
01442                         angs[YAW]       += crandom() * FLECHETTE_SPREAD;
01443                 }
01444 
01445                 AngleVectors( angs, fwd, NULL, NULL );
01446 
01447                 missile = CreateMissile( muzzle, fwd, FLECHETTE_VEL, 10000, ent, qfalse);
01448 
01449                 missile->classname = "flech_proj";
01450                 missile->s.weapon = WP_FLECHETTE;
01451 
01452                 VectorSet( missile->r.maxs, FLECHETTE_SIZE, FLECHETTE_SIZE, FLECHETTE_SIZE );
01453                 VectorScale( missile->r.maxs, -1, missile->r.mins );
01454 
01455                 missile->damage = FLECHETTE_DAMAGE;
01456                 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01457                 missile->methodOfDeath = MOD_FLECHETTE;
01458                 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
01459 
01460                 // we don't want it to bounce forever
01461                 missile->bounceCount = Q_irand(5,8);
01462 
01463                 missile->flags |= FL_BOUNCE_SHRAPNEL;
01464         }
01465 }
01466 
01467 //---------------------------------------------------------
01468 void prox_mine_think( gentity_t *ent )
01469 //---------------------------------------------------------
01470 {
01471         int                     count, i;
01472         qboolean        blow = qfalse;
01473 
01474         // if it isn't time to auto-explode, do a small proximity check
01475         if ( ent->delay > level.time )
01476         {
01477                 count = G_RadiusList( ent->r.currentOrigin, FLECHETTE_MINE_RADIUS_CHECK, ent, qtrue, ent_list );
01478 
01479                 for ( i = 0; i < count; i++ )
01480                 {
01481                         if ( ent_list[i]->client && ent_list[i]->health > 0 && ent->activator && ent_list[i]->s.number != ent->activator->s.number )
01482                         {
01483                                 blow = qtrue;
01484                                 break;
01485                         }
01486                 }
01487         }
01488         else
01489         {
01490                 // well, we must die now
01491                 blow = qtrue;
01492         }
01493 
01494         if ( blow )
01495         {
01496                 ent->think = laserTrapExplode;
01497                 ent->nextthink = level.time + 200;
01498         }
01499         else
01500         {
01501                 // we probably don't need to do this thinking logic very often...maybe this is fast enough?
01502                 ent->nextthink = level.time + 500;
01503         }
01504 }
01505 
01506 //-----------------------------------------------------------------------------
01507 static void WP_TraceSetStart( gentity_t *ent, vec3_t start, vec3_t mins, vec3_t maxs )
01508 //-----------------------------------------------------------------------------
01509 {
01510         //make sure our start point isn't on the other side of a wall
01511         trace_t tr;
01512         vec3_t  entMins;
01513         vec3_t  entMaxs;
01514 
01515         VectorAdd( ent->r.currentOrigin, ent->r.mins, entMins );
01516         VectorAdd( ent->r.currentOrigin, ent->r.maxs, entMaxs );
01517 
01518         if ( G_BoxInBounds( start, mins, maxs, entMins, entMaxs ) )
01519         {
01520                 return;
01521         }
01522 
01523         if ( !ent->client )
01524         {
01525                 return;
01526         }
01527 
01528         trap_Trace( &tr, ent->client->ps.origin, mins, maxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP );
01529 
01530         if ( tr.startsolid || tr.allsolid )
01531         {
01532                 return;
01533         }
01534 
01535         if ( tr.fraction < 1.0f )
01536         {
01537                 VectorCopy( tr.endpos, start );
01538         }
01539 }
01540 
01541 void WP_ExplosiveDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
01542 {
01543         laserTrapExplode(self);
01544 }
01545 
01546 //----------------------------------------------
01547 void WP_flechette_alt_blow( gentity_t *ent )
01548 //----------------------------------------------
01549 {
01550         ent->s.pos.trDelta[0] = 1;
01551         ent->s.pos.trDelta[1] = 0;
01552         ent->s.pos.trDelta[2] = 0;
01553 
01554         laserTrapExplode(ent);
01555 }
01556 
01557 //------------------------------------------------------------------------------
01558 static void WP_CreateFlechetteBouncyThing( vec3_t start, vec3_t fwd, gentity_t *self )
01559 //------------------------------------------------------------------------------
01560 {
01561         gentity_t       *missile = CreateMissile( start, fwd, 700 + random() * 700, 1500 + random() * 2000, self, qtrue );
01562         
01563         missile->think = WP_flechette_alt_blow;
01564 
01565         missile->activator = self;
01566 
01567         missile->s.weapon = WP_FLECHETTE;
01568         missile->classname = "flech_alt";
01569         missile->mass = 4;
01570 
01571         // How 'bout we give this thing a size...
01572         VectorSet( missile->r.mins, -3.0f, -3.0f, -3.0f );
01573         VectorSet( missile->r.maxs, 3.0f, 3.0f, 3.0f );
01574         missile->clipmask = MASK_SHOT;
01575 
01576         missile->touch = touch_NULL;
01577 
01578         // normal ones bounce, alt ones explode on impact
01579         missile->s.pos.trType = TR_GRAVITY;
01580 
01581         missile->flags |= FL_BOUNCE_HALF;
01582         missile->s.eFlags |= EF_ALT_FIRING;
01583 
01584         missile->bounceCount = 50;
01585 
01586         missile->damage = FLECHETTE_ALT_DAMAGE;
01587         missile->dflags = 0;
01588         missile->splashDamage = FLECHETTE_ALT_SPLASH_DAM;
01589         missile->splashRadius = FLECHETTE_ALT_SPLASH_RAD;
01590 
01591         missile->r.svFlags = SVF_USE_CURRENT_ORIGIN;
01592 
01593         missile->methodOfDeath = MOD_FLECHETTE_ALT_SPLASH;
01594         missile->splashMethodOfDeath = MOD_FLECHETTE_ALT_SPLASH;
01595 
01596         VectorCopy( start, missile->pos2 );
01597 }
01598 
01599 //---------------------------------------------------------
01600 static void WP_FlechetteAltFire( gentity_t *self )
01601 //---------------------------------------------------------
01602 {
01603         vec3_t  dir, fwd, start, angs;
01604         int i;
01605 
01606         vectoangles( forward, angs );
01607         VectorCopy( muzzle, start );
01608 
01609         WP_TraceSetStart( self, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
01610 
01611         for ( i = 0; i < 2; i++ )
01612         {
01613                 VectorCopy( angs, dir );
01614 
01615                 dir[PITCH] -= random() * 4 + 8; // make it fly upwards
01616                 dir[YAW] += crandom() * 2;
01617                 AngleVectors( dir, fwd, NULL, NULL );
01618 
01619                 WP_CreateFlechetteBouncyThing( start, fwd, self );
01620         }
01621 }
01622 
01623 //---------------------------------------------------------
01624 static void WP_FireFlechette( gentity_t *ent, qboolean altFire )
01625 //---------------------------------------------------------
01626 {
01627         if ( altFire )
01628         {
01629                 //WP_FlechetteProxMine( ent );
01630                 WP_FlechetteAltFire(ent);
01631         }
01632         else
01633         {
01634                 WP_FlechetteMainFire( ent );
01635         }
01636 }
01637 
01638 
01639 
01640 /*
01641 ======================================================================
01642 
01643 ROCKET LAUNCHER
01644 
01645 ======================================================================
01646 */
01647 
01648 //---------------------------------------------------------
01649 void rocketThink( gentity_t *ent )
01650 //---------------------------------------------------------
01651 {
01652         vec3_t newdir, targetdir, 
01653                         up={0,0,1}, right; 
01654         vec3_t  org;
01655         float dot, dot2, dis;
01656         int i;
01657         float vel = (ent->spawnflags&1)?ent->speed:ROCKET_VELOCITY;
01658 
01659         if ( ent->genericValue1 && ent->genericValue1 < level.time )
01660         {//time's up, we're done, remove us
01661                 if ( ent->genericValue2 )
01662                 {//explode when die
01663                         RocketDie( ent, &g_entities[ent->r.ownerNum], &g_entities[ent->r.ownerNum], 0, MOD_UNKNOWN );
01664                 }
01665                 else
01666                 {//just remove when die
01667                         G_FreeEntity( ent );
01668                 }
01669                 return;
01670         }
01671         if ( !ent->enemy 
01672                 || !ent->enemy->client 
01673                 || ent->enemy->health <= 0 
01674                 || ent->enemy->client->ps.powerups[PW_CLOAKED] )
01675         {//no enemy or enemy not a client or enemy dead or enemy cloaked
01676                 if ( !ent->genericValue1  )
01677                 {//doesn't have its own self-kill time
01678                         ent->nextthink = level.time + 10000;
01679                         ent->think = G_FreeEntity;
01680                 }
01681                 return;
01682         }
01683 
01684         if ( (ent->spawnflags&1) )
01685         {//vehicle rocket
01686                 if ( ent->enemy->client && ent->enemy->client->NPC_class == CLASS_VEHICLE )
01687                 {//tracking another vehicle
01688                         if ( ent->enemy->client->ps.speed+4000 > vel )
01689                         {
01690                                 vel = ent->enemy->client->ps.speed+4000;
01691                         }
01692                 }
01693         }
01694 
01695         if ( ent->enemy && ent->enemy->inuse )
01696         {       
01697                 float newDirMult = ent->angle?ent->angle*2.0f:1.0f;
01698                 float oldDirMult = ent->angle?(1.0f-ent->angle)*2.0f:1.0f;
01699 
01700                 VectorCopy( ent->enemy->r.currentOrigin, org );
01701                 org[2] += (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5f;
01702 
01703                 VectorSubtract( org, ent->r.currentOrigin, targetdir );
01704                 VectorNormalize( targetdir );
01705 
01706                 // Now the rocket can't do a 180 in space, so we'll limit the turn to about 45 degrees.
01707                 dot = DotProduct( targetdir, ent->movedir );
01708                 if ( (ent->spawnflags&1) )
01709                 {//vehicle rocket
01710                         if ( ent->radius > -1.0f )
01711                         {//can lose the lock if DotProduct drops below this number
01712                                 if ( dot < ent->radius )
01713                                 {//lost the lock!!!
01714                                         //HMM... maybe can re-lock on if they come in front again?
01715                                         /*
01716                                         //OR: should it stop trying to lock altogether?
01717                                         if ( ent->genericValue1 )
01718                                         {//have a timelimit, set next think to that
01719                                                 ent->nextthink = ent->genericValue1;
01720                                                 if ( ent->genericValue2 )
01721                                                 {//explode when die
01722                                                         ent->think = G_ExplodeMissile;
01723                                                 }
01724                                                 else
01725                                                 {
01726                                                         ent->think = G_FreeEntity;
01727                                                 }
01728                                         }
01729                                         else
01730                                         {
01731                                                 ent->think = NULL;
01732                                                 ent->nextthink = -1;
01733                                         }
01734                                         */
01735                                         return;
01736                                 }
01737                         }
01738                 }
01739 
01740 
01741                 // a dot of 1.0 means right-on-target.
01742                 if ( dot < 0.0f )
01743                 {       
01744                         // Go in the direction opposite, start a 180.
01745                         CrossProduct( ent->movedir, up, right );
01746                         dot2 = DotProduct( targetdir, right );
01747 
01748                         if ( dot2 > 0 )
01749                         {       
01750                                 // Turn 45 degrees right.
01751                                 VectorMA( ent->movedir, 0.4f*newDirMult, right, newdir );
01752                         }
01753                         else
01754                         {       
01755                                 // Turn 45 degrees left.
01756                                 VectorMA( ent->movedir, -0.4f*newDirMult, right, newdir );
01757                         }
01758 
01759                         // Yeah we've adjusted horizontally, but let's split the difference vertically, so we kinda try to move towards it.
01760                         newdir[2] = ( (targetdir[2]*newDirMult) + (ent->movedir[2]*oldDirMult) ) * 0.5;
01761 
01762                         // let's also slow down a lot
01763                         vel *= 0.5f;
01764                 }
01765                 else if ( dot < 0.70f )
01766                 {       
01767                         // Still a bit off, so we turn a bit softer
01768                         VectorMA( ent->movedir, 0.5f*newDirMult, targetdir, newdir );
01769                 }
01770                 else
01771                 {       
01772                         // getting close, so turn a bit harder
01773                         VectorMA( ent->movedir, 0.9f*newDirMult, targetdir, newdir );
01774                 }
01775 
01776                 // add crazy drunkenness
01777                 for (i = 0; i < 3; i++ )
01778                 {
01779                         newdir[i] += crandom() * ent->random * 0.25f;
01780                 }
01781 
01782                 // decay the randomness
01783                 ent->random *= 0.9f;
01784 
01785                 if ( ent->enemy->client
01786                         && ent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE )
01787                 {//tracking a client who's on the ground, aim at the floor...?
01788                         // Try to crash into the ground if we get close enough to do splash damage
01789                         dis = Distance( ent->r.currentOrigin, org );
01790 
01791                         if ( dis < 128 )
01792                         {
01793                                 // the closer we get, the more we push the rocket down, heh heh.
01794                                 newdir[2] -= (1.0f - (dis / 128.0f)) * 0.6f;
01795                         }
01796                 }
01797 
01798                 VectorNormalize( newdir );
01799 
01800                 VectorScale( newdir, vel * 0.5f, ent->s.pos.trDelta );
01801                 VectorCopy( newdir, ent->movedir );
01802                 SnapVector( ent->s.pos.trDelta );                       // save net bandwidth
01803                 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
01804                 ent->s.pos.trTime = level.time;
01805         }
01806 
01807         ent->nextthink = level.time + ROCKET_ALT_THINK_TIME;    // Nothing at all spectacular happened, continue.
01808         return;
01809 }
01810 
01811 extern void G_ExplodeMissile( gentity_t *ent );
01812 void RocketDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
01813 {
01814         self->die = 0;
01815         self->r.contents = 0;
01816 
01817         G_ExplodeMissile( self );
01818 
01819         self->think = G_FreeEntity;
01820         self->nextthink = level.time;
01821 }
01822 
01823 //---------------------------------------------------------
01824 static void WP_FireRocket( gentity_t *ent, qboolean altFire )
01825 //---------------------------------------------------------
01826 {
01827         int     damage  = ROCKET_DAMAGE;
01828         int     vel = ROCKET_VELOCITY;
01829         int dif = 0;
01830         float rTime;
01831         gentity_t *missile;
01832 
01833         if ( altFire )
01834         {
01835                 vel *= 0.5f;
01836         }
01837 
01838         missile = CreateMissile( muzzle, forward, vel, 10000, ent, altFire );
01839 
01840         if (ent->client && ent->client->ps.rocketLockIndex != ENTITYNUM_NONE)
01841         {
01842                 float lockTimeInterval = ((g_gametype.integer==GT_SIEGE)?2400.0f:1200.0f)/16.0f;
01843                 rTime = ent->client->ps.rocketLockTime;
01844 
01845                 if (rTime == -1)
01846                 {
01847                         rTime = ent->client->ps.rocketLastValidTime;
01848                 }
01849                 dif = ( level.time - rTime ) / lockTimeInterval;
01850 
01851                 if (dif < 0)
01852                 {
01853                         dif = 0;
01854                 }
01855 
01856                 //It's 10 even though it locks client-side at 8, because we want them to have a sturdy lock first, and because there's a slight difference in time between server and client
01857                 if ( dif >= 10 && rTime != -1 )
01858                 {
01859                         missile->enemy = &g_entities[ent->client->ps.rocketLockIndex];
01860 
01861                         if (missile->enemy && missile->enemy->client && missile->enemy->health > 0 && !OnSameTeam(ent, missile->enemy))
01862                         { //if enemy became invalid, died, or is on the same team, then don't seek it
01863                                 missile->angle = 0.5f;
01864                                 missile->think = rocketThink;
01865                                 missile->nextthink = level.time + ROCKET_ALT_THINK_TIME;
01866                         }
01867                 }
01868 
01869                 ent->client->ps.rocketLockIndex = ENTITYNUM_NONE;
01870                 ent->client->ps.rocketLockTime = 0;
01871                 ent->client->ps.rocketTargetTime = 0;
01872         }
01873 
01874         missile->classname = "rocket_proj";
01875         missile->s.weapon = WP_ROCKET_LAUNCHER;
01876 
01877         // Make it easier to hit things
01878         VectorSet( missile->r.maxs, ROCKET_SIZE, ROCKET_SIZE, ROCKET_SIZE );
01879         VectorScale( missile->r.maxs, -1, missile->r.mins );
01880 
01881         missile->damage = damage;
01882         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01883         if (altFire)
01884         {
01885                 missile->methodOfDeath = MOD_ROCKET_HOMING;
01886                 missile->splashMethodOfDeath = MOD_ROCKET_HOMING_SPLASH;
01887         }
01888         else
01889         {
01890                 missile->methodOfDeath = MOD_ROCKET;
01891                 missile->splashMethodOfDeath = MOD_ROCKET_SPLASH;
01892         }
01893 //===testing being able to shoot rockets out of the air==================================
01894         missile->health = 10;
01895         missile->takedamage = qtrue;
01896         missile->r.contents = MASK_SHOT;
01897         missile->die = RocketDie;
01898 //===testing being able to shoot rockets out of the air==================================
01899         
01900         missile->clipmask = MASK_SHOT;
01901         missile->splashDamage = ROCKET_SPLASH_DAMAGE;
01902         missile->splashRadius = ROCKET_SPLASH_RADIUS;
01903 
01904         // we don't want it to ever bounce
01905         missile->bounceCount = 0;
01906 }
01907 
01908 /*
01909 ======================================================================
01910 
01911 THERMAL DETONATOR
01912 
01913 ======================================================================
01914 */
01915 
01916 #define TD_DAMAGE                       70 //only do 70 on a direct impact
01917 #define TD_SPLASH_RAD           128
01918 #define TD_SPLASH_DAM           90
01919 #define TD_VELOCITY                     900
01920 #define TD_MIN_CHARGE           0.15f
01921 #define TD_TIME                         3000//6000
01922 #define TD_ALT_TIME                     3000
01923 
01924 #define TD_ALT_DAMAGE           60//100
01925 #define TD_ALT_SPLASH_RAD       128
01926 #define TD_ALT_SPLASH_DAM       50//90
01927 #define TD_ALT_VELOCITY         600
01928 #define TD_ALT_MIN_CHARGE       0.15f
01929 #define TD_ALT_TIME                     3000
01930 
01931 void thermalThinkStandard(gentity_t *ent);
01932 
01933 //---------------------------------------------------------
01934 void thermalDetonatorExplode( gentity_t *ent )
01935 //---------------------------------------------------------
01936 {
01937         if ( !ent->count )
01938         {
01939                 G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/thermal/warning.wav" ) );
01940                 ent->count = 1;
01941                 ent->genericValue5 = level.time + 500;
01942                 ent->think = thermalThinkStandard;
01943                 ent->nextthink = level.time;
01944                 ent->r.svFlags |= SVF_BROADCAST;//so everyone hears/sees the explosion?
01945         }
01946         else
01947         {
01948                 vec3_t  origin;
01949                 vec3_t  dir={0,0,1};
01950 
01951                 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
01952                 origin[2] += 8;
01953                 SnapVector( origin );
01954                 G_SetOrigin( ent, origin );
01955 
01956                 ent->s.eType = ET_GENERAL;
01957                 G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
01958                 ent->freeAfterEvent = qtrue;
01959 
01960                 if (G_RadiusDamage( ent->r.currentOrigin, ent->parent,  ent->splashDamage, ent->splashRadius, 
01961                                 ent, ent, ent->splashMethodOfDeath))
01962                 {
01963                         g_entities[ent->r.ownerNum].client->accuracy_hits++;
01964                 }
01965 
01966                 trap_LinkEntity( ent );
01967         }
01968 }
01969 
01970 void thermalThinkStandard(gentity_t *ent)
01971 {
01972         if (ent->genericValue5 < level.time)
01973         {
01974                 ent->think = thermalDetonatorExplode;
01975                 ent->nextthink = level.time;
01976                 return;
01977         }
01978 
01979         G_RunObject(ent);
01980         ent->nextthink = level.time;
01981 }
01982 
01983 //---------------------------------------------------------
01984 gentity_t *WP_FireThermalDetonator( gentity_t *ent, qboolean altFire )
01985 //---------------------------------------------------------
01986 {
01987         gentity_t       *bolt;
01988         vec3_t          dir, start;
01989         float chargeAmount = 1.0f; // default of full charge
01990         
01991         VectorCopy( forward, dir );
01992         VectorCopy( muzzle, start );
01993 
01994         bolt = G_Spawn();
01995         
01996         bolt->physicsObject = qtrue;
01997 
01998         bolt->classname = "thermal_detonator";
01999         bolt->think = thermalThinkStandard;
02000         bolt->nextthink = level.time;
02001         bolt->touch = touch_NULL;
02002 
02003         // How 'bout we give this thing a size...
02004         VectorSet( bolt->r.mins, -3.0f, -3.0f, -3.0f );
02005         VectorSet( bolt->r.maxs, 3.0f, 3.0f, 3.0f );
02006         bolt->clipmask = MASK_SHOT;
02007 
02008         W_TraceSetStart( ent, start, bolt->r.mins, bolt->r.maxs );//make sure our start point isn't on the other side of a wall
02009 
02010         if ( ent->client )
02011         {
02012                 chargeAmount = level.time - ent->client->ps.weaponChargeTime;
02013         }
02014 
02015         // get charge amount
02016         chargeAmount = chargeAmount / (float)TD_VELOCITY;
02017 
02018         if ( chargeAmount > 1.0f )
02019         {
02020                 chargeAmount = 1.0f;
02021         }
02022         else if ( chargeAmount < TD_MIN_CHARGE )
02023         {
02024                 chargeAmount = TD_MIN_CHARGE;
02025         }
02026 
02027         // normal ones bounce, alt ones explode on impact
02028         bolt->genericValue5 = level.time + TD_TIME; // How long 'til she blows
02029         bolt->s.pos.trType = TR_GRAVITY;
02030         bolt->parent = ent;
02031         bolt->r.ownerNum = ent->s.number;
02032         VectorScale( dir, TD_VELOCITY * chargeAmount, bolt->s.pos.trDelta );
02033 
02034         if ( ent->health >= 0 )
02035         {
02036                 bolt->s.pos.trDelta[2] += 120;
02037         }
02038 
02039         if ( !altFire )
02040         {
02041                 bolt->flags |= FL_BOUNCE_HALF;
02042         }
02043 
02044         bolt->s.loopSound = G_SoundIndex( "sound/weapons/thermal/thermloop.wav" );
02045         bolt->s.loopIsSoundset = qfalse;
02046 
02047         bolt->damage = TD_DAMAGE;
02048         bolt->dflags = 0;
02049         bolt->splashDamage = TD_SPLASH_DAM;
02050         bolt->splashRadius = TD_SPLASH_RAD;
02051 
02052         bolt->s.eType = ET_MISSILE;
02053         bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
02054         bolt->s.weapon = WP_THERMAL;
02055 
02056         bolt->methodOfDeath = MOD_THERMAL;
02057         bolt->splashMethodOfDeath = MOD_THERMAL_SPLASH;
02058 
02059         bolt->s.pos.trTime = level.time;                // move a bit on the very first frame
02060         VectorCopy( start, bolt->s.pos.trBase );
02061         
02062         SnapVector( bolt->s.pos.trDelta );                      // save net bandwidth
02063         VectorCopy (start, bolt->r.currentOrigin);
02064 
02065         VectorCopy( start, bolt->pos2 );
02066 
02067         bolt->bounceCount = -5;
02068 
02069         return bolt;
02070 }
02071 
02072 gentity_t *WP_DropThermal( gentity_t *ent )
02073 {
02074         AngleVectors( ent->client->ps.viewangles, forward, vright, up );
02075         return (WP_FireThermalDetonator( ent, qfalse ));
02076 }
02077 
02078 
02079 //---------------------------------------------------------
02080 qboolean WP_LobFire( gentity_t *self, vec3_t start, vec3_t target, vec3_t mins, vec3_t maxs, int clipmask, 
02081                                 vec3_t velocity, qboolean tracePath, int ignoreEntNum, int enemyNum,
02082                                 float minSpeed, float maxSpeed, float idealSpeed, qboolean mustHit )
02083 //---------------------------------------------------------
02084 { //for the galak mech NPC
02085         float   targetDist, shotSpeed, speedInc = 100, travelTime, impactDist, bestImpactDist = Q3_INFINITE;//fireSpeed, 
02086         vec3_t  targetDir, shotVel, failCase; 
02087         trace_t trace;
02088         trajectory_t    tr;
02089         qboolean        blocked;
02090         int             elapsedTime, skipNum, timeStep = 500, hitCount = 0, maxHits = 7;
02091         vec3_t  lastPos, testPos;
02092         gentity_t       *traceEnt;
02093         
02094         if ( !idealSpeed )
02095         {
02096                 idealSpeed = 300;
02097         }
02098         else if ( idealSpeed < speedInc )
02099         {
02100                 idealSpeed = speedInc;
02101         }
02102         shotSpeed = idealSpeed;
02103         skipNum = (idealSpeed-speedInc)/speedInc;
02104         if ( !minSpeed )
02105         {
02106                 minSpeed = 100;
02107         }
02108         if ( !maxSpeed )
02109         {
02110                 maxSpeed = 900;
02111         }
02112         while ( hitCount < maxHits )
02113         {
02114                 VectorSubtract( target, start, targetDir );
02115                 targetDist = VectorNormalize( targetDir );
02116 
02117                 VectorScale( targetDir, shotSpeed, shotVel );
02118                 travelTime = targetDist/shotSpeed;
02119                 shotVel[2] += travelTime * 0.5 * g_gravity.value;
02120 
02121                 if ( !hitCount )                
02122                 {//save the first (ideal) one as the failCase (fallback value)
02123                         if ( !mustHit )
02124                         {//default is fine as a return value
02125                                 VectorCopy( shotVel, failCase );
02126                         }
02127                 }
02128 
02129                 if ( tracePath )
02130                 {//do a rough trace of the path
02131                         blocked = qfalse;
02132 
02133                         VectorCopy( start, tr.trBase );
02134                         VectorCopy( shotVel, tr.trDelta );
02135                         tr.trType = TR_GRAVITY;
02136                         tr.trTime = level.time;
02137                         travelTime *= 1000.0f;
02138                         VectorCopy( start, lastPos );
02139                         
02140                         //This may be kind of wasteful, especially on long throws... use larger steps?  Divide the travelTime into a certain hard number of slices?  Trace just to apex and down?
02141                         for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep )
02142                         {
02143                                 if ( (float)elapsedTime > travelTime )
02144                                 {//cap it
02145                                         elapsedTime = floor( travelTime );
02146                                 }
02147                                 BG_EvaluateTrajectory( &tr, level.time + elapsedTime, testPos );
02148                                 trap_Trace( &trace, lastPos, mins, maxs, testPos, ignoreEntNum, clipmask );
02149 
02150                                 if ( trace.allsolid || trace.startsolid )
02151                                 {
02152                                         blocked = qtrue;
02153                                         break;
02154                                 }
02155                                 if ( trace.fraction < 1.0f )
02156                                 {//hit something
02157                                         if ( trace.entityNum == enemyNum )
02158                                         {//hit the enemy, that's perfect!
02159                                                 break;
02160                                         }
02161                                         else if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, target ) < 4096 )//hit within 64 of desired location, should be okay
02162                                         {//close enough!
02163                                                 break;
02164                                         }
02165                                         else
02166                                         {//FIXME: maybe find the extents of this brush and go above or below it on next try somehow?
02167                                                 impactDist = DistanceSquared( trace.endpos, target );
02168                                                 if ( impactDist < bestImpactDist )
02169                                                 {
02170                                                         bestImpactDist = impactDist;
02171                                                         VectorCopy( shotVel, failCase );
02172                                                 }
02173                                                 blocked = qtrue;
02174                                                 //see if we should store this as the failCase
02175                                                 if ( trace.entityNum < ENTITYNUM_WORLD )
02176                                                 {//hit an ent
02177                                                         traceEnt = &g_entities[trace.entityNum];
02178                                                         if ( traceEnt && traceEnt->takedamage && !OnSameTeam( self, traceEnt ) )
02179                                                         {//hit something breakable, so that's okay
02180                                                                 //we haven't found a clear shot yet so use this as the failcase
02181                                                                 VectorCopy( shotVel, failCase );
02182                                                         }
02183                                                 }
02184                                                 break;
02185                                         }
02186                                 }
02187                                 if ( elapsedTime == floor( travelTime ) )
02188                                 {//reached end, all clear
02189                                         break;
02190                                 }
02191                                 else
02192                                 {
02193                                         //all clear, try next slice
02194                                         VectorCopy( testPos, lastPos );
02195                                 }
02196                         }
02197                         if ( blocked )
02198                         {//hit something, adjust speed (which will change arc)
02199                                 hitCount++;
02200                                 shotSpeed = idealSpeed + ((hitCount-skipNum) * speedInc);//from min to max (skipping ideal)
02201                                 if ( hitCount >= skipNum )
02202                                 {//skip ideal since that was the first value we tested
02203                                         shotSpeed += speedInc;
02204                                 }
02205                         }
02206                         else
02207                         {//made it!
02208                                 break;
02209                         }
02210                 }
02211                 else
02212                 {//no need to check the path, go with first calc
02213                         break;
02214                 }
02215         }
02216 
02217         if ( hitCount >= maxHits )
02218         {//NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?)
02219                 VectorCopy( failCase, velocity );
02220                 return qfalse;
02221         }
02222         VectorCopy( shotVel, velocity );
02223         return qtrue;
02224 }
02225 
02226 /*
02227 ======================================================================
02228 
02229 LASER TRAP / TRIP MINE
02230 
02231 ======================================================================
02232 */
02233 #define LT_DAMAGE                       100
02234 #define LT_SPLASH_RAD           256.0f
02235 #define LT_SPLASH_DAM           105
02236 #define LT_VELOCITY                     900.0f
02237 #define LT_SIZE                         1.5f
02238 #define LT_ALT_TIME                     2000
02239 #define LT_ACTIVATION_DELAY     1000
02240 #define LT_DELAY_TIME           50
02241 
02242 void laserTrapExplode( gentity_t *self )
02243 {
02244         vec3_t v;
02245         self->takedamage = qfalse;
02246 
02247         if (self->activator)
02248         {
02249                 G_RadiusDamage( self->r.currentOrigin, self->activator, self->splashDamage, self->splashRadius, self, self, MOD_TRIP_MINE_SPLASH/*MOD_LT_SPLASH*/ );
02250         }
02251 
02252         if (self->s.weapon != WP_FLECHETTE)
02253         {
02254                 G_AddEvent( self, EV_MISSILE_MISS, 0);
02255         }
02256 
02257         VectorCopy(self->s.pos.trDelta, v);
02258         //Explode outward from the surface
02259 
02260         if (self->s.time == -2)
02261         {
02262                 v[0] = 0;
02263                 v[1] = 0;
02264                 v[2] = 0;
02265         }
02266 
02267         if (self->s.weapon == WP_FLECHETTE)
02268         {
02269                 G_PlayEffect(EFFECT_EXPLOSION_FLECHETTE, self->r.currentOrigin, v);
02270         }
02271         else
02272         {
02273                 G_PlayEffect(EFFECT_EXPLOSION_TRIPMINE, self->r.currentOrigin, v);
02274         }
02275 
02276         self->think = G_FreeEntity;
02277         self->nextthink = level.time;
02278 }
02279 
02280 void laserTrapDelayedExplode( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
02281 {
02282         self->enemy = attacker;
02283         self->think = laserTrapExplode;
02284         self->nextthink = level.time + FRAMETIME;
02285         self->takedamage = qfalse;
02286         if ( attacker && !attacker->s.number )
02287         {
02288                 //less damage when shot by player
02289                 self->splashDamage /= 3;
02290                 self->splashRadius /= 3;
02291         }
02292 }
02293 
02294 void touchLaserTrap( gentity_t *ent, gentity_t *other, trace_t *trace )
02295 {
02296         if (other && other->s.number < ENTITYNUM_WORLD)
02297         { //just explode if we hit any entity. This way we don't have things happening like tripmines floating
02298           //in the air after getting stuck to a moving door
02299                 if ( ent->activator != other )
02300                 {
02301                         ent->touch = 0;
02302                         ent->nextthink = level.time + FRAMETIME;
02303                         ent->think = laserTrapExplode;
02304                         VectorCopy(trace->plane.normal, ent->s.pos.trDelta);
02305                 }
02306         }
02307         else
02308         {
02309                 ent->touch = 0;
02310                 if (trace->entityNum != ENTITYNUM_NONE)
02311                 {
02312                         ent->enemy = &g_entities[trace->entityNum];
02313                 }
02314                 laserTrapStick(ent, trace->endpos, trace->plane.normal);
02315         }
02316 }
02317 
02318 void proxMineThink(gentity_t *ent)
02319 {
02320         int i = 0;
02321         gentity_t *cl;
02322         gentity_t *owner = NULL;
02323 
02324         if (ent->r.ownerNum < ENTITYNUM_WORLD)
02325         {
02326                 owner = &g_entities[ent->r.ownerNum];
02327         }
02328 
02329         ent->nextthink = level.time;
02330 
02331         if (ent->genericValue15 < level.time ||
02332                 !owner ||
02333                 !owner->inuse ||
02334                 !owner->client ||
02335                 owner->client->pers.connected != CON_CONNECTED)
02336         { //time to die!
02337                 ent->think = laserTrapExplode;
02338                 return;
02339         }
02340 
02341         while (i < MAX_CLIENTS)
02342         { //eh, just check for clients, don't care about anyone else...
02343                 cl = &g_entities[i];
02344 
02345                 if (cl->inuse && cl->client && cl->client->pers.connected == CON_CONNECTED &&
02346                         owner != cl && cl->client->sess.sessionTeam != TEAM_SPECTATOR &&
02347                         cl->client->tempSpectate < level.time && cl->health > 0)
02348                 {
02349                         if (!OnSameTeam(owner, cl) || g_friendlyFire.integer)
02350                         { //not on the same team, or friendly fire is enabled
02351                                 vec3_t v;
02352 
02353                                 VectorSubtract(ent->r.currentOrigin, cl->client->ps.origin, v);
02354                                 if (VectorLength(v) < (ent->splashRadius/2.0f))
02355                                 {
02356                                         ent->think = laserTrapExplode;
02357                                         return;
02358                                 }
02359                         }
02360                 }
02361                 i++;
02362         }
02363 }
02364 
02365 void laserTrapThink ( gentity_t *ent )
02366 {
02367         gentity_t       *traceEnt;
02368         vec3_t          end;
02369         trace_t         tr;
02370 
02371         //just relink it every think
02372         trap_LinkEntity(ent);
02373 
02374         //turn on the beam effect
02375         if ( !(ent->s.eFlags&EF_FIRING) )
02376         {//arm me
02377                 G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/laser_trap/warning.wav" ) );
02378                 ent->s.eFlags |= EF_FIRING;
02379         }
02380         ent->think = laserTrapThink;
02381         ent->nextthink = level.time + FRAMETIME;
02382 
02383         // Find the main impact point
02384         VectorMA ( ent->s.pos.trBase, 1024, ent->movedir, end );
02385         trap_Trace ( &tr, ent->r.currentOrigin, NULL, NULL, end, ent->s.number, MASK_SHOT);
02386         
02387         traceEnt = &g_entities[ tr.entityNum ];
02388 
02389         ent->s.time = -1; //let all clients know to draw a beam from this guy
02390 
02391         if ( traceEnt->client || tr.startsolid )
02392         {
02393                 //go boom
02394                 ent->touch = 0;
02395                 ent->nextthink = level.time + LT_DELAY_TIME;
02396                 ent->think = laserTrapExplode;
02397         }
02398 }
02399 
02400 void laserTrapStick( gentity_t *ent, vec3_t endpos, vec3_t normal )
02401 {
02402         G_SetOrigin( ent, endpos );
02403         VectorCopy( normal, ent->pos1 );
02404 
02405         VectorClear( ent->s.apos.trDelta );
02406         // This will orient the object to face in the direction of the normal
02407         VectorCopy( normal, ent->s.pos.trDelta );
02408         //VectorScale( normal, -1, ent->s.pos.trDelta );
02409         ent->s.pos.trTime = level.time;
02410         
02411         
02412         //This does nothing, cg_missile makes assumptions about direction of travel controlling angles
02413         vectoangles( normal, ent->s.apos.trBase );
02414         VectorClear( ent->s.apos.trDelta );
02415         ent->s.apos.trType = TR_STATIONARY;
02416         VectorCopy( ent->s.apos.trBase, ent->s.angles );
02417         VectorCopy( ent->s.angles, ent->r.currentAngles );
02418         
02419 
02420         G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/laser_trap/stick.wav" ) );
02421         if ( ent->count )
02422         {//a tripwire
02423                 //add draw line flag
02424                 VectorCopy( normal, ent->movedir );
02425                 ent->think = laserTrapThink;
02426                 ent->nextthink = level.time + LT_ACTIVATION_DELAY;//delay the activation
02427                 ent->touch = touch_NULL;
02428                 //make it shootable
02429                 ent->takedamage = qtrue;
02430                 ent->health = 5;
02431                 ent->die = laserTrapDelayedExplode;
02432 
02433                 //shove the box through the wall
02434                 VectorSet( ent->r.mins, -LT_SIZE*2, -LT_SIZE*2, -LT_SIZE*2 );
02435                 VectorSet( ent->r.maxs, LT_SIZE*2, LT_SIZE*2, LT_SIZE*2 );
02436 
02437                 //so that the owner can blow it up with projectiles
02438                 ent->r.svFlags |= SVF_OWNERNOTSHARED;
02439         }
02440         else
02441         {
02442                 ent->touch = touchLaserTrap;
02443                 ent->think = proxMineThink;//laserTrapExplode;
02444                 ent->genericValue15 = level.time + 30000; //auto-explode after 30 seconds.
02445                 ent->nextthink = level.time + LT_ALT_TIME; // How long 'til she blows
02446 
02447                 //make it shootable
02448                 ent->takedamage = qtrue;
02449                 ent->health = 5;
02450                 ent->die = laserTrapDelayedExplode;
02451 
02452                 //shove the box through the wall
02453                 VectorSet( ent->r.mins, -LT_SIZE*2, -LT_SIZE*2, -LT_SIZE*2 );
02454                 VectorSet( ent->r.maxs, LT_SIZE*2, LT_SIZE*2, LT_SIZE*2 );
02455 
02456                 //so that the owner can blow it up with projectiles
02457                 ent->r.svFlags |= SVF_OWNERNOTSHARED;
02458 
02459                 if ( !(ent->s.eFlags&EF_FIRING) )
02460                 {//arm me
02461                         G_Sound( ent, CHAN_WEAPON, G_SoundIndex( "sound/weapons/laser_trap/warning.wav" ) );
02462                         ent->s.eFlags |= EF_FIRING;
02463                         ent->s.time = -1;
02464                         ent->s.bolt2 = 1;
02465                 }
02466         }
02467 }
02468 
02469 void TrapThink(gentity_t *ent)
02470 { //laser trap think
02471         ent->nextthink = level.time + 50;
02472         G_RunObject(ent);
02473 }
02474 
02475 void CreateLaserTrap( gentity_t *laserTrap, vec3_t start, gentity_t *owner )
02476 { //create a laser trap entity
02477         laserTrap->classname = "laserTrap";
02478         laserTrap->flags |= FL_BOUNCE_HALF;
02479         laserTrap->s.eFlags |= EF_MISSILE_STICK;
02480         laserTrap->splashDamage = LT_SPLASH_DAM;
02481         laserTrap->splashRadius = LT_SPLASH_RAD;
02482         laserTrap->damage = LT_DAMAGE;
02483         laserTrap->methodOfDeath = MOD_TRIP_MINE_SPLASH;
02484         laserTrap->splashMethodOfDeath = MOD_TRIP_MINE_SPLASH;
02485         laserTrap->s.eType = ET_GENERAL;
02486         laserTrap->r.svFlags = SVF_USE_CURRENT_ORIGIN;
02487         laserTrap->s.weapon = WP_TRIP_MINE;
02488         laserTrap->s.pos.trType = TR_GRAVITY;
02489         laserTrap->r.contents = MASK_SHOT;
02490         laserTrap->parent = owner;
02491         laserTrap->activator = owner;
02492         laserTrap->r.ownerNum = owner->s.number;
02493         VectorSet( laserTrap->r.mins, -LT_SIZE, -LT_SIZE, -LT_SIZE );
02494         VectorSet( laserTrap->r.maxs, LT_SIZE, LT_SIZE, LT_SIZE );
02495         laserTrap->clipmask = MASK_SHOT;
02496         laserTrap->s.solid = 2;
02497         laserTrap->s.modelindex = G_ModelIndex( "models/weapons2/laser_trap/laser_trap_w.glm" );
02498         laserTrap->s.modelGhoul2 = 1;
02499         laserTrap->s.g2radius = 40;
02500 
02501         laserTrap->s.genericenemyindex = owner->s.number+MAX_GENTITIES;
02502 
02503         laserTrap->health = 1;
02504 
02505         laserTrap->s.time = 0;
02506 
02507         laserTrap->s.pos.trTime = level.time;           // move a bit on the very first frame
02508         VectorCopy( start, laserTrap->s.pos.trBase );
02509         SnapVector( laserTrap->s.pos.trBase );                  // save net bandwidth
02510         
02511         SnapVector( laserTrap->s.pos.trDelta );                 // save net bandwidth
02512         VectorCopy (start, laserTrap->r.currentOrigin);
02513 
02514         laserTrap->s.apos.trType = TR_GRAVITY;
02515         laserTrap->s.apos.trTime = level.time;
02516         laserTrap->s.apos.trBase[YAW] = rand()%360;
02517         laserTrap->s.apos.trBase[PITCH] = rand()%360;
02518         laserTrap->s.apos.trBase[ROLL] = rand()%360;
02519 
02520         if (rand()%10 < 5)
02521         {
02522                 laserTrap->s.apos.trBase[YAW] = -laserTrap->s.apos.trBase[YAW];
02523         }
02524 
02525         VectorCopy( start, laserTrap->pos2 );
02526         laserTrap->touch = touchLaserTrap;
02527         laserTrap->think = TrapThink;
02528         laserTrap->nextthink = level.time + 50;
02529 }
02530 
02531 void WP_PlaceLaserTrap( gentity_t *ent, qboolean alt_fire )
02532 {
02533         gentity_t       *laserTrap;
02534         gentity_t       *found = NULL;
02535         vec3_t          dir, start;
02536         int                     trapcount = 0;
02537         int                     foundLaserTraps[MAX_GENTITIES];
02538         int                     trapcount_org;
02539         int                     lowestTimeStamp;
02540         int                     removeMe;
02541         int                     i;
02542 
02543         foundLaserTraps[0] = ENTITYNUM_NONE;
02544 
02545         VectorCopy( forward, dir );
02546         VectorCopy( muzzle, start );
02547 
02548         laserTrap = G_Spawn();
02549         
02550         //limit to 10 placed at any one time
02551         //see how many there are now
02552         while ( (found = G_Find( found, FOFS(classname), "laserTrap" )) != NULL )
02553         {
02554                 if ( found->parent != ent )
02555                 {
02556                         continue;
02557                 }
02558                 foundLaserTraps[trapcount++] = found->s.number;
02559         }
02560         //now remove first ones we find until there are only 9 left
02561         found = NULL;
02562         trapcount_org = trapcount;
02563         lowestTimeStamp = level.time;
02564         while ( trapcount > 9 )
02565         {
02566                 removeMe = -1;
02567                 for ( i = 0; i < trapcount_org; i++ )
02568                 {
02569                         if ( foundLaserTraps[i] == ENTITYNUM_NONE )
02570                         {
02571                                 continue;
02572                         }
02573                         found = &g_entities[foundLaserTraps[i]];
02574                         if ( laserTrap && found->setTime < lowestTimeStamp )
02575                         {
02576                                 removeMe = i;
02577                                 lowestTimeStamp = found->setTime;
02578                         }
02579                 }
02580                 if ( removeMe != -1 )
02581                 {
02582                         //remove it... or blow it?
02583                         if ( &g_entities[foundLaserTraps[removeMe]] == NULL )
02584                         {
02585                                 break;
02586                         }
02587                         else
02588                         {
02589                                 G_FreeEntity( &g_entities[foundLaserTraps[removeMe]] );
02590                         }
02591                         foundLaserTraps[removeMe] = ENTITYNUM_NONE;
02592                         trapcount--;
02593                 }
02594                 else
02595                 {
02596                         break;
02597                 }
02598         }
02599 
02600         //now make the new one
02601         CreateLaserTrap( laserTrap, start, ent );
02602 
02603         //set player-created-specific fields
02604         laserTrap->setTime = level.time;//remember when we placed it
02605 
02606         if (!alt_fire)
02607         {//tripwire
02608                 laserTrap->count = 1;
02609         }
02610 
02611         //move it
02612         laserTrap->s.pos.trType = TR_GRAVITY;
02613 
02614         if (alt_fire)
02615         {
02616                 VectorScale( dir, 512, laserTrap->s.pos.trDelta );
02617         }
02618         else
02619         {
02620                 VectorScale( dir, 256, laserTrap->s.pos.trDelta );
02621         }
02622 
02623         trap_LinkEntity(laserTrap);
02624 }
02625 
02626 
02627 /*
02628 ======================================================================
02629 
02630 DET PACK
02631 
02632 ======================================================================
02633 */
02634 void VectorNPos(vec3_t in, vec3_t out)
02635 {
02636         if (in[0] < 0) { out[0] = -in[0]; } else { out[0] = in[0]; }
02637         if (in[1] < 0) { out[1] = -in[1]; } else { out[1] = in[1]; }
02638         if (in[2] < 0) { out[2] = -in[2]; } else { out[2] = in[2]; }
02639 }
02640 
02641 void DetPackBlow(gentity_t *self);
02642 
02643 void charge_stick (gentity_t *self, gentity_t *other, trace_t *trace)
02644 {
02645         gentity_t       *tent;
02646 
02647         if ( other 
02648                 && (other->flags&FL_BBRUSH)
02649                 && other->s.pos.trType == TR_STATIONARY
02650                 && other->s.apos.trType == TR_STATIONARY )
02651         {//a perfectly still breakable brush, let us attach directly to it!
02652                 self->target_ent = other;//remember them when we blow up
02653         }
02654         else if ( other 
02655                 && other->s.number < ENTITYNUM_WORLD
02656                 && other->s.eType == ET_MOVER
02657                 && trace->plane.normal[2] > 0 )
02658         {//stick to it?
02659                 self->s.groundEntityNum = other->s.number;
02660         }
02661         else if (other && other->s.number < ENTITYNUM_WORLD &&
02662                 (other->client || !other->s.weapon))
02663         { //hit another entity that is not stickable, "bounce" off
02664                 vec3_t vNor, tN;
02665 
02666                 VectorCopy(trace->plane.normal, vNor);
02667                 VectorNormalize(vNor);
02668                 VectorNPos(self->s.pos.trDelta, tN);
02669                 self->s.pos.trDelta[0] += vNor[0]*(tN[0]*(((float)Q_irand(1, 10))*0.1));
02670                 self->s.pos.trDelta[1] += vNor[1]*(tN[1]*(((float)Q_irand(1, 10))*0.1));
02671                 self->s.pos.trDelta[2] += vNor[1]*(tN[2]*(((float)Q_irand(1, 10))*0.1));
02672 
02673                 vectoangles(vNor, self->s.angles);
02674                 vectoangles(vNor, self->s.apos.trBase);
02675                 self->touch = charge_stick;
02676                 return;
02677         }
02678         else if (other && other->s.number < ENTITYNUM_WORLD)
02679         { //hit an entity that we just want to explode on (probably another projectile or something)
02680                 vec3_t v;
02681 
02682                 self->touch = 0;
02683                 self->think = 0;
02684                 self->nextthink = 0;
02685 
02686                 self->takedamage = qfalse;
02687 
02688                 VectorClear(self->s.apos.trDelta);
02689                 self->s.apos.trType = TR_STATIONARY;
02690 
02691                 G_RadiusDamage( self->r.currentOrigin, self->parent, self->splashDamage, self->splashRadius, self, self, MOD_DET_PACK_SPLASH );
02692                 VectorCopy(trace->plane.normal, v);
02693                 VectorCopy(v, self->pos2);
02694                 self->count = -1;
02695                 G_PlayEffect(EFFECT_EXPLOSION_DETPACK, self->r.currentOrigin, v);
02696 
02697                 self->think = G_FreeEntity;
02698                 self->nextthink = level.time;
02699                 return;
02700         }
02701 
02702         //if we get here I guess we hit hte world so we can stick to it
02703 
02704         self->touch = 0;
02705         self->think = DetPackBlow;
02706         self->nextthink = level.time + 30000;
02707 
02708         VectorClear(self->s.apos.trDelta);
02709         self->s.apos.trType = TR_STATIONARY;
02710 
02711         self->s.pos.trType = TR_STATIONARY;
02712         VectorCopy( self->r.currentOrigin, self->s.origin );
02713         VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
02714         VectorClear( self->s.pos.trDelta );
02715 
02716         VectorClear( self->s.apos.trDelta );
02717 
02718         VectorNormalize(trace->plane.normal);
02719 
02720         vectoangles(trace->plane.normal, self->s.angles);
02721         VectorCopy(self->s.angles, self->r.currentAngles );
02722         VectorCopy(self->s.angles, self->s.apos.trBase);
02723 
02724         VectorCopy(trace->plane.normal, self->pos2);
02725         self->count = -1;
02726 
02727         G_Sound(self, CHAN_WEAPON, G_SoundIndex("sound/weapons/detpack/stick.wav"));
02728                 
02729         tent = G_TempEntity( self->r.currentOrigin, EV_MISSILE_MISS );
02730         tent->s.weapon = 0;
02731         tent->parent = self;
02732         tent->r.ownerNum = self->s.number;
02733 
02734         //so that the owner can blow it up with projectiles
02735         self->r.svFlags |= SVF_OWNERNOTSHARED;
02736 }
02737 
02738 void DetPackBlow(gentity_t *self)
02739 {
02740         vec3_t v;
02741 
02742         self->pain = 0;
02743         self->die = 0;
02744         self->takedamage = qfalse;
02745 
02746         if ( self->target_ent )
02747         {//we were attached to something, do *direct* damage to it!
02748                 G_Damage( self->target_ent, self, &g_entities[self->r.ownerNum], v, self->r.currentOrigin, self->damage, 0, MOD_DET_PACK_SPLASH );
02749         }
02750         G_RadiusDamage( self->r.currentOrigin, self->parent, self->splashDamage, self->splashRadius, self, self, MOD_DET_PACK_SPLASH );
02751         v[0] = 0;
02752         v[1] = 0;
02753         v[2] = 1;
02754 
02755         if (self->count == -1)
02756         {
02757                 VectorCopy(self->pos2, v);
02758         }
02759 
02760         G_PlayEffect(EFFECT_EXPLOSION_DETPACK, self->r.currentOrigin, v);
02761 
02762         self->think = G_FreeEntity;
02763         self->nextthink = level.time;
02764 }
02765 
02766 void DetPackPain(gentity_t *self, gentity_t *attacker, int damage)
02767 {
02768         self->think = DetPackBlow;
02769         self->nextthink = level.time + Q_irand(50, 100);
02770         self->takedamage = qfalse;
02771 }
02772 
02773 void DetPackDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
02774 {
02775         self->think = DetPackBlow;
02776         self->nextthink = level.time + Q_irand(50, 100);
02777         self->takedamage = qfalse;
02778 }
02779 
02780 void drop_charge (gentity_t *self, vec3_t start, vec3_t dir) 
02781 {
02782         gentity_t       *bolt;
02783 
02784         VectorNormalize (dir);
02785 
02786         bolt = G_Spawn();
02787         bolt->classname = "detpack";
02788         bolt->nextthink = level.time + FRAMETIME;
02789         bolt->think = G_RunObject;
02790         bolt->s.eType = ET_GENERAL;
02791         bolt->s.g2radius = 100;
02792         bolt->s.modelGhoul2 = 1;
02793         bolt->s.modelindex = G_ModelIndex("models/weapons2/detpack/det_pack_proj.glm");
02794 
02795         bolt->parent = self;
02796         bolt->r.ownerNum = self->s.number;
02797         bolt->damage = 100;
02798         bolt->splashDamage = 200;
02799         bolt->splashRadius = 200;
02800         bolt->methodOfDeath = MOD_DET_PACK_SPLASH;
02801         bolt->splashMethodOfDeath = MOD_DET_PACK_SPLASH;
02802         bolt->clipmask = MASK_SHOT;
02803         bolt->s.solid = 2;
02804         bolt->r.contents = MASK_SHOT;
02805         bolt->touch = charge_stick;
02806 
02807         bolt->physicsObject = qtrue;
02808 
02809         bolt->s.genericenemyindex = self->s.number+MAX_GENTITIES;
02810         //rww - so client prediction knows we own this and won't hit it
02811 
02812         VectorSet( bolt->r.mins, -2, -2, -2 );
02813         VectorSet( bolt->r.maxs, 2, 2, 2 );
02814 
02815         bolt->health = 1;
02816         bolt->takedamage = qtrue;
02817         bolt->pain = DetPackPain;
02818         bolt->die = DetPackDie;
02819 
02820         bolt->s.weapon = WP_DET_PACK;
02821 
02822         bolt->setTime = level.time;
02823 
02824         G_SetOrigin(bolt, start);
02825         bolt->s.pos.trType = TR_GRAVITY;
02826         VectorCopy( start, bolt->s.pos.trBase );
02827         VectorScale(dir, 300, bolt->s.pos.trDelta );
02828         bolt->s.pos.trTime = level.time;
02829 
02830         bolt->s.apos.trType = TR_GRAVITY;
02831         bolt->s.apos.trTime = level.time;
02832         bolt->s.apos.trBase[YAW] = rand()%360;
02833         bolt->s.apos.trBase[PITCH] = rand()%360;
02834         bolt->s.apos.trBase[ROLL] = rand()%360;
02835 
02836         if (rand()%10 < 5)
02837         {
02838                 bolt->s.apos.trBase[YAW] = -bolt->s.apos.trBase[YAW];
02839         }
02840 
02841         vectoangles(dir, bolt->s.angles);
02842         VectorCopy(bolt->s.angles, bolt->s.apos.trBase);
02843         VectorSet(bolt->s.apos.trDelta, 300, 0, 0 );
02844         bolt->s.apos.trTime = level.time;
02845 
02846         trap_LinkEntity(bolt);
02847 }
02848 
02849 void BlowDetpacks(gentity_t *ent)
02850 {
02851         gentity_t *found = NULL;
02852 
02853         if ( ent->client->ps.hasDetPackPlanted )
02854         {
02855                 while ( (found = G_Find( found, FOFS(classname), "detpack") ) != NULL )
02856                 {//loop through all ents and blow the crap out of them!
02857                         if ( found->parent == ent )
02858                         {
02859                                 VectorCopy( found->r.currentOrigin, found->s.origin );
02860                                 found->think = DetPackBlow;
02861                                 found->nextthink = level.time + 100 + random() * 200;
02862                                 G_Sound( found, CHAN_BODY, G_SoundIndex("sound/weapons/detpack/warning.wav") );
02863                         }
02864                 }
02865                 ent->client->ps.hasDetPackPlanted = qfalse;
02866         }
02867 }
02868 
02869 qboolean CheatsOn(void) 
02870 {
02871         if ( !g_cheats.integer )
02872         {
02873                 return qfalse;
02874         }
02875         return qtrue;
02876 }
02877 
02878 void WP_DropDetPack( gentity_t *ent, qboolean alt_fire )
02879 {
02880         gentity_t       *found = NULL;
02881         int                     trapcount = 0;
02882         int                     foundDetPacks[MAX_GENTITIES] = {ENTITYNUM_NONE};
02883         int                     trapcount_org;
02884         int                     lowestTimeStamp;
02885         int                     removeMe;
02886         int                     i;
02887 
02888         if ( !ent || !ent->client )
02889         {
02890                 return;
02891         }
02892 
02893         //limit to 10 placed at any one time
02894         //see how many there are now
02895         while ( (found = G_Find( found, FOFS(classname), "detpack" )) != NULL )
02896         {
02897                 if ( found->parent != ent )
02898                 {
02899                         continue;
02900                 }
02901                 foundDetPacks[trapcount++] = found->s.number;
02902         }
02903         //now remove first ones we find until there are only 9 left
02904         found = NULL;
02905         trapcount_org = trapcount;
02906         lowestTimeStamp = level.time;
02907         while ( trapcount > 9 )
02908         {
02909                 removeMe = -1;
02910                 for ( i = 0; i < trapcount_org; i++ )
02911                 {
02912                         if ( foundDetPacks[i] == ENTITYNUM_NONE )
02913                         {
02914                                 continue;
02915                         }
02916                         found = &g_entities[foundDetPacks[i]];
02917                         if ( found->setTime < lowestTimeStamp )
02918                         {
02919                                 removeMe = i;
02920                                 lowestTimeStamp = found->setTime;
02921                         }
02922                 }
02923                 if ( removeMe != -1 )
02924                 {
02925                         //remove it... or blow it?
02926                         if ( &g_entities[foundDetPacks[removeMe]] == NULL )
02927                         {
02928                                 break;
02929                         }
02930                         else
02931                         {
02932                                 if (!CheatsOn())
02933                                 { //Let them have unlimited if cheats are enabled
02934                                         G_FreeEntity( &g_entities[foundDetPacks[removeMe]] );
02935                                 }
02936                         }
02937                         foundDetPacks[removeMe] = ENTITYNUM_NONE;
02938                         trapcount--;
02939                 }
02940                 else
02941                 {
02942                         break;
02943                 }
02944         }
02945 
02946         if ( alt_fire  )
02947         {
02948                 BlowDetpacks(ent);
02949         }
02950         else
02951         {
02952                 AngleVectors( ent->client->ps.viewangles, forward, vright, up );
02953 
02954                 CalcMuzzlePoint( ent, forward, vright, up, muzzle );
02955 
02956                 VectorNormalize( forward );
02957                 VectorMA( muzzle, -4, forward, muzzle );
02958                 drop_charge( ent, muzzle, forward );
02959 
02960                 ent->client->ps.hasDetPackPlanted = qtrue;
02961         }
02962 }
02963 
02964 #pragma warning(disable : 4701) //local variable may be used without having been initialized
02965 static void WP_FireConcussionAlt( gentity_t *ent )
02966 {//a rail-gun-like beam
02967         int                     damage = CONC_ALT_DAMAGE, skip, traces = DISRUPTOR_ALT_TRACES;
02968         qboolean        render_impact = qtrue;
02969         vec3_t          start, end;
02970         vec3_t          muzzle2, dir;
02971         trace_t         tr;
02972         gentity_t       *traceEnt, *tent;
02973         float           shotRange = 8192.0f;
02974         qboolean        hitDodged = qfalse;
02975         vec3_t shot_mins, shot_maxs;
02976         int                     i;
02977 
02978         //Shove us backwards for half a second
02979         VectorMA( ent->client->ps.velocity, -200, forward, ent->client->ps.velocity );
02980         ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
02981         if ( (ent->client->ps.pm_flags&PMF_DUCKED) )
02982         {//hunkered down
02983                 ent->client->ps.pm_time = 100;
02984         }
02985         else
02986         {
02987                 ent->client->ps.pm_time = 250;
02988         }
02989 //      ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK|PMF_TIME_NOFRICTION;
02990         //FIXME: only if on ground?  So no "rocket jump"?  Or: (see next FIXME)
02991         //FIXME: instead, set a forced ucmd backmove instead of this sliding
02992 
02993         VectorCopy( muzzle, muzzle2 ); // making a backup copy
02994 
02995         VectorCopy( muzzle, start );
02996         WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );
02997 
02998         skip = ent->s.number;
02999 
03000 //      if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
03001 //      {
03002 //              // in overcharge mode, so doing double damage
03003 //              damage *= 2;
03004 //      }
03005         
03006         //Make it a little easier to hit guys at long range
03007         VectorSet( shot_mins, -1, -1, -1 );
03008         VectorSet( shot_maxs, 1, 1, 1 );
03009 
03010         for ( i = 0; i < traces; i++ )
03011         {
03012                 VectorMA( start, shotRange, forward, end );
03013 
03014                 //NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0"
03015                 //alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter
03016                 //gi.trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 );
03017                 if (d_projectileGhoul2Collision.integer)
03018                 {
03019                         trap_G2Trace( &tr, start, shot_mins, shot_maxs, end, skip, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
03020                 }
03021                 else
03022                 {
03023                         trap_Trace( &tr, start, shot_mins, shot_maxs, end, skip, MASK_SHOT );
03024                 }
03025 
03026                 traceEnt = &g_entities[tr.entityNum];
03027 
03028                 if (d_projectileGhoul2Collision.integer && traceEnt->inuse && traceEnt->client)
03029                 { //g2 collision checks -rww
03030                         if (traceEnt->inuse && traceEnt->client && traceEnt->ghoul2)
03031                         { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with.
03032                                 traceEnt->client->g2LastSurfaceHit = tr.surfaceFlags;
03033                                 traceEnt->client->g2LastSurfaceTime = level.time;
03034                         }
03035 
03036                         if (traceEnt->ghoul2)
03037                         {
03038                                 tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here.
03039                         }
03040                 }
03041                 if ( tr.surfaceFlags & SURF_NOIMPACT ) 
03042                 {
03043                         render_impact = qfalse;
03044                 }
03045 
03046                 if ( tr.entityNum == ent->s.number )
03047                 {
03048                         // should never happen, but basically we don't want to consider a hit to ourselves?
03049                         // Get ready for an attempt to trace through another person
03050                         VectorCopy( tr.endpos, muzzle2 );
03051                         VectorCopy( tr.endpos, start );
03052                         skip = tr.entityNum;
03053 #ifdef _DEBUG
03054                         Com_Printf( "BAD! Concussion gun shot somehow traced back and hit the owner!\n" );                      
03055 #endif
03056                         continue;
03057                 }
03058 
03059                 // always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
03060                 //NOTE: let's just draw one beam at the end
03061                 //tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT );
03062                 //tent->svFlags |= SVF_BROADCAST;
03063 
03064                 //VectorCopy( muzzle2, tent->s.origin2 );
03065 
03066                 if ( tr.fraction >= 1.0f )
03067                 {
03068                         // draw the beam but don't do anything else
03069                         break;
03070                 }
03071 
03072                 if ( traceEnt->s.weapon == WP_SABER )//&& traceEnt->NPC 
03073                 {//FIXME: need a more reliable way to know we hit a jedi?
03074                         hitDodged = Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE );
03075                         //acts like we didn't even hit him
03076                 }
03077                 if ( !hitDodged )
03078                 {
03079                         if ( render_impact )
03080                         {
03081                                 if (( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage ) 
03082                                         || !Q_stricmp( traceEnt->classname, "misc_model_breakable" ) 
03083                                         || traceEnt->s.eType == ET_MOVER )
03084                                 {
03085                                         qboolean noKnockBack;
03086 
03087                                         // Create a simple impact type mark that doesn't last long in the world
03088                                         //G_PlayEffectID( G_EffectIndex( "concussion/alt_hit" ), tr.endpos, tr.plane.normal );
03089                                         //no no no
03090 
03091                                         if ( traceEnt->client && LogAccuracyHit( traceEnt, ent )) 
03092                                         {//NOTE: hitting multiple ents can still get you over 100% accuracy
03093                                                 ent->client->accuracy_hits++;
03094                                         } 
03095 
03096                                         noKnockBack = (traceEnt->flags&FL_NO_KNOCKBACK);//will be set if they die, I want to know if it was on *before* they died
03097                                         if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH )
03098                                         {//hehe
03099                                                 G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT );
03100                                                 break;
03101                                         }
03102                                         G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT );
03103 
03104                                         //do knockback and knockdown manually
03105                                         if ( traceEnt->client )
03106                                         {//only if we hit a client
03107                                                 vec3_t pushDir;
03108                                                 VectorCopy( forward, pushDir );
03109                                                 if ( pushDir[2] < 0.2f )
03110                                                 {
03111                                                         pushDir[2] = 0.2f;
03112                                                 }//hmm, re-normalize?  nah...
03113                                                 /*
03114                                                 if ( !noKnockBack )
03115                                                 {//knock-backable
03116                                                         G_Throw( traceEnt, pushDir, 200 );
03117                                                 }
03118                                                 */
03119                                                 if ( traceEnt->health > 0 )
03120                                                 {//alive
03121                                                         //if ( G_HasKnockdownAnims( traceEnt ) )
03122                                                         if (!noKnockBack && !traceEnt->localAnimIndex && traceEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN &&
03123                                                                 BG_KnockDownable(&traceEnt->client->ps)) //just check for humanoids..
03124                                                         {//knock-downable
03125                                                                 //G_Knockdown( traceEnt, ent, pushDir, 400, qtrue );
03126                                                                 vec3_t plPDif;
03127                                                                 float pStr;
03128 
03129                                                                 //cap it and stuff, base the strength and whether or not we can knockdown on the distance
03130                                                                 //from the shooter to the target
03131                                                                 VectorSubtract(traceEnt->client->ps.origin, ent->client->ps.origin, plPDif);
03132                                                                 pStr = 500.0f-VectorLength(plPDif);
03133                                                                 if (pStr < 150.0f)
03134                                                                 {
03135                                                                         pStr = 150.0f;
03136                                                                 }
03137                                                                 if (pStr > 200.0f)
03138                                                                 {
03139                                                                         traceEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
03140                                                                         traceEnt->client->ps.forceHandExtendTime = level.time + 1100;
03141                                                                         traceEnt->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim
03142                                                                 }
03143                                                                 traceEnt->client->ps.otherKiller = ent->s.number;
03144                                                                 traceEnt->client->ps.otherKillerTime = level.time + 5000;
03145                                                                 traceEnt->client->ps.otherKillerDebounceTime = level.time + 100;
03146 
03147                                                                 traceEnt->client->ps.velocity[0] += pushDir[0]*pStr;
03148                                                                 traceEnt->client->ps.velocity[1] += pushDir[1]*pStr;
03149                                                                 traceEnt->client->ps.velocity[2] = pStr;
03150                                                         }
03151                                                 }
03152                                         }
03153 
03154                                         if ( traceEnt->s.eType == ET_MOVER )
03155                                         {//stop the traces on any mover
03156                                                 break;
03157                                         }
03158                                 }
03159                                 else 
03160                                 {
03161                                          // we only make this mark on things that can't break or move
03162                                 //      tent = G_TempEntity(tr.endpos, EV_MISSILE_MISS);
03163                                 //      tent->s.eventParm = DirToByte(tr.plane.normal);
03164                                 //      tent->s.eFlags |= EF_ALT_FIRING;
03165 
03166                                         //tent->svFlags |= SVF_BROADCAST;
03167                                         //eh? why broadcast?
03168                                 //      VectorCopy( tr.plane.normal, tent->pos1 );
03169 
03170                                         //mmm..no..don't do this more than once for no reason whatsoever.
03171                                         break; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool?
03172                                 }
03173                         }
03174                         else // not rendering impact, must be a skybox or other similar thing?
03175                         {
03176                                 break; // don't try anymore traces
03177                         }
03178                 }
03179                 // Get ready for an attempt to trace through another person
03180                 VectorCopy( tr.endpos, muzzle2 );
03181                 VectorCopy( tr.endpos, start );
03182                 skip = tr.entityNum;
03183                 hitDodged = qfalse;
03184         }
03185         //just draw one beam all the way to the end
03186 //      tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT );
03187 //      tent->svFlags |= SVF_BROADCAST;
03188         //again, why broadcast?
03189 
03190 //      tent = G_TempEntity(tr.endpos, EV_MISSILE_MISS);
03191 //      tent->s.eventParm = DirToByte(tr.plane.normal);
03192 //      tent->s.eFlags |= EF_ALT_FIRING;
03193 //      VectorCopy( muzzle, tent->s.origin2 );
03194 
03195         // now go along the trail and make sight events
03196         VectorSubtract( tr.endpos, muzzle, dir );
03197 
03198 //      shotDist = VectorNormalize( dir );
03199 
03200         //let's pack all this junk into a single tempent, and send it off.
03201         tent = G_TempEntity(tr.endpos, EV_CONC_ALT_IMPACT);
03202         tent->s.eventParm = DirToByte(tr.plane.normal);
03203         tent->s.owner = ent->s.number;
03204         VectorCopy(dir, tent->s.angles);
03205         VectorCopy(muzzle, tent->s.origin2);
03206         VectorCopy(forward, tent->s.angles2);
03207 
03208 #if 0 //yuck
03209         //FIXME: if shoot *really* close to someone, the alert could be way out of their FOV
03210         for ( dist = 0; dist < shotDist; dist += 64 )
03211         {
03212                 //FIXME: on a really long shot, this could make a LOT of alerts in one frame...
03213                 VectorMA( muzzle, dist, dir, spot );
03214                 AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
03215                 //FIXME: creates *way* too many effects, make it one effect somehow?
03216                 G_PlayEffectID( G_EffectIndex( "concussion/alt_ring" ), spot, actualAngles );
03217         }
03218         //FIXME: spawn a temp ent that continuously spawns sight alerts here?  And 1 sound alert to draw their attention?
03219         VectorMA( start, shotDist-4, forward, spot );
03220         AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
03221 
03222         G_PlayEffectID( G_EffectIndex( "concussion/altmuzzle_flash" ), muzzle, forward );
03223 #endif
03224 }
03225 #pragma warning(default : 4701) //local variable may be used without having been initialized
03226 
03227 static void WP_FireConcussion( gentity_t *ent )
03228 {//a fast rocket-like projectile
03229         vec3_t  start;
03230         int             damage  = CONC_DAMAGE;
03231         float   vel = CONC_VELOCITY;
03232         gentity_t *missile;
03233 
03234         //hold us still for a bit
03235         //ent->client->ps.pm_time = 300;
03236         //ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
03237         //add viewkick
03238 //      if ( ent->s.number < MAX_CLIENTS//player only
03239 //              && !cg.renderingThirdPerson )//gives an advantage to being in 3rd person, but would look silly otherwise
03240 //      {//kick the view back
03241 //              cg.kick_angles[PITCH] = Q_flrand( -10, -15 );
03242 //              cg.kick_time = level.time;
03243 //      }
03244         //mm..yeah..this needs some reworking for mp
03245 
03246         VectorCopy( muzzle, start );
03247         WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
03248 
03249         missile = CreateMissile( start, forward, vel, 10000, ent, qfalse );
03250 
03251         missile->classname = "conc_proj";
03252         missile->s.weapon = WP_CONCUSSION;
03253         missile->mass = 10;
03254 
03255         // Make it easier to hit things
03256         VectorSet( missile->r.maxs, ROCKET_SIZE, ROCKET_SIZE, ROCKET_SIZE );
03257         VectorScale( missile->r.maxs, -1, missile->r.mins );
03258 
03259         missile->damage = damage;
03260         missile->dflags = DAMAGE_EXTRA_KNOCKBACK;
03261 
03262         missile->methodOfDeath = MOD_CONC;
03263         missile->splashMethodOfDeath = MOD_CONC;
03264 
03265         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
03266         missile->splashDamage = CONC_SPLASH_DAMAGE;
03267         missile->splashRadius = CONC_SPLASH_RADIUS;
03268 
03269         // we don't want it to ever bounce
03270         missile->bounceCount = 0;
03271 }
03272 
03273 
03274 //---------------------------------------------------------
03275 // FireStunBaton
03276 //---------------------------------------------------------
03277 void WP_FireStunBaton( gentity_t *ent, qboolean alt_fire )
03278 {
03279         gentity_t       *tr_ent;
03280         trace_t         tr;
03281         vec3_t          mins, maxs, end;
03282         vec3_t          muzzleStun;
03283 
03284         if (!ent->client)
03285         {
03286                 VectorCopy(ent->r.currentOrigin, muzzleStun);
03287                 muzzleStun[2] += 8;
03288         }
03289         else
03290         {
03291                 VectorCopy(ent->client->ps.origin, muzzleStun);
03292                 muzzleStun[2] += ent->client->ps.viewheight-6;
03293         }
03294 
03295         VectorMA(muzzleStun, 20.0f, forward, muzzleStun);
03296         VectorMA(muzzleStun, 4.0f, vright, muzzleStun);
03297 
03298         VectorMA( muzzleStun, STUN_BATON_RANGE, forward, end );
03299 
03300         VectorSet( maxs, 6, 6, 6 );
03301         VectorScale( maxs, -1, mins );
03302 
03303         trap_Trace ( &tr, muzzleStun, mins, maxs, end, ent->s.number, MASK_SHOT );
03304 
03305         if ( tr.entityNum >= ENTITYNUM_WORLD )
03306         {
03307                 return;
03308         }
03309 
03310         tr_ent = &g_entities[tr.entityNum];
03311 
03312         if (tr_ent && tr_ent->takedamage && tr_ent->client)
03313         { //see if either party is involved in a duel
03314                 if (tr_ent->client->ps.duelInProgress &&
03315                         tr_ent->client->ps.duelIndex != ent->s.number)
03316                 {
03317                         return;
03318                 }
03319 
03320                 if (ent->client &&
03321                         ent->client->ps.duelInProgress &&
03322                         ent->client->ps.duelIndex != tr_ent->s.number)
03323                 {
03324                         return;
03325                 }
03326         }
03327 
03328         if ( tr_ent && tr_ent->takedamage )
03329         {
03330                 G_PlayEffect( EFFECT_STUNHIT, tr.endpos, tr.plane.normal );
03331 
03332                 G_Sound( tr_ent, CHAN_WEAPON, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
03333                 G_Damage( tr_ent, ent, ent, forward, tr.endpos, STUN_BATON_DAMAGE, (DAMAGE_NO_KNOCKBACK|DAMAGE_HALF_ABSORB), MOD_STUN_BATON );
03334 
03335                 if (tr_ent->client)
03336                 { //if it's a player then use the shock effect
03337                         if ( tr_ent->client->NPC_class == CLASS_VEHICLE )
03338                         {//not on vehicles
03339                                 if ( !tr_ent->m_pVehicle
03340                                         || tr_ent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL 
03341                                         || tr_ent->m_pVehicle->m_pVehicleInfo->type == VH_FLIER )
03342                                 {//can zap animals
03343                                         tr_ent->client->ps.electrifyTime = level.time + Q_irand( 3000, 4000 );
03344                                 }
03345                         }
03346                         else
03347                         {
03348                                 tr_ent->client->ps.electrifyTime = level.time + 700;
03349                         }
03350                 }
03351         }
03352 }
03353 
03354 
03355 //---------------------------------------------------------
03356 // FireMelee
03357 //---------------------------------------------------------
03358 void WP_FireMelee( gentity_t *ent, qboolean alt_fire )
03359 {
03360         gentity_t       *tr_ent;
03361         trace_t         tr;
03362         vec3_t          mins, maxs, end;
03363         vec3_t          muzzlePunch;
03364 
03365         if (ent->client && ent->client->ps.torsoAnim == BOTH_MELEE2)
03366         { //right
03367                 if (ent->client->ps.brokenLimbs & (1 << BROKENLIMB_RARM))
03368                 {
03369                         return;
03370                 }
03371         }
03372         else
03373         { //left
03374                 if (ent->client->ps.brokenLimbs & (1 << BROKENLIMB_LARM))
03375                 {
03376                         return;
03377                 }
03378         }
03379 
03380         if (!ent->client)
03381         {
03382                 VectorCopy(ent->r.currentOrigin, muzzlePunch);
03383                 muzzlePunch[2] += 8;
03384         }
03385         else
03386         {
03387                 VectorCopy(ent->client->ps.origin, muzzlePunch);
03388                 muzzlePunch[2] += ent->client->ps.viewheight-6;
03389         }
03390 
03391         VectorMA(muzzlePunch, 20.0f, forward, muzzlePunch);
03392         VectorMA(muzzlePunch, 4.0f, vright, muzzlePunch);
03393 
03394         VectorMA( muzzlePunch, MELEE_RANGE, forward, end );
03395 
03396         VectorSet( maxs, 6, 6, 6 );
03397         VectorScale( maxs, -1, mins );
03398 
03399         trap_Trace ( &tr, muzzlePunch, mins, maxs, end, ent->s.number, MASK_SHOT );
03400 
03401         if (tr.entityNum != ENTITYNUM_NONE)
03402         { //hit something
03403                 tr_ent = &g_entities[tr.entityNum];
03404 
03405                 G_Sound( ent, CHAN_AUTO, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
03406 
03407                 if (tr_ent->takedamage && tr_ent->client)
03408                 { //special duel checks
03409                         if (tr_ent->client->ps.duelInProgress &&
03410                                 tr_ent->client->ps.duelIndex != ent->s.number)
03411                         {
03412                                 return;
03413                         }
03414 
03415                         if (ent->client &&
03416                                 ent->client->ps.duelInProgress &&
03417                                 ent->client->ps.duelIndex != tr_ent->s.number)
03418                         {
03419                                 return;
03420                         }
03421                 }
03422 
03423                 if ( tr_ent->takedamage )
03424                 { //damage them, do more damage if we're in the second right hook
03425                         int dmg = MELEE_SWING1_DAMAGE;
03426 
03427                         if (ent->client && ent->client->ps.torsoAnim == BOTH_MELEE2)
03428                         { //do a tad bit more damage on the second swing
03429                                 dmg = MELEE_SWING2_DAMAGE;
03430                         }
03431 
03432                         if ( G_HeavyMelee( ent ) )
03433                         { //2x damage for heavy melee class
03434                                 dmg *= 2;
03435                         }
03436 
03437                         G_Damage( tr_ent, ent, ent, forward, tr.endpos, dmg, DAMAGE_NO_ARMOR, MOD_MELEE );
03438                 }
03439         }
03440 }
03441 
03447 
03448 
03449 /*
03450 ======================
03451 SnapVectorTowards
03452 
03453 Round a vector to integers for more efficient network
03454 transmission, but make sure that it rounds towards a given point
03455 rather than blindly truncating.  This prevents it from truncating 
03456 into a wall.
03457 ======================
03458 */
03459 void SnapVectorTowards( vec3_t v, vec3_t to ) {
03460         int             i;
03461 
03462         for ( i = 0 ; i < 3 ; i++ ) {
03463                 if ( to[i] <= v[i] ) {
03464                         v[i] = (int)v[i];
03465                 } else {
03466                         v[i] = (int)v[i] + 1;
03467                 }
03468         }
03469 }
03470 
03471 
03472 //======================================================================
03473 
03474 
03475 /*
03476 ===============
03477 LogAccuracyHit
03478 ===============
03479 */
03480 qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
03481         if( !target->takedamage ) {
03482                 return qfalse;
03483         }
03484 
03485         if ( target == attacker ) {
03486                 return qfalse;
03487         }
03488 
03489         if( !target->client ) {
03490                 return qfalse;
03491         }
03492 
03493         if (!attacker)
03494         {
03495                 return qfalse;
03496         }
03497 
03498         if( !attacker->client ) {
03499                 return qfalse;
03500         }
03501 
03502         if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
03503                 return qfalse;
03504         }
03505 
03506         if ( OnSameTeam( target, attacker ) ) {
03507                 return qfalse;
03508         }
03509 
03510         return qtrue;
03511 }
03512 
03513 
03514 /*
03515 ===============
03516 CalcMuzzlePoint
03517 
03518 set muzzle location relative to pivoting eye
03519 rwwFIXMEFIXME: Since ghoul2 models are on server and properly updated now,
03520 it may be reasonable to base muzzle point off actual weapon bolt point.
03521 The down side would be that it does not necessarily look alright from a
03522 first person perspective.
03523 ===============
03524 */
03525 void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) 
03526 {
03527         int weapontype;
03528         vec3_t muzzleOffPoint;
03529 
03530         weapontype = ent->s.weapon;
03531         VectorCopy( ent->s.pos.trBase, muzzlePoint );
03532 
03533         VectorCopy(WP_MuzzlePoint[weapontype], muzzleOffPoint);
03534 
03535         if (weapontype > WP_NONE && weapontype < WP_NUM_WEAPONS)
03536         {       // Use the table to generate the muzzlepoint;
03537                 {       // Crouching.  Use the add-to-Z method to adjust vertically.
03538                         VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint);
03539                         VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint);
03540                         muzzlePoint[2] += ent->client->ps.viewheight + muzzleOffPoint[2];
03541                 }
03542         }
03543 
03544         // snap to integer coordinates for more efficient network bandwidth usage
03545         SnapVector( muzzlePoint );
03546 }
03547 
03548 /*
03549 ===============
03550 CalcMuzzlePointOrigin
03551 
03552 set muzzle location relative to pivoting eye
03553 ===============
03554 */
03555 void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) {
03556         VectorCopy( ent->s.pos.trBase, muzzlePoint );
03557         muzzlePoint[2] += ent->client->ps.viewheight;
03558         VectorMA( muzzlePoint, 14, forward, muzzlePoint );
03559         // snap to integer coordinates for more efficient network bandwidth usage
03560         SnapVector( muzzlePoint );
03561 }
03562 
03563 extern void G_MissileImpact( gentity_t *ent, trace_t *trace );
03564 void WP_TouchVehMissile( gentity_t *ent, gentity_t *other, trace_t *trace )
03565 {
03566         trace_t myTrace;
03567         memcpy( (void *)&myTrace, (void *)trace, sizeof(myTrace) );
03568         if ( other )
03569         {
03570                 myTrace.entityNum = other->s.number;
03571         }
03572         G_MissileImpact( ent, &myTrace );
03573 }
03574 
03575 void WP_CalcVehMuzzle(gentity_t *ent, int muzzleNum)
03576 {
03577         Vehicle_t *pVeh = ent->m_pVehicle;
03578         mdxaBone_t boltMatrix;
03579         vec3_t  vehAngles;
03580 
03581         assert(pVeh);
03582 
03583         if (pVeh->m_iMuzzleTime[muzzleNum] == level.time)
03584         { //already done for this frame, don't need to do it again
03585                 return;
03586         }
03587         //Uh... how about we set this, hunh...?  :)
03588         pVeh->m_iMuzzleTime[muzzleNum] = level.time;
03589         
03590         VectorCopy( ent->client->ps.viewangles, vehAngles );
03591         if ( pVeh->m_pVehicleInfo
03592                 && (pVeh->m_pVehicleInfo->type == VH_ANIMAL
03593                          ||pVeh->m_pVehicleInfo->type == VH_WALKER
03594                          ||pVeh->m_pVehicleInfo->type == VH_SPEEDER) )
03595         {
03596                 vehAngles[PITCH] = vehAngles[ROLL] = 0;
03597         }
03598 
03599         trap_G2API_GetBoltMatrix_NoRecNoRot(ent->ghoul2, 0, pVeh->m_iMuzzleTag[muzzleNum], &boltMatrix, vehAngles,
03600                 ent->client->ps.origin, level.time, NULL, ent->modelScale);
03601         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, pVeh->m_vMuzzlePos[muzzleNum]);
03602         BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, pVeh->m_vMuzzleDir[muzzleNum]);
03603 }
03604 
03605 void WP_VehWeapSetSolidToOwner( gentity_t *self )
03606 {
03607         self->r.svFlags |= SVF_OWNERNOTSHARED;
03608         if ( self->genericValue1 )
03609         {//expire after a time
03610                 if ( self->genericValue2 )
03611                 {//blow up when your lifetime is up
03612                         self->think = G_ExplodeMissile;//FIXME: custom func?
03613                 }
03614                 else
03615                 {//just remove yourself
03616                         self->think = G_FreeEntity;//FIXME: custom func?
03617                 }
03618                 self->nextthink = level.time + self->genericValue1;
03619         }
03620 }
03621 
03622 #define VEH_HOMING_MISSILE_THINK_TIME           100
03623 gentity_t *WP_FireVehicleWeapon( gentity_t *ent, vec3_t start, vec3_t dir, vehWeaponInfo_t *vehWeapon, qboolean alt_fire, qboolean isTurretWeap )
03624 {
03625         gentity_t       *missile = NULL;
03626 
03627         //FIXME: add some randomness...?  Inherent inaccuracy stat of weapon?  Pilot skill?
03628         if ( !vehWeapon )
03629         {//invalid vehicle weapon
03630                 return NULL;
03631         }
03632         else if ( vehWeapon->bIsProjectile )
03633         {//projectile entity
03634                 vec3_t          mins, maxs;
03635 
03636                 VectorSet( maxs, vehWeapon->fWidth/2.0f,vehWeapon->fWidth/2.0f,vehWeapon->fHeight/2.0f );
03637                 VectorScale( maxs, -1, mins );
03638 
03639                 //make sure our start point isn't on the other side of a wall
03640                 WP_TraceSetStart( ent, start, mins, maxs );
03641                 
03642                 //FIXME: CUSTOM MODEL?
03643                 //QUERY: alt_fire true or not?  Does it matter?
03644                 missile = CreateMissile( start, dir, vehWeapon->fSpeed, 10000, ent, qfalse );
03645 
03646                 missile->classname = "vehicle_proj";
03647                 
03648                 missile->s.genericenemyindex = ent->s.number+MAX_GENTITIES;
03649                 missile->damage = vehWeapon->iDamage;
03650                 missile->splashDamage = vehWeapon->iSplashDamage;
03651                 missile->splashRadius = vehWeapon->fSplashRadius;
03652 
03653                 //FIXME: externalize some of these properties?
03654                 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
03655                 missile->clipmask = MASK_SHOT;
03656                 //Maybe by checking flags...?
03657                 if ( vehWeapon->bSaberBlockable )
03658                 {
03659                         missile->clipmask |= CONTENTS_LIGHTSABER;
03660                 }
03661                 /*
03662                 if ( (vehWeapon->iFlags&VWF_KNOCKBACK) )
03663                 {
03664                         missile->dflags &= ~DAMAGE_DEATH_KNOCKBACK;
03665                 }
03666                 if ( (vehWeapon->iFlags&VWF_RADAR) )
03667                 {
03668                         missile->s.eFlags |= EF_RADAROBJECT;
03669                 }
03670                 */
03671                 // Make it easier to hit things
03672                 VectorCopy( mins, missile->r.mins );
03673                 VectorCopy( maxs, missile->r.maxs );
03674                 //some slightly different stuff for things with bboxes
03675                 if ( vehWeapon->fWidth || vehWeapon->fHeight )
03676                 {//we assume it's a rocket-like thing
03677                         missile->s.weapon = WP_ROCKET_LAUNCHER;//does this really matter?
03678                         missile->methodOfDeath = MOD_VEHICLE;//MOD_ROCKET;
03679                         missile->splashMethodOfDeath = MOD_VEHICLE;//MOD_ROCKET;// ?SPLASH;
03680 
03681                         // we don't want it to ever bounce
03682                         missile->bounceCount = 0;
03683 
03684                         missile->mass = 10;
03685                 }
03686                 else
03687                 {//a blaster-laser-like thing
03688                         missile->s.weapon = WP_BLASTER;//does this really matter?
03689                         missile->methodOfDeath = MOD_VEHICLE; //count as a heavy weap
03690                         missile->splashMethodOfDeath = MOD_VEHICLE;// ?SPLASH;
03691                         // we don't want it to bounce forever
03692                         missile->bounceCount = 8;
03693                 }
03694                 
03695                 if ( vehWeapon->bHasGravity )
03696                 {//TESTME: is this all we need to do?
03697                         missile->s.weapon = WP_THERMAL;//does this really matter?
03698                         missile->s.pos.trType = TR_GRAVITY;
03699                 }
03700                 
03701                 if ( vehWeapon->bIonWeapon )
03702                 {//so it disables ship shields and sends them out of control
03703                         missile->s.weapon = WP_DEMP2;
03704                 }
03705 
03706                 if ( vehWeapon->iHealth )
03707                 {//the missile can take damage
03708                         missile->health = vehWeapon->iHealth;
03709                         missile->takedamage = qtrue;
03710                         missile->r.contents = MASK_SHOT;
03711                         missile->die = RocketDie;
03712                 }
03713 
03714                 //pilot should own this projectile on server if we have a pilot
03715                 if (ent->m_pVehicle && ent->m_pVehicle->m_pPilot)
03716                 {//owned by vehicle pilot
03717                         missile->r.ownerNum = ent->m_pVehicle->m_pPilot->s.number;
03718                 }
03719                 else
03720                 {//owned by vehicle?
03721                         missile->r.ownerNum = ent->s.number;
03722                 }
03723 
03724                 //set veh as cgame side owner for purpose of fx overrides
03725                 missile->s.owner = ent->s.number;
03726                 if ( alt_fire )
03727                 {//use the second weapon's iShotFX
03728                         missile->s.eFlags |= EF_ALT_FIRING;
03729                 }
03730                 if ( isTurretWeap )
03731                 {//look for the turret weapon info on cgame side, not vehicle weapon info
03732                         missile->s.weapon = WP_TURRET;
03733                 }
03734                 if ( vehWeapon->iLifeTime )
03735                 {//expire after a time
03736                         if ( vehWeapon->bExplodeOnExpire )
03737                         {//blow up when your lifetime is up
03738                                 missile->think = G_ExplodeMissile;//FIXME: custom func?
03739                         }
03740                         else
03741                         {//just remove yourself
03742                                 missile->think = G_FreeEntity;//FIXME: custom func?
03743                         }
03744                         missile->nextthink = level.time + vehWeapon->iLifeTime;
03745                 }
03746                 missile->s.otherEntityNum2 = (vehWeapon-&g_vehWeaponInfo[0]);
03747                 missile->s.eFlags |= EF_JETPACK_ACTIVE;
03748                 //homing
03749                 if ( vehWeapon->fHoming )
03750                 {//homing missile
03751                         if ( ent->client && ent->client->ps.rocketLockIndex != ENTITYNUM_NONE )
03752                         {
03753                                 int dif = 0;
03754                                 float rTime;
03755                                 rTime = ent->client->ps.rocketLockTime;
03756 
03757                                 if (rTime == -1)
03758                                 {
03759                                         rTime = ent->client->ps.rocketLastValidTime;
03760                                 }
03761 
03762                                 if ( !vehWeapon->iLockOnTime )
03763                                 {//no minimum lock-on time
03764                                         dif = 10;//guaranteed lock-on
03765                                 }
03766                                 else
03767                                 {
03768                                         float lockTimeInterval = vehWeapon->iLockOnTime/16.0f;
03769                                         dif = ( level.time - rTime ) / lockTimeInterval;
03770                                 }
03771 
03772                                 if (dif < 0)
03773                                 {
03774                                         dif = 0;
03775                                 }
03776 
03777                                 //It's 10 even though it locks client-side at 8, because we want them to have a sturdy lock first, and because there's a slight difference in time between server and client
03778                                 if ( dif >= 10 && rTime != -1 )
03779                                 {
03780                                         missile->enemy = &g_entities[ent->client->ps.rocketLockIndex];
03781 
03782                                         if (missile->enemy && missile->enemy->client && missile->enemy->health > 0 && !OnSameTeam(ent, missile->enemy))
03783                                         { //if enemy became invalid, died, or is on the same team, then don't seek it
03784                                                 missile->spawnflags |= 1;//just to let it know it should be faster...
03785                                                 missile->speed = vehWeapon->fSpeed;
03786                                                 missile->angle = vehWeapon->fHoming;
03787                                                 missile->radius = vehWeapon->fHomingFOV;
03788                                                 //crap, if we have a lifetime, need to store that somewhere else on ent and have rocketThink func check it every frame...
03789                                                 if ( vehWeapon->iLifeTime )
03790                                                 {//expire after a time
03791                                                         missile->genericValue1 = level.time + vehWeapon->iLifeTime;
03792                                                         missile->genericValue2 = (int)(vehWeapon->bExplodeOnExpire);
03793                                                 }
03794                                                 //now go ahead and use the rocketThink func
03795                                                 missile->think = rocketThink;//FIXME: custom func?
03796                                                 missile->nextthink = level.time + VEH_HOMING_MISSILE_THINK_TIME;
03797                                                 missile->s.eFlags |= EF_RADAROBJECT;//FIXME: externalize
03798                                                 if ( missile->enemy->s.NPC_class == CLASS_VEHICLE )
03799                                                 {//let vehicle know we've locked on to them
03800                                                         missile->s.otherEntityNum = missile->enemy->s.number;
03801                                                 }
03802                                         }
03803                                 }
03804 
03805                                 VectorCopy( dir, missile->movedir );
03806                                 missile->random = 1.0f;//FIXME: externalize?
03807                         }
03808                 }
03809                 if ( !vehWeapon->fSpeed )
03810                 {//a mine or something?
03811                         //only do damage when someone touches us
03812                         missile->s.weapon = WP_THERMAL;//does this really matter?
03813                         G_SetOrigin( missile, start );
03814                         missile->touch = WP_TouchVehMissile;
03815                         missile->s.eFlags |= EF_RADAROBJECT;//FIXME: externalize
03816                         //crap, if we have a lifetime, need to store that somewhere else on ent and have rocketThink func check it every frame...
03817                         if ( vehWeapon->iLifeTime )
03818                         {//expire after a time
03819                                 missile->genericValue1 = vehWeapon->iLifeTime;
03820                                 missile->genericValue2 = (int)(vehWeapon->bExplodeOnExpire);
03821                         }
03822                         //now go ahead and use the setsolidtoowner func
03823                         missile->think = WP_VehWeapSetSolidToOwner;
03824                         missile->nextthink = level.time + 3000;
03825                 }
03826         }
03827         else
03828         {//traceline
03829                 //FIXME: implement
03830         }
03831 
03832         return missile;
03833 }
03834 
03835 //custom routine to not waste tempents horribly -rww
03836 void G_VehMuzzleFireFX( gentity_t *ent, gentity_t *broadcaster, int muzzlesFired )
03837 {
03838         Vehicle_t *pVeh = ent->m_pVehicle;
03839         gentity_t *b;
03840 
03841         if (!pVeh)
03842         {
03843                 return;
03844         }
03845 
03846         if (!broadcaster)
03847         { //oh well. We will WASTE A TEMPENT.
03848                 b = G_TempEntity( ent->client->ps.origin, EV_VEH_FIRE );
03849         }
03850         else
03851         { //joy
03852                 b = broadcaster;
03853         }
03854 
03855         //this guy owns it
03856         b->s.owner = ent->s.number;
03857 
03858         //this is the bitfield of all muzzles fired this time
03859         //NOTE: just need MAX_VEHICLE_MUZZLES bits for this... should be cool since it's currently 12 and we're sending it in 16 bits
03860         b->s.trickedentindex = muzzlesFired;
03861 
03862         if ( broadcaster )
03863         { //add the event
03864                 G_AddEvent( b, EV_VEH_FIRE, 0 );
03865         }
03866 }
03867 
03868 void G_EstimateCamPos( vec3_t viewAngles, vec3_t cameraFocusLoc, float viewheight, float thirdPersonRange, 
03869                                           float thirdPersonHorzOffset, float vertOffset, float pitchOffset, 
03870                                           int ignoreEntNum, vec3_t camPos )
03871 {
03872         int             MASK_CAMERACLIP = (MASK_SOLID|CONTENTS_PLAYERCLIP);
03873         float   CAMERA_SIZE = 4;
03874         vec3_t  cameramins;
03875         vec3_t  cameramaxs;
03876         vec3_t  cameraFocusAngles, camerafwd, cameraup;
03877         vec3_t  cameraIdealTarget, cameraCurTarget;
03878         vec3_t  cameraIdealLoc, cameraCurLoc;
03879         vec3_t  diff;
03880         vec3_t  camAngles;
03881         vec3_t  viewaxis[3];
03882         trace_t trace;
03883 
03884         VectorSet( cameramins, -CAMERA_SIZE, -CAMERA_SIZE, -CAMERA_SIZE );
03885         VectorSet( cameramaxs, CAMERA_SIZE, CAMERA_SIZE, CAMERA_SIZE );
03886 
03887         VectorCopy( viewAngles, cameraFocusAngles );
03888         cameraFocusAngles[PITCH] += pitchOffset;
03889         if ( !bg_fighterAltControl.integer )
03890         {//clamp view pitch
03891                 cameraFocusAngles[PITCH] = AngleNormalize180( cameraFocusAngles[PITCH] );
03892                 if (cameraFocusAngles[PITCH] > 80.0)
03893                 {
03894                         cameraFocusAngles[PITCH] = 80.0;
03895                 }
03896                 else if (cameraFocusAngles[PITCH] < -80.0)
03897                 {
03898                         cameraFocusAngles[PITCH] = -80.0;
03899                 }
03900         }
03901         AngleVectors(cameraFocusAngles, camerafwd, NULL, cameraup);
03902 
03903         cameraFocusLoc[2] += viewheight;
03904 
03905         VectorCopy( cameraFocusLoc, cameraIdealTarget );
03906         cameraIdealTarget[2] += vertOffset;
03907 
03908         //NOTE: on cgame, this uses the thirdpersontargetdamp value, we ignore that here
03909         VectorCopy( cameraIdealTarget, cameraCurTarget );
03910         trap_Trace( &trace, cameraFocusLoc, cameramins, cameramaxs, cameraCurTarget, ignoreEntNum, MASK_CAMERACLIP );
03911         if (trace.fraction < 1.0)
03912         {
03913                 VectorCopy(trace.endpos, cameraCurTarget);
03914         }
03915 
03916         VectorMA(cameraIdealTarget, -(thirdPersonRange), camerafwd, cameraIdealLoc);
03917         //NOTE: on cgame, this uses the thirdpersoncameradamp value, we ignore that here
03918         VectorCopy( cameraIdealLoc, cameraCurLoc );
03919         trap_Trace(&trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, ignoreEntNum, MASK_CAMERACLIP);
03920         if (trace.fraction < 1.0)
03921         {
03922                 VectorCopy( trace.endpos, cameraCurLoc );
03923         }
03924 
03925         VectorSubtract(cameraCurTarget, cameraCurLoc, diff);
03926         {
03927                 float dist = VectorNormalize(diff);
03928                 //under normal circumstances, should never be 0.00000 and so on.
03929                 if ( !dist || (diff[0] == 0 || diff[1] == 0) )
03930                 {//must be hitting something, need some value to calc angles, so use cam forward
03931                         VectorCopy( camerafwd, diff );
03932                 }
03933         }
03934 
03935         vectoangles(diff, camAngles);
03936 
03937         if ( thirdPersonHorzOffset != 0.0f )
03938         {
03939                 AnglesToAxis( camAngles, viewaxis );
03940                 VectorMA( cameraCurLoc, thirdPersonHorzOffset, viewaxis[1], cameraCurLoc );
03941         }
03942 
03943         VectorCopy(cameraCurLoc, camPos);
03944 }
03945 
03946 void WP_GetVehicleCamPos( gentity_t *ent, gentity_t *pilot, vec3_t camPos )
03947 {
03948         float thirdPersonHorzOffset = ent->m_pVehicle->m_pVehicleInfo->cameraHorzOffset;
03949         float thirdPersonRange = ent->m_pVehicle->m_pVehicleInfo->cameraRange;
03950         float pitchOffset = ent->m_pVehicle->m_pVehicleInfo->cameraPitchOffset;
03951         float vertOffset = ent->m_pVehicle->m_pVehicleInfo->cameraVertOffset;
03952 
03953         if ( ent->client->ps.hackingTime )
03954         {
03955                 thirdPersonHorzOffset += (((float)ent->client->ps.hackingTime)/MAX_STRAFE_TIME) * -80.0f;
03956                 thirdPersonRange += fabs(((float)ent->client->ps.hackingTime)/MAX_STRAFE_TIME) * 100.0f;
03957         }
03958 
03959         if ( ent->m_pVehicle->m_pVehicleInfo->cameraPitchDependantVertOffset )
03960         {
03961                 if ( pilot->client->ps.viewangles[PITCH] > 0 )
03962                 {
03963                         vertOffset = 130+pilot->client->ps.viewangles[PITCH]*-10;
03964                         if ( vertOffset < -170 )
03965                         {
03966                                 vertOffset = -170;
03967                         }
03968                 }
03969                 else if ( pilot->client->ps.viewangles[PITCH] < 0 )
03970                 {
03971                         vertOffset = 130+pilot->client->ps.viewangles[PITCH]*-5;
03972                         if ( vertOffset > 130 )
03973                         {
03974                                 vertOffset = 130;
03975                         }
03976                 }
03977                 else
03978                 {
03979                         vertOffset = 30;
03980                 }
03981                 if ( pilot->client->ps.viewangles[PITCH] > 0 )
03982                 {
03983                         pitchOffset = pilot->client->ps.viewangles[PITCH]*-0.75;
03984                 }
03985                 else if ( pilot->client->ps.viewangles[PITCH] < 0 )
03986                 {
03987                         pitchOffset = pilot->client->ps.viewangles[PITCH]*-0.75;
03988                 }
03989                 else
03990                 {
03991                         pitchOffset = 0;
03992                 }
03993         }
03994 
03995         //Control Scheme 3 Method:
03996         G_EstimateCamPos( ent->client->ps.viewangles, pilot->client->ps.origin, pilot->client->ps.viewheight, thirdPersonRange, 
03997                 thirdPersonHorzOffset, vertOffset, pitchOffset, 
03998                 pilot->s.number, camPos );
03999         /*
04000         //Control Scheme 2 Method:
04001         G_EstimateCamPos( ent->m_pVehicle->m_vOrientation, ent->r.currentOrigin, pilot->client->ps.viewheight, thirdPersonRange, 
04002                 thirdPersonHorzOffset, vertOffset, pitchOffset, 
04003                 pilot->s.number, camPos );
04004         */
04005 }
04006 
04007 void WP_VehLeadCrosshairVeh( gentity_t *camTraceEnt, vec3_t newEnd, const vec3_t dir, const vec3_t shotStart, vec3_t shotDir )
04008 {
04009         if ( camTraceEnt 
04010                 && camTraceEnt->client
04011                 && camTraceEnt->client->NPC_class == CLASS_VEHICLE )
04012         {//if the crosshair is on a vehicle, lead it
04013                 float distAdjust = DotProduct( camTraceEnt->client->ps.velocity, dir );
04014                 VectorMA( newEnd, distAdjust, dir, newEnd );
04015         }
04016         VectorSubtract( newEnd, shotStart, shotDir );
04017         VectorNormalize( shotDir );
04018 }
04019 
04020 #define MAX_XHAIR_DIST_ACCURACY 20000.0f
04021 extern float g_cullDistance;
04022 extern int BG_VehTraceFromCamPos( trace_t *camTrace, bgEntity_t *bgEnt, const vec3_t entOrg, const vec3_t shotStart, const vec3_t end, vec3_t newEnd, vec3_t shotDir, float bestDist );
04023 qboolean WP_VehCheckTraceFromCamPos( gentity_t *ent, const vec3_t shotStart, vec3_t shotDir )
04024 {
04025         //FIXME: only if dynamicCrosshair and dynamicCrosshairPrecision is on!
04026         if ( !ent 
04027                 || !ent->m_pVehicle 
04028                 || !ent->m_pVehicle->m_pVehicleInfo 
04029                 || !ent->m_pVehicle->m_pPilot//not being driven
04030                 || !((gentity_t*)ent->m_pVehicle->m_pPilot)->client//not being driven by a client...?!!!
04031                 || (ent->m_pVehicle->m_pPilot->s.number >= MAX_CLIENTS) )//being driven, but not by a real client, no need to worry about crosshair
04032         {
04033                 return qfalse;
04034         }
04035         if ( (ent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && g_cullDistance > MAX_XHAIR_DIST_ACCURACY )
04036                 || ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER)
04037         {
04038                 //FIRST: simulate the normal crosshair trace from the center of the veh straight forward
04039                 trace_t trace;
04040                 vec3_t  dir, start, end;
04041                 if ( ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
04042                 {//for some reason, the walker always draws the crosshair out from from the first muzzle point
04043                         AngleVectors( ent->client->ps.viewangles, dir, NULL, NULL );
04044                         VectorCopy( ent->r.currentOrigin, start );
04045                         start[2] += ent->m_pVehicle->m_pVehicleInfo->height-DEFAULT_MINS_2-48;
04046                 }
04047                 else
04048                 {
04049                         vec3_t ang;
04050                         if (ent->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER)
04051                         {
04052                                 VectorSet(ang, 0.0f, ent->m_pVehicle->m_vOrientation[1], 0.0f);
04053                         }
04054                         else
04055                         {
04056                                 VectorCopy(ent->m_pVehicle->m_vOrientation, ang);
04057                         }
04058                         AngleVectors( ang, dir, NULL, NULL );
04059                         VectorCopy( ent->r.currentOrigin, start );
04060                 }
04061                 VectorMA( start, g_cullDistance, dir, end );
04062                 trap_Trace( &trace, start, vec3_origin, vec3_origin, end, 
04063                         ent->s.number, CONTENTS_SOLID|CONTENTS_BODY );
04064 
04065                 if ( ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
04066                 {//just use the result of that one trace since walkers don't do the extra trace
04067                         VectorSubtract( trace.endpos, shotStart, shotDir );
04068                         VectorNormalize( shotDir );
04069                         return qtrue;
04070                 }
04071                 else
04072                 {//NOW do the trace from the camPos and compare with above trace
04073                         trace_t extraTrace;
04074                         vec3_t  newEnd;
04075                         int camTraceEntNum = BG_VehTraceFromCamPos( &extraTrace, (bgEntity_t *)ent, ent->r.currentOrigin, shotStart, end, newEnd, shotDir, (trace.fraction*g_cullDistance) );
04076                         if ( camTraceEntNum )
04077                         {
04078                                 WP_VehLeadCrosshairVeh( &g_entities[camTraceEntNum-1], newEnd, dir, shotStart, shotDir );
04079                                 return qtrue;
04080                         }
04081                 }
04082         }
04083         return qfalse;
04084 }
04085 
04086 //---------------------------------------------------------
04087 void FireVehicleWeapon( gentity_t *ent, qboolean alt_fire ) 
04088 //---------------------------------------------------------
04089 {
04090         Vehicle_t *pVeh = ent->m_pVehicle;
04091         int muzzlesFired = 0;
04092         gentity_t *missile = NULL;
04093         vehWeaponInfo_t *vehWeapon = NULL;
04094         qboolean        clearRocketLockEntity = qfalse;
04095         
04096         if ( !pVeh )
04097         {
04098                 return;
04099         }
04100 
04101         if (pVeh->m_iRemovedSurfaces)
04102         { //can't fire when the thing is breaking apart
04103                 return;
04104         }
04105 
04106         if (pVeh->m_pVehicleInfo->type == VH_WALKER &&
04107                 ent->client->ps.electrifyTime > level.time)
04108         { //don't fire while being electrocuted
04109                 return;
04110         }
04111 
04112         // TODO?: If possible (probably not enough time), it would be nice if secondary fire was actually a mode switch/toggle
04113         // so that, for instance, an x-wing can have 4-gun fire, or individual muzzle fire. If you wanted a different weapon, you
04114         // would actually have to press the 2 key or something like that (I doubt I'd get a graphic for it anyways though). -AReis
04115 
04116         // If this is not the alternate fire, fire a normal blaster shot...
04117         if ( pVeh->m_pVehicleInfo &&
04118                 (pVeh->m_pVehicleInfo->type != VH_FIGHTER || (pVeh->m_ulFlags&VEH_WINGSOPEN)) ) // NOTE: Wings open also denotes that it has already launched.
04119         {//fighters can only fire when wings are open
04120                 int     weaponNum = 0, vehWeaponIndex = VEH_WEAPON_NONE;
04121                 int     delay = 1000;
04122                 qboolean aimCorrect = qfalse;
04123                 qboolean linkedFiring = qfalse;
04124 
04125                 if ( !alt_fire )
04126                 {
04127                         weaponNum = 0;
04128                 }
04129                 else
04130                 {
04131                         weaponNum = 1;
04132                 }
04133 
04134                 vehWeaponIndex = pVeh->m_pVehicleInfo->weapon[weaponNum].ID;
04135 
04136                 if ( pVeh->weaponStatus[weaponNum].ammo <= 0 )
04137                 {//no ammo for this weapon
04138                         if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS )
04139                         {// let the client know he's out of ammo
04140                                 int i;
04141                                 //but only if one of the vehicle muzzles is actually ready to fire this weapon
04142                                 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04143                                 {
04144                                         if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04145                                         {//this muzzle doesn't match the weapon we're trying to use
04146                                                 continue;
04147                                         }
04148                                         if ( pVeh->m_iMuzzleTag[i] != -1 
04149                                                 && pVeh->m_iMuzzleWait[i] < level.time )
04150                                         {//this one would have fired, send the no ammo message
04151                                                 G_AddEvent( (gentity_t*)pVeh->m_pPilot, EV_NOAMMO, weaponNum );
04152                                                 break;
04153                                         }
04154                                 }
04155                         }
04156                         return;
04157                 }
04158 
04159                 delay = pVeh->m_pVehicleInfo->weapon[weaponNum].delay;
04160                 aimCorrect = pVeh->m_pVehicleInfo->weapon[weaponNum].aimCorrect;
04161                 if ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable == 2//always linked
04162                         || ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable == 1//optionally linkable
04163                                  && pVeh->weaponStatus[weaponNum].linked ) )//linked
04164                 {//we're linking the primary or alternate weapons, so we'll do *all* the muzzles
04165                         linkedFiring = qtrue;
04166                 }
04167 
04168                 if ( vehWeaponIndex <= VEH_WEAPON_BASE || vehWeaponIndex >= MAX_VEH_WEAPONS )
04169                 {//invalid vehicle weapon
04170                         return;
04171                 }
04172                 else
04173                 {
04174                         int i, numMuzzles = 0, numMuzzlesReady = 0, cumulativeDelay = 0, cumulativeAmmo = 0;
04175                         qboolean sentAmmoWarning = qfalse;
04176 
04177                         vehWeapon = &g_vehWeaponInfo[vehWeaponIndex];
04178 
04179                         if ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable == 2 )
04180                         {//always linked weapons don't accumulate delay, just use specified delay
04181                                 cumulativeDelay = delay;
04182                         }
04183                         //find out how many we've got for this weapon
04184                         for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04185                         {
04186                                 if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04187                                 {//this muzzle doesn't match the weapon we're trying to use
04188                                         continue;
04189                                 }
04190                                 if ( pVeh->m_iMuzzleTag[i] != -1 && pVeh->m_iMuzzleWait[i] < level.time )
04191                                 {
04192                                         numMuzzlesReady++;
04193                                 }
04194                                 if ( pVeh->m_pVehicleInfo->weapMuzzle[pVeh->weaponStatus[weaponNum].nextMuzzle] != vehWeaponIndex )
04195                                 {//Our designated next muzzle for this weapon isn't valid for this weapon (happens when ships fire for the first time)
04196                                         //set the next to this one
04197                                         pVeh->weaponStatus[weaponNum].nextMuzzle = i;
04198                                 }
04199                                 if ( linkedFiring )
04200                                 {
04201                                         cumulativeAmmo += vehWeapon->iAmmoPerShot;
04202                                         if ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable != 2 )
04203                                         {//always linked weapons don't accumulate delay, just use specified delay
04204                                                 cumulativeDelay += delay;
04205                                         }
04206                                 }
04207                                 numMuzzles++;
04208                         }
04209 
04210                         if ( linkedFiring )
04211                         {//firing all muzzles at once
04212                                 if ( numMuzzlesReady != numMuzzles )
04213                                 {//can't fire all linked muzzles yet
04214                                         return;
04215                                 }
04216                                 else 
04217                                 {//can fire all linked muzzles, check ammo
04218                                         if ( pVeh->weaponStatus[weaponNum].ammo < cumulativeAmmo )
04219                                         {//can't fire, not enough ammo
04220                                                 if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS )
04221                                                 {// let the client know he's out of ammo
04222                                                         G_AddEvent( (gentity_t*)pVeh->m_pPilot, EV_NOAMMO, weaponNum );
04223                                                 }
04224                                                 return;
04225                                         }
04226                                 }
04227                         }
04228 
04229                         for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04230                         {
04231                                 if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04232                                 {//this muzzle doesn't match the weapon we're trying to use
04233                                         continue;
04234                                 }
04235                                 if ( !linkedFiring
04236                                         && i != pVeh->weaponStatus[weaponNum].nextMuzzle )
04237                                 {//we're only firing one muzzle and this isn't it
04238                                         continue;
04239                                 }
04240 
04241                                 // Fire this muzzle.
04242                                 if ( pVeh->m_iMuzzleTag[i] != -1 && pVeh->m_iMuzzleWait[i] < level.time )
04243                                 {
04244                                         vec3_t  start, dir;
04245                                         
04246                                         if ( pVeh->weaponStatus[weaponNum].ammo < vehWeapon->iAmmoPerShot )
04247                                         {//out of ammo!
04248                                                 if ( !sentAmmoWarning )
04249                                                 {
04250                                                         sentAmmoWarning = qtrue;
04251                                                         if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS )
04252                                                         {// let the client know he's out of ammo
04253                                                                 G_AddEvent( (gentity_t*)pVeh->m_pPilot, EV_NOAMMO, weaponNum );
04254                                                         }
04255                                                 }
04256                                         }
04257                                         else
04258                                         {//have enough ammo to shoot
04259                                                 //do the firing
04260                                                 WP_CalcVehMuzzle(ent, i);
04261                                                 VectorCopy( pVeh->m_vMuzzlePos[i], start );
04262                                                 VectorCopy( pVeh->m_vMuzzleDir[i], dir );
04263                                                 if ( WP_VehCheckTraceFromCamPos( ent, start, dir ) )
04264                                                 {//auto-aim at whatever crosshair would be over from camera's point of view (if closer)
04265                                                 }
04266                                                 else if ( aimCorrect )
04267                                                 {//auto-aim the missile at the crosshair if there's anything there
04268                                                         trace_t trace;
04269                                                         vec3_t  end;
04270                                                         vec3_t  ang;
04271                                                         vec3_t  fixedDir;
04272 
04273                                                         if (pVeh->m_pVehicleInfo->type == VH_SPEEDER)
04274                                                         {
04275                                                                 VectorSet(ang, 0.0f, pVeh->m_vOrientation[1], 0.0f);
04276                                                         }
04277                                                         else
04278                                                         {
04279                                                                 VectorCopy(pVeh->m_vOrientation, ang);
04280                                                         }
04281                                                         AngleVectors( ang, fixedDir, NULL, NULL );
04282                                                         VectorMA( ent->r.currentOrigin, 32768, fixedDir, end );
04283                                                         //VectorMA( ent->r.currentOrigin, 8192, dir, end );
04284                                                         trap_Trace( &trace, ent->r.currentOrigin, vec3_origin, vec3_origin, end, ent->s.number, MASK_SHOT );
04285                                                         if ( trace.fraction < 1.0f && !trace.allsolid && !trace.startsolid )
04286                                                         {
04287                                                                 vec3_t newEnd;
04288                                                                 VectorCopy( trace.endpos, newEnd );
04289                                                                 WP_VehLeadCrosshairVeh( &g_entities[trace.entityNum], newEnd, fixedDir, start, dir );
04290                                                         }
04291                                                 }
04292 
04293                                                 //play the weapon's muzzle effect if we have one
04294                                                 //NOTE: just need MAX_VEHICLE_MUZZLES bits for this... should be cool since it's currently 12 and we're sending it in 16 bits
04295                                                 muzzlesFired |= (1<<i);
04296                                                                                                 
04297                                                 missile = WP_FireVehicleWeapon( ent, start, dir, vehWeapon, alt_fire, qfalse );
04298                                                 if ( vehWeapon->fHoming )
04299                                                 {//clear the rocket lock entity *after* all muzzles have fired
04300                                                         clearRocketLockEntity = qtrue;
04301                                                 }
04302                                         }
04303 
04304                                         if ( linkedFiring )
04305                                         {//we're linking the weapon, so continue on and fire all appropriate muzzles
04306                                                 continue;
04307                                         }
04308                                         //else just firing one
04309                                         //take the ammo, set the next muzzle and set the delay on it
04310                                         if ( numMuzzles > 1 )
04311                                         {//more than one, look for it
04312                                                 int nextMuzzle = pVeh->weaponStatus[weaponNum].nextMuzzle;
04313                                                 while ( 1 )
04314                                                 {
04315                                                         nextMuzzle++;
04316                                                         if ( nextMuzzle >= MAX_VEHICLE_MUZZLES )
04317                                                         {
04318                                                                 nextMuzzle = 0;
04319                                                         }
04320                                                         if ( nextMuzzle == pVeh->weaponStatus[weaponNum].nextMuzzle )
04321                                                         {//WTF?  Wrapped without finding another valid one!
04322                                                                 break;
04323                                                         }
04324                                                         if ( pVeh->m_pVehicleInfo->weapMuzzle[nextMuzzle] == vehWeaponIndex )
04325                                                         {//this is the next muzzle for this weapon
04326                                                                 pVeh->weaponStatus[weaponNum].nextMuzzle = nextMuzzle;
04327                                                                 break;
04328                                                         }
04329                                                 }
04330                                         }//else, just stay on the one we just fired
04331                                         //set the delay on the next muzzle
04332                                         pVeh->m_iMuzzleWait[pVeh->weaponStatus[weaponNum].nextMuzzle] = level.time + delay;
04333                                         //take away the ammo
04334                                         pVeh->weaponStatus[weaponNum].ammo -= vehWeapon->iAmmoPerShot;
04335                                         //NOTE: in order to send the vehicle's ammo info to the client, we copy the ammo into the first 2 ammo slots on the vehicle NPC's client->ps.ammo array
04336                                         if ( pVeh->m_pParentEntity && ((gentity_t*)(pVeh->m_pParentEntity))->client )
04337                                         {
04338                                                 ((gentity_t*)(pVeh->m_pParentEntity))->client->ps.ammo[weaponNum] = pVeh->weaponStatus[weaponNum].ammo;
04339                                         }
04340                                         //done!
04341                                         //we'll get in here again next frame and try the next muzzle...
04342                                         //return;
04343                                         goto tryFire;
04344                                 }
04345                         }
04346                         //we went through all the muzzles, so apply the cumulative delay and ammo cost
04347                         if ( cumulativeAmmo )
04348                         {//taking ammo one shot at a time
04349                                 //take the ammo
04350                                 pVeh->weaponStatus[weaponNum].ammo -= cumulativeAmmo;
04351                                 //NOTE: in order to send the vehicle's ammo info to the client, we copy the ammo into the first 2 ammo slots on the vehicle NPC's client->ps.ammo array
04352                                 if ( pVeh->m_pParentEntity && ((gentity_t*)(pVeh->m_pParentEntity))->client )
04353                                 {
04354                                         ((gentity_t*)(pVeh->m_pParentEntity))->client->ps.ammo[weaponNum] = pVeh->weaponStatus[weaponNum].ammo;
04355                                 }
04356                         }
04357                         if ( cumulativeDelay )
04358                         {//we linked muzzles so we need to apply the cumulative delay now, to each of the linked muzzles
04359                                 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
04360                                 {
04361                                         if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
04362                                         {//this muzzle doesn't match the weapon we're trying to use
04363                                                 continue;
04364                                         }
04365                                         //apply the cumulative delay
04366                                         pVeh->m_iMuzzleWait[i] = level.time + cumulativeDelay;
04367                                 }
04368                         }
04369                 }
04370         }
04371 
04372 tryFire:
04373         if ( clearRocketLockEntity )
04374         {//hmm, should probably clear that anytime any weapon fires?
04375                 ent->client->ps.rocketLockIndex = ENTITYNUM_NONE;
04376                 ent->client->ps.rocketLockTime = 0;
04377                 ent->client->ps.rocketTargetTime = 0;
04378         }
04379 
04380         if ( vehWeapon && muzzlesFired > 0 )
04381         {
04382                 G_VehMuzzleFireFX(ent, missile, muzzlesFired );
04383         }
04384 }
04385 
04386 /*
04387 ===============
04388 FireWeapon
04389 ===============
04390 */
04391 #include "../namespace_begin.h"
04392 int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
04393 #include "../namespace_end.h"
04394 
04395 void FireWeapon( gentity_t *ent, qboolean altFire ) {
04396         if (ent->client->ps.powerups[PW_QUAD] ) {
04397                 s_quadFactor = g_quadfactor.value;
04398         } else {
04399                 s_quadFactor = 1;
04400         }
04401 
04402         // track shots taken for accuracy tracking.  Grapple is not a weapon and gauntet is just not tracked
04403         if( ent->s.weapon != WP_SABER && ent->s.weapon != WP_STUN_BATON && ent->s.weapon != WP_MELEE ) 
04404         {
04405                 if( ent->s.weapon == WP_FLECHETTE ) {
04406                         ent->client->accuracy_shots += FLECHETTE_SHOTS;
04407                 } else {
04408                         ent->client->accuracy_shots++;
04409                 }
04410         }
04411 
04412         if ( ent && ent->client && ent->client->NPC_class == CLASS_VEHICLE )
04413         {
04414                 FireVehicleWeapon( ent, altFire );
04415                 return;
04416         }
04417         else
04418         {
04419                 // set aiming directions
04420                 if (ent->s.weapon == WP_EMPLACED_GUN &&
04421                         ent->client->ps.emplacedIndex)
04422                 { //if using emplaced then base muzzle point off of gun position/angles
04423                         gentity_t *emp = &g_entities[ent->client->ps.emplacedIndex];
04424 
04425                         if (emp->inuse)
04426                         {
04427                                 float yaw;
04428                                 vec3_t viewAngCap;
04429                                 int override;
04430 
04431                                 VectorCopy(ent->client->ps.viewangles, viewAngCap);
04432                                 if (viewAngCap[PITCH] > 40)
04433                                 {
04434                                         viewAngCap[PITCH] = 40;
04435                                 }
04436 
04437                                 override = BG_EmplacedView(ent->client->ps.viewangles, emp->s.angles, &yaw,
04438                                         emp->s.origin2[0]);
04439                                 
04440                                 if (override)
04441                                 {
04442                                         viewAngCap[YAW] = yaw;
04443                                 }
04444 
04445                                 AngleVectors( viewAngCap, forward, vright, up );
04446                         }
04447                         else
04448                         {
04449                                 AngleVectors( ent->client->ps.viewangles, forward, vright, up );
04450                         }
04451                 }
04452                 else if (ent->s.number < MAX_CLIENTS &&
04453                         ent->client->ps.m_iVehicleNum && ent->s.weapon == WP_BLASTER)
04454                 { //riding a vehicle...with blaster selected
04455                         vec3_t vehTurnAngles;
04456                         gentity_t *vehEnt = &g_entities[ent->client->ps.m_iVehicleNum];
04457 
04458                         if (vehEnt->inuse && vehEnt->client && vehEnt->m_pVehicle)
04459                         {
04460                                 VectorCopy(vehEnt->m_pVehicle->m_vOrientation, vehTurnAngles);
04461                                 vehTurnAngles[PITCH] = ent->client->ps.viewangles[PITCH];
04462                         }
04463                         else
04464                         {
04465                                 VectorCopy(ent->client->ps.viewangles, vehTurnAngles);
04466                         }
04467                         if (ent->client->pers.cmd.rightmove > 0)
04468                         { //shooting to right
04469                                 vehTurnAngles[YAW] -= 90.0f;
04470                         }
04471                         else if (ent->client->pers.cmd.rightmove < 0)
04472                         { //shooting to left
04473                                 vehTurnAngles[YAW] += 90.0f;
04474                         }
04475 
04476                         AngleVectors( vehTurnAngles, forward, vright, up );
04477                 }
04478                 else
04479                 {
04480                         AngleVectors( ent->client->ps.viewangles, forward, vright, up );
04481                 }
04482 
04483                 CalcMuzzlePoint ( ent, forward, vright, up, muzzle );
04484 
04485                 // fire the specific weapon
04486                 switch( ent->s.weapon ) {
04487                 case WP_STUN_BATON:
04488                         WP_FireStunBaton( ent, altFire );
04489                         break;
04490 
04491                 case WP_MELEE:
04492                         WP_FireMelee(ent, altFire);
04493                         break;
04494 
04495                 case WP_SABER:
04496                         break;
04497 
04498                 case WP_BRYAR_PISTOL:
04499                         //if ( g_gametype.integer == GT_SIEGE )
04500                         if (1)
04501                         {//allow alt-fire
04502                                 WP_FireBryarPistol( ent, altFire );
04503                         }
04504                         else
04505                         {
04506                                 WP_FireBryarPistol( ent, qfalse );
04507                         }
04508                         break;
04509 
04510                 case WP_CONCUSSION:
04511                         if ( altFire )
04512                         {
04513                                 WP_FireConcussionAlt( ent );
04514                         }
04515                         else
04516                         {
04517                                 WP_FireConcussion( ent );
04518                         }
04519                         break;
04520 
04521                 case WP_BRYAR_OLD:
04522                         WP_FireBryarPistol( ent, altFire );
04523                         break;
04524 
04525                 case WP_BLASTER:
04526                         WP_FireBlaster( ent, altFire );
04527                         break;
04528 
04529                 case WP_DISRUPTOR:
04530                         WP_FireDisruptor( ent, altFire );
04531                         break;
04532 
04533                 case WP_BOWCASTER:
04534                         WP_FireBowcaster( ent, altFire );
04535                         break;
04536 
04537                 case WP_REPEATER:
04538                         WP_FireRepeater( ent, altFire );
04539                         break;
04540 
04541                 case WP_DEMP2:
04542                         WP_FireDEMP2( ent, altFire );
04543                         break;
04544 
04545                 case WP_FLECHETTE:
04546                         WP_FireFlechette( ent, altFire );
04547                         break;
04548 
04549                 case WP_ROCKET_LAUNCHER:
04550                         WP_FireRocket( ent, altFire );
04551                         break;
04552 
04553                 case WP_THERMAL:
04554                         WP_FireThermalDetonator( ent, altFire );
04555                         break;
04556 
04557                 case WP_TRIP_MINE:
04558                         WP_PlaceLaserTrap( ent, altFire );
04559                         break;
04560 
04561                 case WP_DET_PACK:
04562                         WP_DropDetPack( ent, altFire );
04563                         break;
04564 
04565                 case WP_EMPLACED_GUN:
04566                         if (ent->client && ent->client->ewebIndex)
04567                         { //specially handled by the e-web itself
04568                                 break;
04569                         }
04570                         WP_FireEmplaced( ent, altFire );
04571                         break;
04572                 default:
04573 //                      assert(!"unknown weapon fire");
04574                         break;
04575                 }
04576         }
04577 
04578         G_LogWeaponFire(ent->s.number, ent->s.weapon);
04579 }
04580 
04581 //---------------------------------------------------------
04582 static void WP_FireEmplaced( gentity_t *ent, qboolean altFire )
04583 //---------------------------------------------------------
04584 {
04585         vec3_t  dir, angs, gunpoint;
04586         vec3_t  right;
04587         gentity_t *gun;
04588         int side;
04589 
04590         if (!ent->client)
04591         {
04592                 return;
04593         }
04594 
04595         if (!ent->client->ps.emplacedIndex)
04596         { //shouldn't be using WP_EMPLACED_GUN if we aren't on an emplaced weapon
04597                 return;
04598         }
04599 
04600         gun = &g_entities[ent->client->ps.emplacedIndex];
04601 
04602         if (!gun->inuse || gun->health <= 0)
04603         { //gun was removed or killed, although we should never hit this check because we should have been forced off it already
04604                 return;
04605         }
04606 
04607         VectorCopy(gun->s.origin, gunpoint);
04608         gunpoint[2] += 46;
04609 
04610         AngleVectors(ent->client->ps.viewangles, NULL, right, NULL);
04611 
04612         if (gun->genericValue10)
04613         { //fire out of the right cannon side
04614                 VectorMA(gunpoint, 10.0f, right, gunpoint);
04615                 side = 0;
04616         }
04617         else
04618         { //the left
04619                 VectorMA(gunpoint, -10.0f, right, gunpoint);
04620                 side = 1;
04621         }
04622 
04623         gun->genericValue10 = side;
04624         G_AddEvent(gun, EV_FIRE_WEAPON, side);
04625 
04626         vectoangles( forward, angs );
04627 
04628         AngleVectors( angs, dir, NULL, NULL );
04629 
04630         WP_FireEmplacedMissile( gun, gunpoint, dir, altFire, ent );
04631 }
04632 
04633 #define EMPLACED_CANRESPAWN 1
04634 
04635 //----------------------------------------------------------
04636 
04637 /*QUAKED emplaced_gun (0 0 1) (-30 -20 8) (30 20 60) CANRESPAWN
04638 
04639  count - if CANRESPAWN spawnflag, decides how long it is before gun respawns (in ms)
04640  constraint - number of degrees gun is constrained from base angles on each side (default 60.0)
04641 
04642  showhealth - set to 1 to show health bar on this entity when crosshair is over it
04643   
04644   teamowner - crosshair shows green for this team, red for opposite team
04645         0 - none
04646         1 - red
04647         2 - blue
04648 
04649   alliedTeam - team that can use this
04650         0 - any
04651         1 - red
04652         2 - blue
04653 
04654   teamnodmg - team that turret does not take damage from or do damage to
04655         0 - none
04656         1 - red
04657         2 - blue
04658 */
04659  
04660 //----------------------------------------------------------
04661 extern qboolean TryHeal(gentity_t *ent, gentity_t *target); //g_utils.c
04662 void emplaced_gun_use( gentity_t *self, gentity_t *other, trace_t *trace )
04663 {
04664         vec3_t fwd1, fwd2;
04665         float dot;
04666         int oldWeapon;
04667         gentity_t *activator = other;
04668         float zoffset = 50;
04669         vec3_t anglesToOwner;
04670         vec3_t vLen;
04671         float ownLen;
04672 
04673         if ( self->health <= 0 )
04674         { //gun is destroyed
04675                 return;
04676         }
04677 
04678         if (self->activator)
04679         { //someone is already using me
04680                 return;
04681         }
04682 
04683         if (!activator->client)
04684         {
04685                 return;
04686         }
04687 
04688         if (activator->client->ps.emplacedTime > level.time)
04689         { //last use attempt still too recent
04690                 return;
04691         }
04692 
04693         if (activator->client->ps.forceHandExtend != HANDEXTEND_NONE)
04694         { //don't use if busy doing something else
04695                 return;
04696         }
04697 
04698         if (activator->client->ps.origin[2] > self->s.origin[2]+zoffset-8)
04699         { //can't use it from the top
04700                 return;
04701         }
04702 
04703         if (activator->client->ps.pm_flags & PMF_DUCKED)
04704         { //must be standing
04705                 return;
04706         }
04707 
04708         if (activator->client->ps.isJediMaster)
04709         { //jm can't use weapons
04710                 return;
04711         }
04712 
04713         VectorSubtract(self->s.origin, activator->client->ps.origin, vLen);
04714         ownLen = VectorLength(vLen);
04715 
04716         if (ownLen > 64.0f)
04717         { //must be within 64 units of the gun to use at all
04718                 return;
04719         }
04720 
04721         // Let's get some direction vectors for the user
04722         AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL );
04723 
04724         // Get the guns direction vector
04725         AngleVectors( self->pos1, fwd2, NULL, NULL );
04726 
04727         dot = DotProduct( fwd1, fwd2 );
04728 
04729         // Must be reasonably facing the way the gun points ( 110 degrees or so ), otherwise we don't allow to use it.
04730         if ( dot < -0.2f )
04731         {
04732                 goto tryHeal;
04733         }
04734 
04735         VectorSubtract(self->s.origin, activator->client->ps.origin, fwd1);
04736         VectorNormalize(fwd1);
04737 
04738         dot = DotProduct( fwd1, fwd2 );
04739 
04740         //check the positioning in relation to the gun as well
04741         if ( dot < 0.6f )
04742         {
04743                 goto tryHeal;
04744         }
04745 
04746         self->genericValue1 = 1;
04747 
04748         oldWeapon = activator->s.weapon;
04749 
04750         // swap the users weapon with the emplaced gun
04751         activator->client->ps.weapon = self->s.weapon;
04752         activator->client->ps.weaponstate = WEAPON_READY;
04753         activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN );
04754 
04755         activator->client->ps.emplacedIndex = self->s.number;
04756 
04757         self->s.emplacedOwner = activator->s.number;
04758         self->s.activeForcePass = NUM_FORCE_POWERS+1;
04759 
04760         // the gun will track which weapon we used to have
04761         self->s.weapon = oldWeapon;
04762 
04763         //user's new owner becomes the gun ent
04764         activator->r.ownerNum = self->s.number;
04765         self->activator = activator;
04766 
04767         VectorSubtract(self->r.currentOrigin, activator->client->ps.origin, anglesToOwner);
04768         vectoangles(anglesToOwner, anglesToOwner);
04769         return;
04770 
04771 tryHeal: //well, not in the right dir, try healing it instead...
04772         TryHeal(activator, self);
04773 }
04774 
04775 void emplaced_gun_realuse( gentity_t *self, gentity_t *other, gentity_t *activator )
04776 {
04777         emplaced_gun_use(self, other, NULL);
04778 }
04779 
04780 //----------------------------------------------------------
04781 void emplaced_gun_pain( gentity_t *self, gentity_t *attacker, int damage )
04782 {
04783         self->s.health = self->health;
04784 
04785         if ( self->health <= 0 )
04786         {
04787                 //death effect.. for now taken care of on cgame
04788         }
04789         else
04790         {
04791                 //if we have a pain behavior set then use it I guess
04792                 G_ActivateBehavior( self, BSET_PAIN );
04793         }
04794 }
04795 
04796 #define EMPLACED_GUN_HEALTH 800
04797 
04798 //----------------------------------------------------------
04799 void emplaced_gun_update(gentity_t *self)
04800 {
04801         vec3_t  smokeOrg, puffAngle;
04802         int oldWeap;
04803         float ownLen = 0;
04804 
04805         if (self->health < 1 && !self->genericValue5)
04806         { //we are dead, set our respawn delay if we have one
04807                 if (self->spawnflags & EMPLACED_CANRESPAWN)
04808                 {
04809                         self->genericValue5 = level.time + 4000 + self->count;
04810                 }
04811         }
04812         else if (self->health < 1 && self->genericValue5 < level.time)
04813         { //we are dead, see if it's time to respawn
04814                 self->s.time = 0;
04815                 self->genericValue4 = 0;
04816                 self->genericValue3 = 0;
04817                 self->health = EMPLACED_GUN_HEALTH*0.4;
04818                 self->s.health = self->health;
04819         }
04820 
04821         if (self->genericValue4 && self->genericValue4 < 2 && self->s.time < level.time)
04822         { //we have finished our warning (red flashing) effect, it's time to finish dying
04823                 vec3_t explOrg;
04824 
04825                 VectorSet( puffAngle, 0, 0, 1 );
04826 
04827                 VectorCopy(self->r.currentOrigin, explOrg);
04828                 explOrg[2] += 16;
04829 
04830                 //just use the detpack explosion effect
04831                 G_PlayEffect(EFFECT_EXPLOSION_DETPACK, explOrg, puffAngle);
04832 
04833                 self->genericValue3 = level.time + Q_irand(2500, 3500);
04834 
04835                 G_RadiusDamage(self->r.currentOrigin, self, self->splashDamage, self->splashRadius, self, NULL, MOD_UNKNOWN);
04836 
04837                 self->s.time = -1;
04838 
04839                 self->genericValue4 = 2;
04840         }
04841 
04842         if (self->genericValue3 > level.time)
04843         { //see if we are freshly dead and should be smoking
04844                 if (self->genericValue2 < level.time)
04845                 { //is it time yet to spawn another smoke puff?
04846                         VectorSet( puffAngle, 0, 0, 1 );
04847                         VectorCopy(self->r.currentOrigin, smokeOrg);
04848 
04849                         smokeOrg[2] += 60;
04850 
04851                         G_PlayEffect(EFFECT_SMOKE, smokeOrg, puffAngle);
04852                         self->genericValue2 = level.time + Q_irand(250, 400);
04853                 }
04854         }
04855 
04856         if (self->activator && self->activator->client && self->activator->inuse)
04857         { //handle updating current user
04858                 vec3_t vLen;
04859                 VectorSubtract(self->s.origin, self->activator->client->ps.origin, vLen);
04860                 ownLen = VectorLength(vLen);
04861 
04862                 if (!(self->activator->client->pers.cmd.buttons & BUTTON_USE) && self->genericValue1)
04863                 {
04864                         self->genericValue1 = 0;
04865                 }
04866 
04867                 if ((self->activator->client->pers.cmd.buttons & BUTTON_USE) && !self->genericValue1)
04868                 {
04869                         self->activator->client->ps.emplacedIndex = 0;
04870                         self->activator->client->ps.saberHolstered = 0;
04871                         self->nextthink = level.time + 50;
04872                         return;
04873                 }
04874         }
04875 
04876         if ((self->activator && self->activator->client) &&
04877                 (!self->activator->inuse || self->activator->client->ps.emplacedIndex != self->s.number || self->genericValue4 || ownLen > 64))
04878         { //get the user off of me then
04879                 self->activator->client->ps.stats[STAT_WEAPONS] &= ~(1<<WP_EMPLACED_GUN);
04880 
04881                 oldWeap = self->activator->client->ps.weapon;
04882                 self->activator->client->ps.weapon = self->s.weapon;
04883                 self->s.weapon = oldWeap;
04884                 self->activator->r.ownerNum = ENTITYNUM_NONE;
04885                 self->activator->client->ps.emplacedTime = level.time + 1000;
04886                 self->activator->client->ps.emplacedIndex = 0;
04887                 self->activator->client->ps.saberHolstered = 0;
04888                 self->activator = NULL;
04889 
04890                 self->s.activeForcePass = 0;
04891         }
04892         else if (self->activator && self->activator->client)
04893         { //make sure the user is using the emplaced gun weapon
04894                 self->activator->client->ps.weapon = WP_EMPLACED_GUN;
04895                 self->activator->client->ps.weaponstate = WEAPON_READY;
04896         }
04897         self->nextthink = level.time + 50;
04898 }
04899 
04900 //----------------------------------------------------------
04901 void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
04902 { //set us up to flash and then explode
04903         if (self->genericValue4)
04904         {
04905                 return;
04906         }
04907 
04908         self->genericValue4 = 1;
04909 
04910         self->s.time = level.time + 3000;
04911 
04912         self->genericValue5 = 0;
04913 }
04914 
04915 void SP_emplaced_gun( gentity_t *ent )
04916 {
04917         const char *name = "models/map_objects/mp/turret_chair.glm";
04918         vec3_t down;
04919         trace_t tr;
04920 
04921         //make sure our assets are precached
04922         RegisterItem( BG_FindItemForWeapon(WP_EMPLACED_GUN) );
04923 
04924         ent->r.contents = CONTENTS_SOLID;
04925         ent->s.solid = SOLID_BBOX;
04926 
04927         ent->genericValue5 = 0;
04928 
04929         VectorSet( ent->r.mins, -30, -20, 8 );
04930         VectorSet( ent->r.maxs, 30, 20, 60 );
04931 
04932         VectorCopy(ent->s.origin, down);
04933 
04934         down[2] -= 1024;
04935 
04936         trap_Trace(&tr, ent->s.origin, ent->r.mins, ent->r.maxs, down, ent->s.number, MASK_SOLID);
04937 
04938         if (tr.fraction != 1 && !tr.allsolid && !tr.startsolid)
04939         {
04940                 VectorCopy(tr.endpos, ent->s.origin);
04941         }
04942 
04943         ent->spawnflags |= 4; // deadsolid
04944 
04945         ent->health = EMPLACED_GUN_HEALTH;
04946 
04947         if (ent->spawnflags & EMPLACED_CANRESPAWN)
04948         { //make it somewhat easier to kill if it can respawn
04949                 ent->health *= 0.4;
04950         }
04951 
04952         ent->maxHealth = ent->health;
04953         G_ScaleNetHealth(ent);
04954 
04955         ent->genericValue4 = 0;
04956 
04957         ent->takedamage = qtrue;
04958         ent->pain = emplaced_gun_pain;
04959         ent->die = emplaced_gun_die;
04960 
04961         // being caught in this thing when it blows would be really bad.
04962         ent->splashDamage = 80;
04963         ent->splashRadius = 128;
04964 
04965         // amount of ammo that this little poochie has
04966         G_SpawnInt( "count", "600", &ent->count );
04967 
04968         G_SpawnFloat( "constraint", "60", &ent->s.origin2[0] );
04969 
04970         ent->s.modelindex = G_ModelIndex( (char *)name );
04971         ent->s.modelGhoul2 = 1;
04972         ent->s.g2radius = 110;
04973 
04974         //so the cgame knows for sure that we're an emplaced weapon
04975         ent->s.weapon = WP_EMPLACED_GUN;
04976 
04977         G_SetOrigin( ent, ent->s.origin );
04978         
04979         // store base angles for later
04980         VectorCopy( ent->s.angles, ent->pos1 );
04981         VectorCopy( ent->s.angles, ent->r.currentAngles );
04982         VectorCopy( ent->s.angles, ent->s.apos.trBase );
04983 
04984         ent->think = emplaced_gun_update;
04985         ent->nextthink = level.time + 50;
04986 
04987         ent->use = emplaced_gun_realuse;
04988 
04989         ent->r.svFlags |= SVF_PLAYER_USABLE;
04990 
04991         ent->s.pos.trType = TR_STATIONARY;
04992 
04993         ent->s.owner = MAX_CLIENTS+1;
04994         ent->s.shouldtarget = qtrue;
04995         //ent->s.teamowner = 0;
04996 
04997         trap_LinkEntity(ent);
04998 }