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
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
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
00039
00040
00041
00042
00043
00044
00045
00046
00047 {
00048 self->client->ps.eFlags2 &= ~EF2_FLYING;
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
00054
00055
00056 return;
00057 }
00058
00059
00060
00061
00062
00063
00064 void Interrogator_PartsMove(void)
00065 {
00066
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 );
00074 }
00075 else if (NPC->pos1[1] > 180)
00076 {
00077 NPC->pos1[1]=Q_irand( 300, 360 );
00078 }
00079 else
00080 {
00081 NPC->pos1[1]=Q_irand( 0, 60 );
00082 }
00083
00084
00085 NPC_SetBoneAngles(NPC, "left_arm", NPC->pos1);
00086
00087 TIMER_Set( NPC, "syringeDelay", Q_irand( 100, 1000 ) );
00088 }
00089
00090
00091 if ( TIMER_Done(NPC,"scalpelDelay") )
00092 {
00093
00094 if ( NPCInfo->localState == LSTATE_BLADEDOWN )
00095 {
00096 NPC->pos2[0]-= 30;
00097 if (NPC->pos2[0] < 180)
00098 {
00099 NPC->pos2[0] = 180;
00100 NPCInfo->localState = LSTATE_BLADEUP;
00101 }
00102 }
00103 else
00104 {
00105 NPC->pos2[0]+= 30;
00106 if (NPC->pos2[0] >= 360)
00107 {
00108 NPC->pos2[0] = 360;
00109 NPCInfo->localState = LSTATE_BLADEDOWN;
00110 TIMER_Set( NPC, "scalpelDelay", Q_irand( 100, 1000 ) );
00111 }
00112 }
00113
00114 NPC->pos2[0] = AngleNormalize360( NPC->pos2[0]);
00115
00116
00117 NPC_SetBoneAngles(NPC, "right_arm", NPC->pos2);
00118 }
00119
00120
00121 NPC->pos3[1] += Q_irand( 10, 30 );
00122 NPC->pos3[1] = AngleNormalize360( NPC->pos3[1]);
00123
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
00135
00136
00137 void Interrogator_MaintainHeight( void )
00138 {
00139 float dif;
00140
00141
00142
00143 NPC->s.loopSound = G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_lp" );
00144
00145 NPC_UpdateAngles( qtrue, qtrue );
00146
00147
00148 if ( NPC->enemy )
00149 {
00150
00151 dif = (NPC->enemy->r.currentOrigin[2] + NPC->enemy->r.maxs[2]) - NPC->r.currentOrigin[2];
00152
00153
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 )
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
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
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
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
00248
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
00255 if ( tr.fraction > 0.9f )
00256 {
00257 VectorMA( NPC->client->ps.velocity, HUNTER_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
00258
00259
00260 if ( NPC->enemy )
00261 {
00262
00263 dif = (NPC->enemy->r.currentOrigin[2] + 32) - NPC->r.currentOrigin[2];
00264
00265
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
00276
00277 NPCInfo->standTime = level.time + 3000 + random() * 500;
00278 }
00279 }
00280
00281
00282
00283
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
00300 if ( NPCInfo->standTime < level.time )
00301 {
00302
00303 if ( visible )
00304 {
00305 Interrogator_Strafe();
00306 if ( NPCInfo->standTime > level.time )
00307 {
00308 return;
00309 }
00310 }
00311 }
00312
00313
00314 if ( advance == qfalse )
00315 return;
00316
00317
00318 if ( visible == qfalse )
00319 {
00320
00321 NPCInfo->goalEntity = NPC->enemy;
00322 NPCInfo->goalRadius = 12;
00323
00324
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
00343
00344
00345 void Interrogator_Melee( qboolean visible, qboolean advance )
00346 {
00347 if ( TIMER_Done( NPC, "attackDelay" ) )
00348 {
00349
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
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
00358
00359
00360
00361
00362
00363
00364
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
00379
00380
00381 void Interrogator_Attack( void )
00382 {
00383 float distance;
00384 qboolean visible;
00385 qboolean advance;
00386
00387
00388 Interrogator_MaintainHeight();
00389
00390
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
00402 if ( NPC_CheckEnemyExt(qfalse) == qfalse )
00403 {
00404 Interrogator_Idle();
00405 return;
00406 }
00407
00408
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
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
00452
00453
00454 void NPC_BSInterrogator_Default( void )
00455 {
00456
00457
00458 if ( NPC->enemy )
00459 {
00460 Interrogator_Attack();
00461 }
00462 else
00463 {
00464 Interrogator_Idle();
00465 }
00466
00467 }