codemp/game/NPC_AI_Interrogator.c

Go to the documentation of this file.
00001 #include "b_local.h"
00002 #include "g_nav.h"
00003 
00004 void Interrogator_Idle( void );
00005 void DeathFX( gentity_t *ent );
00006 extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
00007 
00008 enum
00009 {
00010 LSTATE_BLADESTOP=0,
00011 LSTATE_BLADEUP,
00012 LSTATE_BLADEDOWN,
00013 };
00014 
00015 /*
00016 -------------------------
00017 NPC_Interrogator_Precache
00018 -------------------------
00019 */
00020 void NPC_Interrogator_Precache(gentity_t *self)
00021 {
00022         G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_lp" );
00023         G_SoundIndex("sound/chars/mark1/misc/anger.wav");
00024         G_SoundIndex( "sound/chars/probe/misc/talk");
00025         G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_inject" );
00026         G_SoundIndex( "sound/chars/interrogator/misc/int_droid_explo" );
00027         G_EffectIndex( "explosions/droidexplosion1" );
00028 }
00029 /*
00030 -------------------------
00031 Interrogator_die
00032 -------------------------
00033 */
00034 void Interrogator_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc )
00035 {
00036         self->client->ps.velocity[2] = -100;
00037         /*
00038         self->locationDamage[HL_NONE] += damage;
00039         if (self->locationDamage[HL_NONE] > 40)
00040         {
00041                 DeathFX(self);
00042                 self->client->ps.eFlags |= EF_NODRAW;
00043                 self->contents = CONTENTS_CORPSE;
00044         }
00045         else
00046         */
00047         {
00048                 self->client->ps.eFlags2 &= ~EF2_FLYING;//moveType = MT_WALK;
00049                 self->client->ps.velocity[0] = Q_irand( -10, -20 );
00050                 self->client->ps.velocity[1] = Q_irand( -10, -20 );
00051                 self->client->ps.velocity[2] = -100;
00052         }
00053         //self->takedamage = qfalse;
00054         //self->client->ps.eFlags |= EF_NODRAW;
00055         //self->contents = 0;
00056         return;
00057 }
00058 
00059 /*
00060 -------------------------
00061 Interrogator_PartsMove
00062 -------------------------
00063 */
00064 void Interrogator_PartsMove(void)
00065 {
00066         // Syringe
00067         if ( TIMER_Done(NPC,"syringeDelay") )
00068         {
00069                 NPC->pos1[1] = AngleNormalize360( NPC->pos1[1]);
00070 
00071                 if ((NPC->pos1[1] < 60) || (NPC->pos1[1] > 300))
00072                 {
00073                         NPC->pos1[1]+=Q_irand( -20, 20 );       // Pitch        
00074                 }
00075                 else if (NPC->pos1[1] > 180)
00076                 {
00077                         NPC->pos1[1]=Q_irand( 300, 360 );       // Pitch        
00078                 }
00079                 else 
00080                 {
00081                         NPC->pos1[1]=Q_irand( 0, 60 );  // Pitch        
00082                 }
00083 
00084         //      gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone1, NPC->pos1, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); 
00085                 NPC_SetBoneAngles(NPC, "left_arm", NPC->pos1);
00086 
00087                 TIMER_Set( NPC, "syringeDelay", Q_irand( 100, 1000 ) );
00088         }
00089 
00090         // Scalpel
00091         if ( TIMER_Done(NPC,"scalpelDelay") )
00092         {
00093                 // Change pitch
00094                 if ( NPCInfo->localState == LSTATE_BLADEDOWN )  // Blade is moving down
00095                 {
00096                         NPC->pos2[0]-= 30;
00097                         if (NPC->pos2[0] < 180)
00098                         {
00099                                 NPC->pos2[0] = 180;
00100                                 NPCInfo->localState = LSTATE_BLADEUP;   // Make it move up
00101                         }
00102                 }
00103                 else                                                                                    // Blade is coming back up
00104                 {
00105                         NPC->pos2[0]+= 30;
00106                         if (NPC->pos2[0] >= 360)
00107                         {
00108                                 NPC->pos2[0] = 360;
00109                                 NPCInfo->localState = LSTATE_BLADEDOWN; // Make it move down
00110                                 TIMER_Set( NPC, "scalpelDelay", Q_irand( 100, 1000 ) );
00111                         }
00112                 }
00113 
00114                 NPC->pos2[0] = AngleNormalize360( NPC->pos2[0]);
00115         //      gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone2, NPC->pos2, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); 
00116 
00117                 NPC_SetBoneAngles(NPC, "right_arm", NPC->pos2);
00118         }
00119 
00120         // Claw
00121         NPC->pos3[1] += Q_irand( 10, 30 );
00122         NPC->pos3[1] = AngleNormalize360( NPC->pos3[1]);
00123         //gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone3, NPC->pos3, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); 
00124 
00125         NPC_SetBoneAngles(NPC, "claw", NPC->pos3);
00126 
00127 }
00128 
00129 #define VELOCITY_DECAY  0.85f
00130 #define HUNTER_UPWARD_PUSH      2
00131 
00132 /*
00133 -------------------------
00134 Interrogator_MaintainHeight
00135 -------------------------
00136 */
00137 void Interrogator_MaintainHeight( void )
00138 {       
00139         float   dif;
00140 //      vec3_t  endPos;
00141 //      trace_t trace;
00142 
00143         NPC->s.loopSound = G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_lp" );
00144         // Update our angles regardless
00145         NPC_UpdateAngles( qtrue, qtrue );
00146 
00147         // If we have an enemy, we should try to hover at about enemy eye level
00148         if ( NPC->enemy )
00149         {
00150                 // Find the height difference
00151                 dif = (NPC->enemy->r.currentOrigin[2] + NPC->enemy->r.maxs[2]) - NPC->r.currentOrigin[2]; 
00152 
00153                 // cap to prevent dramatic height shifts
00154                 if ( fabs( dif ) > 2 )
00155                 {
00156                         if ( fabs( dif ) > 16 )
00157                         {
00158                                 dif = ( dif < 0 ? -16 : 16 );
00159                         }
00160 
00161                         NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
00162                 }
00163         }
00164         else
00165         {
00166                 gentity_t *goal = NULL;
00167 
00168                 if ( NPCInfo->goalEntity )      // Is there a goal?
00169                 {
00170                         goal = NPCInfo->goalEntity;
00171                 }
00172                 else
00173                 {
00174                         goal = NPCInfo->lastGoalEntity;
00175                 }
00176                 if ( goal )
00177                 {
00178                         dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
00179 
00180                         if ( fabs( dif ) > 24 )
00181                         {
00182                                 ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
00183                         }
00184                         else
00185                         {
00186                                 if ( NPC->client->ps.velocity[2] )
00187                                 {
00188                                         NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
00189 
00190                                         if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
00191                                         {
00192                                                 NPC->client->ps.velocity[2] = 0;
00193                                         }
00194                                 }
00195                         }
00196                 }
00197                 // Apply friction
00198                 else if ( NPC->client->ps.velocity[2] )
00199                 {
00200                         NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
00201 
00202                         if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
00203                         {
00204                                 NPC->client->ps.velocity[2] = 0;
00205                         }
00206                 }
00207         }
00208 
00209         // Apply friction
00210         if ( NPC->client->ps.velocity[0] )
00211         {
00212                 NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
00213 
00214                 if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
00215                 {
00216                         NPC->client->ps.velocity[0] = 0;
00217                 }
00218         }
00219 
00220         if ( NPC->client->ps.velocity[1] )
00221         {
00222                 NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
00223 
00224                 if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
00225                 {
00226                         NPC->client->ps.velocity[1] = 0;
00227                 }
00228         }
00229 }
00230 
00231 #define HUNTER_STRAFE_VEL       32
00232 #define HUNTER_STRAFE_DIS       200
00233 /*
00234 -------------------------
00235 Interrogator_Strafe
00236 -------------------------
00237 */
00238 void Interrogator_Strafe( void )
00239 {
00240         int             dir;
00241         vec3_t  end, right;
00242         trace_t tr;
00243         float   dif;
00244 
00245         AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
00246 
00247         // Pick a random strafe direction, then check to see if doing a strafe would be
00248         //      reasonable valid
00249         dir = ( rand() & 1 ) ? -1 : 1;
00250         VectorMA( NPC->r.currentOrigin, HUNTER_STRAFE_DIS * dir, right, end );
00251 
00252         trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
00253 
00254         // Close enough
00255         if ( tr.fraction > 0.9f )
00256         {
00257                 VectorMA( NPC->client->ps.velocity, HUNTER_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
00258 
00259                 // Add a slight upward push
00260                 if ( NPC->enemy )
00261                 {
00262                         // Find the height difference
00263                         dif = (NPC->enemy->r.currentOrigin[2] + 32) - NPC->r.currentOrigin[2]; 
00264 
00265                         // cap to prevent dramatic height shifts
00266                         if ( fabs( dif ) > 8 )
00267                         {
00268                                 dif = ( dif < 0 ? -HUNTER_UPWARD_PUSH : HUNTER_UPWARD_PUSH );
00269                         }
00270 
00271                         NPC->client->ps.velocity[2] += dif;
00272 
00273                 }
00274 
00275                 // Set the strafe start time 
00276                 //NPC->fx_time = level.time;
00277                 NPCInfo->standTime = level.time + 3000 + random() * 500;
00278         }
00279 }
00280 
00281 /*
00282 -------------------------
00283 Interrogator_Hunt
00284 -------------------------`
00285 */
00286 
00287 #define HUNTER_FORWARD_BASE_SPEED       10
00288 #define HUNTER_FORWARD_MULTIPLIER       2
00289 
00290 void Interrogator_Hunt( qboolean visible, qboolean advance )
00291 {
00292         float   distance, speed;
00293         vec3_t  forward;
00294 
00295         Interrogator_PartsMove();
00296 
00297         NPC_FaceEnemy(qfalse);
00298 
00299         //If we're not supposed to stand still, pursue the player
00300         if ( NPCInfo->standTime < level.time )
00301         {
00302                 // Only strafe when we can see the player
00303                 if ( visible )
00304                 {
00305                         Interrogator_Strafe();
00306                         if ( NPCInfo->standTime > level.time )
00307                         {//successfully strafed
00308                                 return;
00309                         }
00310                 }
00311         }
00312 
00313         //If we don't want to advance, stop here
00314         if ( advance == qfalse )
00315                 return;
00316 
00317         //Only try and navigate if the player is visible
00318         if ( visible == qfalse )
00319         {
00320                 // Move towards our goal
00321                 NPCInfo->goalEntity = NPC->enemy;
00322                 NPCInfo->goalRadius = 12;
00323 
00324                 //Get our direction from the navigator if we can't see our target
00325                 if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
00326                         return;
00327         }
00328         else
00329         {
00330                 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
00331                 distance = VectorNormalize( forward );
00332         }
00333 
00334         speed = HUNTER_FORWARD_BASE_SPEED + HUNTER_FORWARD_MULTIPLIER * g_spskill.integer;
00335         VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
00336 }
00337 
00338 #define MIN_DISTANCE            64
00339 
00340 /*
00341 -------------------------
00342 Interrogator_Melee
00343 -------------------------
00344 */
00345 void Interrogator_Melee( qboolean visible, qboolean advance )
00346 {
00347         if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
00348         {
00349                 // Make sure that we are within the height range before we allow any damage to happen
00350                 if ( NPC->r.currentOrigin[2] >= NPC->enemy->r.currentOrigin[2]+NPC->enemy->r.mins[2] && NPC->r.currentOrigin[2]+NPC->r.mins[2]+8 < NPC->enemy->r.currentOrigin[2]+NPC->enemy->r.maxs[2] )
00351                 {
00352                         //gentity_t *tent;
00353 
00354                         TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
00355                         G_Damage( NPC->enemy, NPC, NPC, 0, 0, 2, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
00356 
00357                 //      NPC->enemy->client->poisonDamage = 18;
00358                 //      NPC->enemy->client->poisonTime = level.time + 1000;
00359 
00360                         // Drug our enemy up and do the wonky vision thing
00361 //                      tent = G_TempEntity( NPC->enemy->r.currentOrigin, EV_DRUGGED );
00362 //                      tent->owner = NPC->enemy;
00363 
00364                         //rwwFIXMEFIXME: poison damage
00365 
00366                         G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_inject.mp3" ));
00367                 }
00368         }
00369 
00370         if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00371         {
00372                 Interrogator_Hunt( visible, advance );
00373         }
00374 }
00375 
00376 /*
00377 -------------------------
00378 Interrogator_Attack
00379 -------------------------
00380 */
00381 void Interrogator_Attack( void )
00382 {
00383         float           distance;       
00384         qboolean        visible;
00385         qboolean        advance;
00386 
00387         // Always keep a good height off the ground
00388         Interrogator_MaintainHeight();
00389 
00390         //randomly talk
00391         if ( TIMER_Done(NPC,"patrolNoise") )
00392         {
00393                 if (TIMER_Done(NPC,"angerNoise"))
00394                 {
00395                         G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/talk.wav",     Q_irand(1, 3)) );
00396 
00397                         TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
00398                 }
00399         }
00400 
00401         // If we don't have an enemy, just idle
00402         if ( NPC_CheckEnemyExt(qfalse) == qfalse )
00403         {
00404                 Interrogator_Idle();
00405                 return;
00406         }
00407 
00408         // Rate our distance to the target, and our visibilty
00409         distance        = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); 
00410         visible         = NPC_ClearLOS4( NPC->enemy );
00411         advance         = (qboolean)(distance > MIN_DISTANCE*MIN_DISTANCE );
00412 
00413         if ( !visible )
00414         {
00415                 advance = qtrue;
00416         }
00417         if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00418         {
00419                 Interrogator_Hunt( visible, advance );
00420         }
00421 
00422         NPC_FaceEnemy( qtrue );
00423 
00424         if (!advance)
00425         {
00426                 Interrogator_Melee( visible, advance );
00427         }
00428 }
00429 
00430 /*
00431 -------------------------
00432 Interrogator_Idle
00433 -------------------------
00434 */
00435 void Interrogator_Idle( void )
00436 {
00437         if ( NPC_CheckPlayerTeamStealth() )
00438         {
00439                 G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/mark1/misc/anger.wav" );
00440                 NPC_UpdateAngles( qtrue, qtrue );
00441                 return;
00442         }
00443 
00444         Interrogator_MaintainHeight();
00445 
00446         NPC_BSIdle();
00447 }
00448 
00449 /*
00450 -------------------------
00451 NPC_BSInterrogator_Default
00452 -------------------------
00453 */
00454 void NPC_BSInterrogator_Default( void )
00455 {
00456         //NPC->e_DieFunc = dieF_Interrogator_die;
00457 
00458         if ( NPC->enemy )
00459         {
00460                 Interrogator_Attack();
00461         }
00462         else
00463         {
00464                 Interrogator_Idle();
00465         }
00466 
00467 }