codemp/game/g_missile.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 #include "g_local.h"
00004 #include "w_saber.h"
00005 #include "q_shared.h"
00006 
00007 #define MISSILE_PRESTEP_TIME    50
00008 
00009 extern void laserTrapStick( gentity_t *ent, vec3_t endpos, vec3_t normal );
00010 extern void Jedi_Decloak( gentity_t *self );
00011 
00012 #include "../namespace_begin.h"
00013 extern qboolean FighterIsLanded( Vehicle_t *pVeh, playerState_t *parentPS );
00014 #include "../namespace_end.h"
00015 
00016 /*
00017 ================
00018 G_ReflectMissile
00019 
00020   Reflect the missile roughly back at it's owner
00021 ================
00022 */
00023 float RandFloat(float min, float max);
00024 void G_ReflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward ) 
00025 {
00026         vec3_t  bounce_dir;
00027         int             i;
00028         float   speed;
00029         gentity_t       *owner = ent;
00030         int             isowner = 0;
00031 
00032         if ( ent->r.ownerNum )
00033         {
00034                 owner = &g_entities[ent->r.ownerNum];
00035         }
00036 
00037         if (missile->r.ownerNum == ent->s.number)
00038         { //the original owner is bouncing the missile, so don't try to bounce it back at him
00039                 isowner = 1;
00040         }
00041 
00042         //save the original speed
00043         speed = VectorNormalize( missile->s.pos.trDelta );
00044 
00045         //if ( ent && owner && owner->NPC && owner->enemy && Q_stricmp( "Tavion", owner->NPC_type ) == 0 && Q_irand( 0, 3 ) )
00046         if ( &g_entities[missile->r.ownerNum] && missile->s.weapon != WP_SABER && missile->s.weapon != G2_MODEL_PART && !isowner )
00047         {//bounce back at them if you can
00048                 VectorSubtract( g_entities[missile->r.ownerNum].r.currentOrigin, missile->r.currentOrigin, bounce_dir );
00049                 VectorNormalize( bounce_dir );
00050         }
00051         else if (isowner)
00052         { //in this case, actually push the missile away from me, and since we're giving boost to our own missile by pushing it, up the velocity
00053                 vec3_t missile_dir;
00054 
00055                 speed *= 1.5;
00056 
00057                 VectorSubtract( missile->r.currentOrigin, ent->r.currentOrigin, missile_dir );
00058                 VectorCopy( missile->s.pos.trDelta, bounce_dir );
00059                 VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir );
00060                 VectorNormalize( bounce_dir );
00061         }
00062         else
00063         {
00064                 vec3_t missile_dir;
00065 
00066                 VectorSubtract( ent->r.currentOrigin, missile->r.currentOrigin, missile_dir );
00067                 VectorCopy( missile->s.pos.trDelta, bounce_dir );
00068                 VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir );
00069                 VectorNormalize( bounce_dir );
00070         }
00071         for ( i = 0; i < 3; i++ )
00072         {
00073                 bounce_dir[i] += RandFloat( -0.2f, 0.2f );
00074         }
00075 
00076         VectorNormalize( bounce_dir );
00077         VectorScale( bounce_dir, speed, missile->s.pos.trDelta );
00078         missile->s.pos.trTime = level.time;             // move a bit on the very first frame
00079         VectorCopy( missile->r.currentOrigin, missile->s.pos.trBase );
00080         if ( missile->s.weapon != WP_SABER && missile->s.weapon != G2_MODEL_PART )
00081         {//you are mine, now!
00082                 missile->r.ownerNum = ent->s.number;
00083         }
00084         if ( missile->s.weapon == WP_ROCKET_LAUNCHER )
00085         {//stop homing
00086                 missile->think = 0;
00087                 missile->nextthink = 0;
00088         }
00089 }
00090 
00091 void G_DeflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward ) 
00092 {
00093         vec3_t  bounce_dir;
00094         int             i;
00095         float   speed;
00096         int             isowner = 0;
00097         vec3_t missile_dir;
00098 
00099         if (missile->r.ownerNum == ent->s.number)
00100         { //the original owner is bouncing the missile, so don't try to bounce it back at him
00101                 isowner = 1;
00102         }
00103 
00104         //save the original speed
00105         speed = VectorNormalize( missile->s.pos.trDelta );
00106 
00107         if (ent->client)
00108         {
00109                 //VectorSubtract( ent->r.currentOrigin, missile->r.currentOrigin, missile_dir );
00110                 AngleVectors(ent->client->ps.viewangles, missile_dir, 0, 0);
00111                 VectorCopy(missile_dir, bounce_dir);
00112                 //VectorCopy( missile->s.pos.trDelta, bounce_dir );
00113                 VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir );
00114                 VectorNormalize( bounce_dir );
00115         }
00116         else
00117         {
00118                 VectorCopy(forward, bounce_dir);
00119                 VectorNormalize(bounce_dir);
00120         }
00121 
00122         for ( i = 0; i < 3; i++ )
00123         {
00124                 bounce_dir[i] += RandFloat( -1.0f, 1.0f );
00125         }
00126 
00127         VectorNormalize( bounce_dir );
00128         VectorScale( bounce_dir, speed, missile->s.pos.trDelta );
00129         missile->s.pos.trTime = level.time;             // move a bit on the very first frame
00130         VectorCopy( missile->r.currentOrigin, missile->s.pos.trBase );
00131         if ( missile->s.weapon != WP_SABER && missile->s.weapon != G2_MODEL_PART )
00132         {//you are mine, now!
00133                 missile->r.ownerNum = ent->s.number;
00134         }
00135         if ( missile->s.weapon == WP_ROCKET_LAUNCHER )
00136         {//stop homing
00137                 missile->think = 0;
00138                 missile->nextthink = 0;
00139         }
00140 }
00141 
00142 /*
00143 ================
00144 G_BounceMissile
00145 
00146 ================
00147 */
00148 void G_BounceMissile( gentity_t *ent, trace_t *trace ) {
00149         vec3_t  velocity;
00150         float   dot;
00151         int             hitTime;
00152 
00153         // reflect the velocity on the trace plane
00154         hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
00155         BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
00156         dot = DotProduct( velocity, trace->plane.normal );
00157         VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
00158 
00159 
00160         if ( ent->flags & FL_BOUNCE_SHRAPNEL ) 
00161         {
00162                 VectorScale( ent->s.pos.trDelta, 0.25f, ent->s.pos.trDelta );
00163                 ent->s.pos.trType = TR_GRAVITY;
00164 
00165                 // check for stop
00166                 if ( trace->plane.normal[2] > 0.7 && ent->s.pos.trDelta[2] < 40 ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7
00167                 {
00168                         G_SetOrigin( ent, trace->endpos );
00169                         ent->nextthink = level.time + 100;
00170                         return;
00171                 }
00172         }
00173         else if ( ent->flags & FL_BOUNCE_HALF ) 
00174         {
00175                 VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
00176                 // check for stop
00177                 if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) 
00178                 {
00179                         G_SetOrigin( ent, trace->endpos );
00180                         return;
00181                 }
00182         }
00183 
00184         if (ent->s.weapon == WP_THERMAL)
00185         { //slight hack for hit sound
00186                 G_Sound(ent, CHAN_BODY, G_SoundIndex(va("sound/weapons/thermal/bounce%i.wav", Q_irand(1, 2))));
00187         }
00188         else if (ent->s.weapon == WP_SABER)
00189         {
00190                 G_Sound(ent, CHAN_BODY, G_SoundIndex(va("sound/weapons/saber/bounce%i.wav", Q_irand(1, 3))));
00191         }
00192         else if (ent->s.weapon == G2_MODEL_PART)
00193         {
00194                 //Limb bounce sound?
00195         }
00196 
00197         VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
00198         VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
00199         ent->s.pos.trTime = level.time;
00200 
00201         if (ent->bounceCount != -5)
00202         {
00203                 ent->bounceCount--;
00204         }
00205 }
00206 
00207 
00208 /*
00209 ================
00210 G_ExplodeMissile
00211 
00212 Explode a missile without an impact
00213 ================
00214 */
00215 void G_ExplodeMissile( gentity_t *ent ) {
00216         vec3_t          dir;
00217         vec3_t          origin;
00218 
00219         BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
00220         SnapVector( origin );
00221         G_SetOrigin( ent, origin );
00222 
00223         // we don't have a valid direction, so just point straight up
00224         dir[0] = dir[1] = 0;
00225         dir[2] = 1;
00226 
00227         ent->s.eType = ET_GENERAL;
00228         G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
00229 
00230         ent->freeAfterEvent = qtrue;
00231 
00232         ent->takedamage = qfalse;
00233         // splash damage
00234         if ( ent->splashDamage ) {
00235                 if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, 
00236                                 ent, ent->splashMethodOfDeath ) ) 
00237                 {
00238                         if (ent->parent)
00239                         {
00240                                 g_entities[ent->parent->s.number].client->accuracy_hits++;
00241                         }
00242                         else if (ent->activator)
00243                         {
00244                                 g_entities[ent->activator->s.number].client->accuracy_hits++;
00245                         }
00246                 }
00247         }
00248 
00249         trap_LinkEntity( ent );
00250 }
00251 
00252 void G_RunStuckMissile( gentity_t *ent )
00253 {
00254         if ( ent->takedamage )
00255         {
00256                 if ( ent->s.groundEntityNum >= 0 && ent->s.groundEntityNum < ENTITYNUM_WORLD )
00257                 {
00258                         gentity_t *other = &g_entities[ent->s.groundEntityNum];
00259 
00260                         if ( (!VectorCompare( vec3_origin, other->s.pos.trDelta ) && other->s.pos.trType != TR_STATIONARY) || 
00261                                 (!VectorCompare( vec3_origin, other->s.apos.trDelta ) && other->s.apos.trType != TR_STATIONARY) )
00262                         {//thing I stuck to is moving or rotating now, kill me
00263                                 G_Damage( ent, other, other, NULL, NULL, 99999, 0, MOD_CRUSH );
00264                                 return;
00265                         }
00266                 }
00267         }
00268         // check think function
00269         G_RunThink( ent );
00270 }
00271 
00272 /*
00273 ================
00274 G_BounceProjectile
00275 ================
00276 */
00277 void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
00278         vec3_t v, newv;
00279         float dot;
00280 
00281         VectorSubtract( impact, start, v );
00282         dot = DotProduct( v, dir );
00283         VectorMA( v, -2*dot, dir, newv );
00284 
00285         VectorNormalize(newv);
00286         VectorMA(impact, 8192, newv, endout);
00287 }
00288 
00289 
00290 //-----------------------------------------------------------------------------
00291 gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life, 
00292                                                         gentity_t *owner, qboolean altFire)
00293 //-----------------------------------------------------------------------------
00294 {
00295         gentity_t       *missile;
00296 
00297         missile = G_Spawn();
00298         
00299         missile->nextthink = level.time + life;
00300         missile->think = G_FreeEntity;
00301         missile->s.eType = ET_MISSILE;
00302         missile->r.svFlags = SVF_USE_CURRENT_ORIGIN;
00303         missile->parent = owner;
00304         missile->r.ownerNum = owner->s.number;
00305 
00306         if (altFire)
00307         {
00308                 missile->s.eFlags |= EF_ALT_FIRING;
00309         }
00310 
00311         missile->s.pos.trType = TR_LINEAR;
00312         missile->s.pos.trTime = level.time;// - MISSILE_PRESTEP_TIME;   // NOTENOTE This is a Quake 3 addition over JK2
00313         missile->target_ent = NULL;
00314 
00315         SnapVector(org);
00316         VectorCopy( org, missile->s.pos.trBase );
00317         VectorScale( dir, vel, missile->s.pos.trDelta );
00318         VectorCopy( org, missile->r.currentOrigin);
00319         SnapVector(missile->s.pos.trDelta);
00320 
00321         return missile;
00322 }
00323 
00324 void G_MissileBounceEffect( gentity_t *ent, vec3_t org, vec3_t dir )
00325 {
00326         //FIXME: have an EV_BOUNCE_MISSILE event that checks the s.weapon and does the appropriate effect
00327         switch( ent->s.weapon )
00328         {
00329         case WP_BOWCASTER:
00330                 G_PlayEffectID( G_EffectIndex("bowcaster/deflect"), ent->r.currentOrigin, dir );
00331                 break;
00332         case WP_BLASTER:
00333         case WP_BRYAR_PISTOL:
00334                 G_PlayEffectID( G_EffectIndex("blaster/deflect"), ent->r.currentOrigin, dir );
00335                 break;
00336         default:
00337                 {
00338                         gentity_t *te = G_TempEntity( org, EV_SABER_BLOCK );
00339                         VectorCopy(org, te->s.origin);
00340                         VectorCopy(dir, te->s.angles);
00341                         te->s.eventParm = 0;
00342                         te->s.weapon = 0;//saberNum
00343                         te->s.legsAnim = 0;//bladeNum
00344                 }
00345                 break;
00346         }
00347 }
00348 
00349 /*
00350 ================
00351 G_MissileImpact
00352 ================
00353 */
00354 void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlock );
00355 void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
00356         gentity_t               *other;
00357         qboolean                hitClient = qfalse;
00358         qboolean                isKnockedSaber = qfalse;
00359 
00360         other = &g_entities[trace->entityNum];
00361 
00362         // check for bounce
00363         if ( !other->takedamage &&
00364                 (ent->bounceCount > 0 || ent->bounceCount == -5) &&
00365                 ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) {
00366                 G_BounceMissile( ent, trace );
00367                 G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
00368                 return;
00369         }
00370         else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF))
00371         { //this is a knocked-away saber
00372                 if (ent->bounceCount > 0 || ent->bounceCount == -5)
00373                 {
00374                         G_BounceMissile( ent, trace );
00375                         G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
00376                         return;
00377                 }
00378 
00379                 isKnockedSaber = qtrue;
00380         }
00381         
00382         // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else
00383         if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_BOUNCE_SHRAPNEL) ) ) || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius&&(ent->bounceCount > 0 || ent->bounceCount == -5)) ) 
00384         {
00385                 G_BounceMissile( ent, trace );
00386 
00387                 if ( ent->bounceCount < 1 )
00388                 {
00389                         ent->flags &= ~FL_BOUNCE_SHRAPNEL;
00390                 }
00391                 return;
00392         }
00393 
00394         /*
00395         if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire )
00396         {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!!
00397                 //G_BounceRollMissile( ent, trace );
00398                 if ( ent->owner && ent->owner->s.number == 0 ) 
00399                 {
00400                         G_MissileAddAlerts( ent );
00401                 }
00402                 //gi.linkentity( ent );
00403                 return;
00404         }
00405         */
00406 
00407         if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber)
00408         { //hit this person's saber, so..
00409                 gentity_t *otherOwner = &g_entities[other->r.ownerNum];
00410 
00411                 if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress &&
00412                         otherOwner->client->ps.duelIndex != ent->r.ownerNum)
00413                 {
00414                         goto killProj;
00415                 }
00416         }
00417         else if (!isKnockedSaber)
00418         {
00419                 if (other->takedamage && other->client && other->client->ps.duelInProgress &&
00420                         other->client->ps.duelIndex != ent->r.ownerNum)
00421                 {
00422                         goto killProj;
00423                 }
00424         }
00425 
00426         if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY)
00427         {
00428                 if (ent->methodOfDeath != MOD_REPEATER_ALT &&
00429                         ent->methodOfDeath != MOD_ROCKET &&
00430                         ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
00431                         ent->methodOfDeath != MOD_ROCKET_HOMING &&
00432                         ent->methodOfDeath != MOD_THERMAL &&
00433                         ent->methodOfDeath != MOD_THERMAL_SPLASH &&
00434                         ent->methodOfDeath != MOD_TRIP_MINE_SPLASH &&
00435                         ent->methodOfDeath != MOD_TIMED_MINE_SPLASH &&
00436                         ent->methodOfDeath != MOD_DET_PACK_SPLASH &&
00437                         ent->methodOfDeath != MOD_VEHICLE &&
00438                         ent->methodOfDeath != MOD_CONC &&
00439                         ent->methodOfDeath != MOD_CONC_ALT &&
00440                         ent->methodOfDeath != MOD_SABER &&
00441                         ent->methodOfDeath != MOD_TURBLAST)
00442                 {
00443                         vec3_t fwd;
00444 
00445                         if (trace)
00446                         {
00447                                 VectorCopy(trace->plane.normal, fwd);
00448                         }
00449                         else
00450                         { //oh well
00451                                 AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
00452                         }
00453 
00454                         G_DeflectMissile(other, ent, fwd);
00455                         G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
00456                         return;
00457                 }
00458         }
00459 
00460         if ((other->flags & FL_SHIELDED) &&
00461                 ent->s.weapon != WP_ROCKET_LAUNCHER &&
00462                 ent->s.weapon != WP_THERMAL &&
00463                 ent->s.weapon != WP_TRIP_MINE &&
00464                 ent->s.weapon != WP_DET_PACK &&
00465                 ent->s.weapon != WP_DEMP2 &&
00466                 ent->s.weapon != WP_EMPLACED_GUN &&
00467                 ent->methodOfDeath != MOD_REPEATER_ALT &&
00468                 ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && 
00469                 ent->methodOfDeath != MOD_TURBLAST &&
00470                 ent->methodOfDeath != MOD_VEHICLE &&
00471                 ent->methodOfDeath != MOD_CONC &&
00472                 ent->methodOfDeath != MOD_CONC_ALT &&
00473                 !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) )
00474         {
00475                 vec3_t fwd;
00476 
00477                 if (other->client)
00478                 {
00479                         AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
00480                 }
00481                 else
00482                 {
00483                         AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
00484                 }
00485 
00486                 G_DeflectMissile(other, ent, fwd);
00487                 G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
00488                 return;
00489         }
00490                 
00491         if (other->takedamage && other->client &&
00492                 ent->s.weapon != WP_ROCKET_LAUNCHER &&
00493                 ent->s.weapon != WP_THERMAL &&
00494                 ent->s.weapon != WP_TRIP_MINE &&
00495                 ent->s.weapon != WP_DET_PACK &&
00496                 ent->s.weapon != WP_DEMP2 &&
00497                 ent->methodOfDeath != MOD_REPEATER_ALT &&
00498                 ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
00499                 ent->methodOfDeath != MOD_CONC &&
00500                 ent->methodOfDeath != MOD_CONC_ALT &&
00501                 other->client->ps.saberBlockTime < level.time &&
00502                 !isKnockedSaber &&
00503                 WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0))
00504         { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked)
00505                 vec3_t fwd;
00506                 gentity_t *te;
00507                 int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE];
00508 
00509                 te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK );
00510                 VectorCopy(ent->r.currentOrigin, te->s.origin);
00511                 VectorCopy(trace->plane.normal, te->s.angles);
00512                 te->s.eventParm = 0;
00513                 te->s.weapon = 0;//saberNum
00514                 te->s.legsAnim = 0;//bladeNum
00515 
00516                 /*if (other->client->ps.velocity[2] > 0 ||
00517                         other->client->pers.cmd.forwardmove ||
00518                         other->client->pers.cmd.rightmove)
00519                         */
00520                 if (other->client->ps.velocity[2] > 0 ||
00521                         other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
00522                 {
00523                         otherDefLevel -= 1;
00524                         if (otherDefLevel < 0)
00525                         {
00526                                 otherDefLevel = 0;
00527                         }
00528                 }
00529 
00530                 AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
00531                 if (otherDefLevel == FORCE_LEVEL_1)
00532                 {
00533                         //if def is only level 1, instead of deflecting the shot it should just die here
00534                 }
00535                 else if (otherDefLevel == FORCE_LEVEL_2)
00536                 {
00537                         G_DeflectMissile(other, ent, fwd);
00538                 }
00539                 else
00540                 {
00541                         G_ReflectMissile(other, ent, fwd);
00542                 }
00543                 other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //200;
00544 
00545                 //For jedi AI
00546                 other->client->ps.saberEventFlags |= SEF_DEFLECTED;
00547 
00548                 if (otherDefLevel == FORCE_LEVEL_3)
00549                 {
00550                         other->client->ps.saberBlockTime = 0; //^_^
00551                 }
00552 
00553                 if (otherDefLevel == FORCE_LEVEL_1)
00554                 {
00555                         goto killProj;
00556                 }
00557                 return;
00558         }
00559         else if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber)
00560         { //hit this person's saber, so..
00561                 gentity_t *otherOwner = &g_entities[other->r.ownerNum];
00562 
00563                 if (otherOwner->takedamage && otherOwner->client &&
00564                         ent->s.weapon != WP_ROCKET_LAUNCHER &&
00565                         ent->s.weapon != WP_THERMAL &&
00566                         ent->s.weapon != WP_TRIP_MINE &&
00567                         ent->s.weapon != WP_DET_PACK &&
00568                         ent->s.weapon != WP_DEMP2 &&
00569                         ent->methodOfDeath != MOD_REPEATER_ALT &&
00570                         ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
00571                         ent->methodOfDeath != MOD_CONC &&
00572                         ent->methodOfDeath != MOD_CONC_ALT /*&&
00573                         otherOwner->client->ps.saberBlockTime < level.time*/)
00574                 { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber
00575                         vec3_t fwd;
00576                         gentity_t *te;
00577                         int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE];
00578 
00579                         //in this case, deflect it even if we can't actually block it because it hit our saber
00580                         //WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0);
00581                         if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0)
00582                         {
00583                                 WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue);
00584                         }
00585 
00586                         te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK );
00587                         VectorCopy(ent->r.currentOrigin, te->s.origin);
00588                         VectorCopy(trace->plane.normal, te->s.angles);
00589                         te->s.eventParm = 0;
00590                         te->s.weapon = 0;//saberNum
00591                         te->s.legsAnim = 0;//bladeNum
00592 
00593                         /*if (otherOwner->client->ps.velocity[2] > 0 ||
00594                                 otherOwner->client->pers.cmd.forwardmove ||
00595                                 otherOwner->client->pers.cmd.rightmove)*/
00596                         if (otherOwner->client->ps.velocity[2] > 0 ||
00597                                 otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
00598                         {
00599                                 otherDefLevel -= 1;
00600                                 if (otherDefLevel < 0)
00601                                 {
00602                                         otherDefLevel = 0;
00603                                 }
00604                         }
00605 
00606                         AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL);
00607 
00608                         if (otherDefLevel == FORCE_LEVEL_1)
00609                         {
00610                                 //if def is only level 1, instead of deflecting the shot it should just die here
00611                         }
00612                         else if (otherDefLevel == FORCE_LEVEL_2)
00613                         {
00614                                 G_DeflectMissile(otherOwner, ent, fwd);
00615                         }
00616                         else
00617                         {
00618                                 G_ReflectMissile(otherOwner, ent, fwd);
00619                         }
00620                         otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100));//200;
00621 
00622                         //For jedi AI
00623                         otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED;
00624 
00625                         if (otherDefLevel == FORCE_LEVEL_3)
00626                         {
00627                                 otherOwner->client->ps.saberBlockTime = 0; //^_^
00628                         }
00629 
00630                         if (otherDefLevel == FORCE_LEVEL_1)
00631                         {
00632                                 goto killProj;
00633                         }
00634                         return;
00635                 }
00636         }
00637 
00638         // check for sticking
00639         if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) ) 
00640         {
00641                 laserTrapStick( ent, trace->endpos, trace->plane.normal );
00642                 G_AddEvent( ent, EV_MISSILE_STICK, 0 );
00643                 return;
00644         }
00645 
00646         // impact damage
00647         if (other->takedamage && !isKnockedSaber) {
00648                 // FIXME: wrong damage direction?
00649                 if ( ent->damage ) {
00650                         vec3_t  velocity;
00651                         qboolean didDmg = qfalse;
00652 
00653                         if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
00654                                 g_entities[ent->r.ownerNum].client->accuracy_hits++;
00655                                 hitClient = qtrue;
00656                         }
00657                         BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
00658                         if ( VectorLength( velocity ) == 0 ) {
00659                                 velocity[2] = 1;        // stepped on a grenade
00660                         }
00661 
00662                         if (ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE ||
00663                                 ent->s.weapon == WP_ROCKET_LAUNCHER)
00664                         {
00665                                 if (ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING))
00666                                 {
00667                                         ent->think(ent);
00668                                 }
00669                                 else
00670                                 {
00671                                         G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
00672                                                 /*ent->s.origin*/ent->r.currentOrigin, ent->damage, 
00673                                                 DAMAGE_HALF_ABSORB, ent->methodOfDeath);
00674                                         didDmg = qtrue;
00675                                 }
00676                         }
00677                         else
00678                         {
00679                                 G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
00680                                         /*ent->s.origin*/ent->r.currentOrigin, ent->damage, 
00681                                         0, ent->methodOfDeath);
00682                                 didDmg = qtrue;
00683                         }
00684 
00685                         if (didDmg && other && other->client)
00686                         { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever.
00687                                 class_t npc_class = other->client->NPC_class;
00688 
00689                                 // If we are a robot and we aren't currently doing the full body electricity...
00690                                 if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE ||
00691                                            npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE ||
00692                                            npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd
00693                                            npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY )
00694                                 {
00695                                         // special droid only behaviors
00696                                         if ( other->client->ps.electrifyTime < level.time + 100 )
00697                                         {
00698                                                 // ... do the effect for a split second for some more feedback
00699                                                 other->client->ps.electrifyTime = level.time + 450;
00700                                         }
00701                                         //FIXME: throw some sparks off droids,too
00702                                 }
00703                         }
00704                 }
00705 
00706                 if ( ent->s.weapon == WP_DEMP2 )
00707                 {//a hit with demp2 decloaks people, disables ships
00708                         if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE )
00709                         {//hit a vehicle
00710                                 if ( other->m_pVehicle //valid vehicle ent
00711                                         && other->m_pVehicle->m_pVehicleInfo//valid stats
00712                                         && (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders
00713                                                 ||(other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp("vehicle_proj", ent->classname ) == 0) )//only vehicle ion weapons affect a fighter in this manner
00714                                         && !FighterIsLanded( other->m_pVehicle , &other->client->ps )//not landed
00715                                         && !(other->spawnflags&2) )//and not suspended
00716                                 {//vehicles hit by "ion cannons" lose control
00717                                         if ( other->client->ps.electrifyTime > level.time )
00718                                         {//add onto it
00719                                                 //FIXME: extern the length of the "out of control" time?
00720                                                 other->client->ps.electrifyTime += Q_irand(200,500);
00721                                                 if ( other->client->ps.electrifyTime > level.time + 4000 )
00722                                                 {//cap it
00723                                                         other->client->ps.electrifyTime = level.time + 4000;
00724                                                 }
00725                                         }
00726                                         else
00727                                         {//start it
00728                                                 //FIXME: extern the length of the "out of control" time?
00729                                                 other->client->ps.electrifyTime = level.time + Q_irand(200,500);
00730                                         }
00731                                 }
00732                         }
00733                         else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] )
00734                         {
00735                                 Jedi_Decloak( other );
00736                                 if ( ent->methodOfDeath == MOD_DEMP2_ALT )
00737                                 {//direct hit with alt disables cloak forever
00738                                         //permanently disable the saboteur's cloak
00739                                         other->client->cloakToggleTime = Q3_INFINITE;
00740                                 }
00741                                 else
00742                                 {//temp disable
00743                                         other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
00744                                 }
00745                         }
00746                 }
00747         }
00748 killProj:
00749         // is it cheaper in bandwidth to just remove this ent and create a new
00750         // one, rather than changing the missile into the explosion?
00751 
00752         if ( other->takedamage && other->client && !isKnockedSaber ) {
00753                 G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
00754                 ent->s.otherEntityNum = other->s.number;
00755         } else if( trace->surfaceFlags & SURF_METALSTEPS ) {
00756                 G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
00757         } else if (ent->s.weapon != G2_MODEL_PART && !isKnockedSaber) {
00758                 G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
00759         }
00760 
00761         if (!isKnockedSaber)
00762         {
00763                 ent->freeAfterEvent = qtrue;
00764 
00765                 // change over to a normal entity right at the point of impact
00766                 ent->s.eType = ET_GENERAL;
00767         }
00768 
00769         SnapVectorTowards( trace->endpos, ent->s.pos.trBase );  // save net bandwidth
00770 
00771         G_SetOrigin( ent, trace->endpos );
00772 
00773         ent->takedamage = qfalse;
00774         // splash damage (doesn't apply to person directly hit)
00775         if ( ent->splashDamage ) {
00776                 if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, 
00777                         other, ent, ent->splashMethodOfDeath ) ) {
00778                         if( !hitClient 
00779                                 && g_entities[ent->r.ownerNum].client ) {
00780                                 g_entities[ent->r.ownerNum].client->accuracy_hits++;
00781                         }
00782                 }
00783         }
00784 
00785         if (ent->s.weapon == G2_MODEL_PART)
00786         {
00787                 ent->freeAfterEvent = qfalse; //it will free itself
00788         }
00789 
00790         trap_LinkEntity( ent );
00791 }
00792 
00793 /*
00794 ================
00795 G_RunMissile
00796 ================
00797 */
00798 void G_RunMissile( gentity_t *ent ) {
00799         vec3_t          origin, groundSpot;
00800         trace_t         tr;
00801         int                     passent;
00802         qboolean        isKnockedSaber = qfalse;
00803 
00804         if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF))
00805         {
00806                 isKnockedSaber = qtrue;
00807                 ent->s.pos.trType = TR_GRAVITY;
00808         }
00809 
00810         // get current position
00811         BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
00812 
00813         // if this missile bounced off an invulnerability sphere
00814         if ( ent->target_ent ) {
00815                 passent = ent->target_ent->s.number;
00816         }
00817         else {
00818                 // ignore interactions with the missile owner
00819                 if ( (ent->r.svFlags&SVF_OWNERNOTSHARED) 
00820                         && (ent->s.eFlags&EF_JETPACK_ACTIVE) )
00821                 {//A vehicle missile that should be solid to its owner
00822                         //I don't care about hitting my owner
00823                         passent = ent->s.number;
00824                 }
00825                 else
00826                 {
00827                         passent = ent->r.ownerNum;
00828                 }
00829         }
00830         // trace a line from the previous position to the current position
00831         if (d_projectileGhoul2Collision.integer)
00832         {
00833                 trap_G2Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
00834 
00835                 if (tr.fraction != 1.0 && tr.entityNum < ENTITYNUM_WORLD)
00836                 {
00837                         gentity_t *g2Hit = &g_entities[tr.entityNum];
00838 
00839                         if (g2Hit->inuse && g2Hit->client && g2Hit->ghoul2)
00840                         { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with.
00841                                 g2Hit->client->g2LastSurfaceHit = tr.surfaceFlags;
00842                                 g2Hit->client->g2LastSurfaceTime = level.time;
00843                         }
00844 
00845                         if (g2Hit->ghoul2)
00846                         {
00847                                 tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here.
00848                         }
00849                 }
00850         }
00851         else
00852         {
00853                 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
00854         }
00855 
00856         if ( tr.startsolid || tr.allsolid ) {
00857                 // make sure the tr.entityNum is set to the entity we're stuck in
00858                 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
00859                 tr.fraction = 0;
00860         }
00861         else {
00862                 VectorCopy( tr.endpos, ent->r.currentOrigin );
00863         }
00864 
00865         if (ent->passThroughNum && tr.entityNum == (ent->passThroughNum-1))
00866         {
00867                 VectorCopy( origin, ent->r.currentOrigin );
00868                 trap_LinkEntity( ent );
00869                 goto passthrough;
00870         }
00871 
00872         trap_LinkEntity( ent );
00873 
00874         if (ent->s.weapon == G2_MODEL_PART && !ent->bounceCount)
00875         {
00876                 vec3_t lowerOrg;
00877                 trace_t trG;
00878 
00879                 VectorCopy(ent->r.currentOrigin, lowerOrg);
00880                 lowerOrg[2] -= 1;
00881                 trap_Trace( &trG, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, lowerOrg, passent, ent->clipmask );
00882 
00883                 VectorCopy(trG.endpos, groundSpot);
00884 
00885                 if (!trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD)
00886                 {
00887                         ent->s.groundEntityNum = trG.entityNum;
00888                 }
00889                 else
00890                 {
00891                         ent->s.groundEntityNum = ENTITYNUM_NONE;
00892                 }
00893         }
00894 
00895         if ( tr.fraction != 1) {
00896                 // never explode or bounce on sky
00897                 if ( tr.surfaceFlags & SURF_NOIMPACT ) {
00898                         // If grapple, reset owner
00899                         if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) {
00900                                 ent->parent->client->hook = NULL;
00901                         }
00902 
00903                         if ((ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber)
00904                         {
00905                                 G_RunThink( ent );
00906                                 return;
00907                         }
00908                         else if (ent->s.weapon != G2_MODEL_PART)
00909                         {
00910                                 G_FreeEntity( ent );
00911                                 return;
00912                         }
00913                 }
00914 
00915 #if 0 //will get stomped with missile impact event...
00916                 if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS &&
00917                         (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC))
00918                 { //player or NPC, try making a mark on him
00919                         /*
00920                         gentity_t *evEnt = G_TempEntity(ent->r.currentOrigin, EV_GHOUL2_MARK);
00921 
00922                         evEnt->s.owner = tr.entityNum; //the entity the mark should be placed on
00923                         evEnt->s.weapon = ent->s.weapon; //the weapon used (to determine mark type)
00924                         VectorCopy(ent->r.currentOrigin, evEnt->s.origin); //the point of impact
00925 
00926                         //origin2 gets the predicted trajectory-based position.
00927                         BG_EvaluateTrajectory( &ent->s.pos, level.time, evEnt->s.origin2 );
00928 
00929                         //If they are the same, there will be problems.
00930                         if (VectorCompare(evEnt->s.origin, evEnt->s.origin2))
00931                         {
00932                                 evEnt->s.origin2[2] += 2; //whatever, at least it won't mess up.
00933                         }
00934                         */
00935                         //ok, let's try adding it to the missile ent instead (tempents bad!)
00936                         G_AddEvent(ent, EV_GHOUL2_MARK, 0);
00937 
00938                         //copy current pos to s.origin, and current projected traj to origin2
00939                         VectorCopy(ent->r.currentOrigin, ent->s.origin);
00940                         BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 );
00941 
00942                         //the index for whoever we are hitting
00943                         ent->s.otherEntityNum = tr.entityNum;
00944 
00945                         if (VectorCompare(ent->s.origin, ent->s.origin2))
00946                         {
00947                                 ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up.
00948                         }
00949                 }
00950 #else
00951                 if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS &&
00952                         (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC))
00953                 { //player or NPC, try making a mark on him
00954                         //copy current pos to s.origin, and current projected traj to origin2
00955                         VectorCopy(ent->r.currentOrigin, ent->s.origin);
00956                         BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 );
00957 
00958                         if (VectorCompare(ent->s.origin, ent->s.origin2))
00959                         {
00960                                 ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up.
00961                         }
00962                 }
00963 #endif
00964 
00965                 G_MissileImpact( ent, &tr );
00966 
00967                 if (tr.entityNum == ent->s.otherEntityNum)
00968                 { //if the impact event other and the trace ent match then it's ok to do the g2 mark
00969                         ent->s.trickedentindex = 1;
00970                 }
00971 
00972                 if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART )
00973                 {
00974                         return;         // exploded
00975                 }
00976         }
00977 
00978 passthrough:
00979         if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) )
00980         {//stuck missiles should check some special stuff
00981                 G_RunStuckMissile( ent );
00982                 return;
00983         }
00984 
00985         if (ent->s.weapon == G2_MODEL_PART)
00986         {
00987                 if (ent->s.groundEntityNum == ENTITYNUM_WORLD)
00988                 {
00989                         ent->s.pos.trType = TR_LINEAR;
00990                         VectorClear(ent->s.pos.trDelta);
00991                         ent->s.pos.trTime = level.time;
00992 
00993                         VectorCopy(groundSpot, ent->s.pos.trBase);
00994                         VectorCopy(groundSpot, ent->r.currentOrigin);
00995 
00996                         if (ent->s.apos.trType != TR_STATIONARY)
00997                         {
00998                                 ent->s.apos.trType = TR_STATIONARY;
00999                                 ent->s.apos.trTime = level.time;
01000 
01001                                 ent->s.apos.trBase[ROLL] = 0;
01002                                 ent->s.apos.trBase[PITCH] = 0;
01003                         }
01004                 }
01005         }
01006 
01007         // check think function after bouncing
01008         G_RunThink( ent );
01009 }
01010 
01011 
01012 //=============================================================================
01013 
01014 
01015 
01016