00001 #include "b_local.h"
00002 #include "g_nav.h"
00003
00004 #include "../namespace_begin.h"
00005 gitem_t *BG_FindItemForAmmo( ammo_t ammo );
00006 #include "../namespace_end.h"
00007 extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
00008
00009
00010 enum
00011 {
00012 LSTATE_NONE = 0,
00013 LSTATE_BACKINGUP,
00014 LSTATE_SPINNING,
00015 LSTATE_PAIN,
00016 LSTATE_DROP
00017 };
00018
00019 void ImperialProbe_Idle( void );
00020
00021 void NPC_Probe_Precache(void)
00022 {
00023 int i;
00024
00025 for ( i = 1; i < 4; i++)
00026 {
00027 G_SoundIndex( va( "sound/chars/probe/misc/probetalk%d", i ) );
00028 }
00029 G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
00030 G_SoundIndex("sound/chars/probe/misc/anger1");
00031 G_SoundIndex("sound/chars/probe/misc/fire");
00032
00033 G_EffectIndex( "chunks/probehead" );
00034 G_EffectIndex( "env/med_explode2" );
00035 G_EffectIndex( "explosions/probeexplosion1");
00036 G_EffectIndex( "bryar/muzzle_flash" );
00037
00038 RegisterItem( BG_FindItemForAmmo( AMMO_BLASTER ));
00039 RegisterItem( BG_FindItemForWeapon( WP_BRYAR_PISTOL ) );
00040 }
00041
00042
00043
00044
00045
00046
00047 #define VELOCITY_DECAY 0.85f
00048
00049 void ImperialProbe_MaintainHeight( void )
00050 {
00051 float dif;
00052
00053
00054
00055
00056 NPC_UpdateAngles( qtrue, qtrue );
00057
00058
00059 if ( NPC->enemy )
00060 {
00061
00062 dif = NPC->enemy->r.currentOrigin[2] - NPC->r.currentOrigin[2];
00063
00064
00065 if ( fabs( dif ) > 8 )
00066 {
00067 if ( fabs( dif ) > 16 )
00068 {
00069 dif = ( dif < 0 ? -16 : 16 );
00070 }
00071
00072 NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
00073 }
00074 }
00075 else
00076 {
00077 gentity_t *goal = NULL;
00078
00079 if ( NPCInfo->goalEntity )
00080 {
00081 goal = NPCInfo->goalEntity;
00082 }
00083 else
00084 {
00085 goal = NPCInfo->lastGoalEntity;
00086 }
00087 if ( goal )
00088 {
00089 dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
00090
00091 if ( fabs( dif ) > 24 )
00092 {
00093 ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
00094 }
00095 else
00096 {
00097 if ( NPC->client->ps.velocity[2] )
00098 {
00099 NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
00100
00101 if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
00102 {
00103 NPC->client->ps.velocity[2] = 0;
00104 }
00105 }
00106 }
00107 }
00108
00109 else if ( NPC->client->ps.velocity[2] )
00110 {
00111 NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
00112
00113 if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
00114 {
00115 NPC->client->ps.velocity[2] = 0;
00116 }
00117 }
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148 }
00149
00150
00151 if ( NPC->client->ps.velocity[0] )
00152 {
00153 NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
00154
00155 if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
00156 {
00157 NPC->client->ps.velocity[0] = 0;
00158 }
00159 }
00160
00161 if ( NPC->client->ps.velocity[1] )
00162 {
00163 NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
00164
00165 if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
00166 {
00167 NPC->client->ps.velocity[1] = 0;
00168 }
00169 }
00170 }
00171
00172
00173
00174
00175
00176
00177
00178 #define HUNTER_STRAFE_VEL 256
00179 #define HUNTER_STRAFE_DIS 200
00180 #define HUNTER_UPWARD_PUSH 32
00181
00182 void ImperialProbe_Strafe( void )
00183 {
00184 int dir;
00185 vec3_t end, right;
00186 trace_t tr;
00187
00188 AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
00189
00190
00191
00192 dir = ( rand() & 1 ) ? -1 : 1;
00193 VectorMA( NPC->r.currentOrigin, HUNTER_STRAFE_DIS * dir, right, end );
00194
00195 trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
00196
00197
00198 if ( tr.fraction > 0.9f )
00199 {
00200 VectorMA( NPC->client->ps.velocity, HUNTER_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
00201
00202
00203 NPC->client->ps.velocity[2] += HUNTER_UPWARD_PUSH;
00204
00205
00206
00207 NPCInfo->standTime = level.time + 3000 + random() * 500;
00208 }
00209 }
00210
00211
00212
00213
00214
00215
00216
00217 #define HUNTER_FORWARD_BASE_SPEED 10
00218 #define HUNTER_FORWARD_MULTIPLIER 5
00219
00220 void ImperialProbe_Hunt( qboolean visible, qboolean advance )
00221 {
00222 float distance, speed;
00223 vec3_t forward;
00224
00225 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00226
00227
00228 if ( NPCInfo->standTime < level.time )
00229 {
00230
00231 if ( visible )
00232 {
00233 ImperialProbe_Strafe();
00234 return;
00235 }
00236 }
00237
00238
00239 if ( advance == qfalse )
00240 return;
00241
00242
00243 if ( visible == qfalse )
00244 {
00245
00246 NPCInfo->goalEntity = NPC->enemy;
00247 NPCInfo->goalRadius = 12;
00248
00249
00250 if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
00251 return;
00252 }
00253 else
00254 {
00255 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
00256 distance = VectorNormalize( forward );
00257 }
00258
00259 speed = HUNTER_FORWARD_BASE_SPEED + HUNTER_FORWARD_MULTIPLIER * g_spskill.integer;
00260 VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
00261 }
00262
00263
00264
00265
00266
00267
00268 void ImperialProbe_FireBlaster(void)
00269 {
00270 vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1;
00271 static vec3_t forward, vright, up;
00272 static vec3_t muzzle;
00273 int genBolt1;
00274 gentity_t *missile;
00275 mdxaBone_t boltMatrix;
00276
00277 genBolt1 = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash");
00278
00279
00280 trap_G2API_GetBoltMatrix( NPC->ghoul2, 0,
00281 genBolt1,
00282 &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time,
00283 NULL, NPC->modelScale );
00284
00285 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 );
00286
00287 G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, vec3_origin );
00288
00289 G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/probe/misc/fire" ));
00290
00291 if (NPC->health)
00292 {
00293 CalcEntitySpot( NPC->enemy, SPOT_CHEST, enemy_org1 );
00294 enemy_org1[0]+= Q_irand(0,10);
00295 enemy_org1[1]+= Q_irand(0,10);
00296 VectorSubtract (enemy_org1, muzzle1, delta1);
00297 vectoangles ( delta1, angleToEnemy1 );
00298 AngleVectors (angleToEnemy1, forward, vright, up);
00299 }
00300 else
00301 {
00302 AngleVectors (NPC->r.currentAngles, forward, vright, up);
00303 }
00304
00305 missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC, qfalse );
00306
00307 missile->classname = "bryar_proj";
00308 missile->s.weapon = WP_BRYAR_PISTOL;
00309
00310 if ( g_spskill.integer <= 1 )
00311 {
00312 missile->damage = 5;
00313 }
00314 else
00315 {
00316 missile->damage = 10;
00317 }
00318
00319
00320 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00321 missile->methodOfDeath = MOD_UNKNOWN;
00322 missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00323
00324 }
00325
00326
00327
00328
00329
00330
00331 void ImperialProbe_Ranged( qboolean visible, qboolean advance )
00332 {
00333 int delay_min,delay_max;
00334
00335 if ( TIMER_Done( NPC, "attackDelay" ) )
00336 {
00337
00338 if ( g_spskill.integer == 0 )
00339 {
00340 delay_min = 500;
00341 delay_max = 3000;
00342 }
00343 else if ( g_spskill.integer > 1 )
00344 {
00345 delay_min = 500;
00346 delay_max = 2000;
00347 }
00348 else
00349 {
00350 delay_min = 300;
00351 delay_max = 1500;
00352 }
00353
00354 TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
00355 ImperialProbe_FireBlaster();
00356
00357 }
00358
00359 if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00360 {
00361 ImperialProbe_Hunt( visible, advance );
00362 }
00363 }
00364
00365
00366
00367
00368
00369
00370
00371 #define MIN_MELEE_RANGE 320
00372 #define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
00373
00374 #define MIN_DISTANCE 128
00375 #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
00376
00377 void ImperialProbe_AttackDecision( void )
00378 {
00379 float distance;
00380 qboolean visible;
00381 qboolean advance;
00382
00383
00384 ImperialProbe_MaintainHeight();
00385
00386
00387 if ( TIMER_Done(NPC,"patrolNoise") )
00388 {
00389 if (TIMER_Done(NPC,"angerNoise"))
00390 {
00391 G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
00392
00393 TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
00394 }
00395 }
00396
00397
00398 if ( NPC_CheckEnemyExt(qfalse) == qfalse )
00399 {
00400 ImperialProbe_Idle();
00401 return;
00402 }
00403
00404 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL);
00405
00406
00407 distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00408 visible = NPC_ClearLOS4( NPC->enemy );
00409 advance = (qboolean)(distance > MIN_DISTANCE_SQR);
00410
00411
00412 if ( visible == qfalse )
00413 {
00414 if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00415 {
00416 ImperialProbe_Hunt( visible, advance );
00417 return;
00418 }
00419 }
00420
00421
00422 NPC_FaceEnemy( qtrue );
00423
00424
00425 ImperialProbe_Ranged( visible, advance );
00426 }
00427
00428
00429
00430
00431
00432
00433 void NPC_Probe_Pain(gentity_t *self, gentity_t *attacker, int damage)
00434 {
00435 float pain_chance;
00436 gentity_t *other = attacker;
00437 int mod = gPainMOD;
00438
00439 VectorCopy( self->NPC->lastPathAngles, self->s.angles );
00440
00441 if ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
00442 {
00443 vec3_t endPos;
00444 trace_t trace;
00445
00446 VectorSet( endPos, self->r.currentOrigin[0], self->r.currentOrigin[1], self->r.currentOrigin[2] - 128 );
00447 trap_Trace( &trace, self->r.currentOrigin, NULL, NULL, endPos, self->s.number, MASK_SOLID );
00448
00449 if ( trace.fraction == 1.0f || mod == MOD_DEMP2 )
00450 {
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467 if ( (mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT) && other )
00468 {
00469 vec3_t dir;
00470
00471 NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
00472
00473 VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, dir );
00474 VectorNormalize( dir );
00475
00476 VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity );
00477 self->client->ps.velocity[2] -= 127;
00478 }
00479
00480
00481
00482 self->client->ps.electrifyTime = level.time + 3000;
00483
00484 self->NPC->localState = LSTATE_DROP;
00485 }
00486 }
00487 else
00488 {
00489 pain_chance = NPC_GetPainChance( self, damage );
00490
00491 if ( random() < pain_chance )
00492 {
00493 NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE);
00494 }
00495 }
00496
00497 NPC_Pain( self, attacker, damage );
00498 }
00499
00500
00501
00502
00503
00504
00505
00506 void ImperialProbe_Idle( void )
00507 {
00508 ImperialProbe_MaintainHeight();
00509
00510 NPC_BSIdle();
00511 }
00512
00513
00514
00515
00516
00517
00518 void ImperialProbe_Patrol( void )
00519 {
00520 ImperialProbe_MaintainHeight();
00521
00522 if ( NPC_CheckPlayerTeamStealth() )
00523 {
00524 NPC_UpdateAngles( qtrue, qtrue );
00525 return;
00526 }
00527
00528
00529 if (!NPC->enemy)
00530 {
00531 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL );
00532
00533 if ( UpdateGoal() )
00534 {
00535
00536 NPC->s.loopSound = G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
00537 ucmd.buttons |= BUTTON_WALKING;
00538 NPC_MoveToGoal( qtrue );
00539 }
00540
00541 if (TIMER_Done(NPC,"patrolNoise"))
00542 {
00543 G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
00544
00545 TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
00546 }
00547 }
00548 else
00549 {
00550 G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/probe/misc/anger1" );
00551 TIMER_Set( NPC, "angerNoise", Q_irand( 2000, 4000 ) );
00552
00553 }
00554
00555 NPC_UpdateAngles( qtrue, qtrue );
00556 }
00557
00558
00559
00560
00561
00562
00563 void ImperialProbe_Wait(void)
00564 {
00565 if ( NPCInfo->localState == LSTATE_DROP )
00566 {
00567 vec3_t endPos;
00568 trace_t trace;
00569
00570 NPCInfo->desiredYaw = AngleNormalize360( NPCInfo->desiredYaw + 25 );
00571
00572 VectorSet( endPos, NPC->r.currentOrigin[0], NPC->r.currentOrigin[1], NPC->r.currentOrigin[2] - 32 );
00573 trap_Trace( &trace, NPC->r.currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
00574
00575 if ( trace.fraction != 1.0f )
00576 {
00577 G_Damage(NPC, NPC->enemy, NPC->enemy, NULL, NULL, 2000, 0,MOD_UNKNOWN);
00578 }
00579 }
00580
00581 NPC_UpdateAngles( qtrue, qtrue );
00582 }
00583
00584
00585
00586
00587
00588
00589 void NPC_BSImperialProbe_Default( void )
00590 {
00591
00592 if ( NPC->enemy )
00593 {
00594 NPCInfo->goalEntity = NPC->enemy;
00595 ImperialProbe_AttackDecision();
00596 }
00597 else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
00598 {
00599 ImperialProbe_Patrol();
00600 }
00601 else if ( NPCInfo->localState == LSTATE_DROP )
00602 {
00603 ImperialProbe_Wait();
00604 }
00605 else
00606 {
00607 ImperialProbe_Idle();
00608 }
00609 }