00001 #include "b_local.h"
00002 #include "g_nav.h"
00003 #include "anims.h"
00004 #include "w_saber.h"
00005
00006 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
00007 extern void NPC_AimAdjust( int change );
00008 extern qboolean WP_LobFire( gentity_t *self, vec3_t start, vec3_t target, vec3_t mins, vec3_t maxs, int clipmask,
00009 vec3_t velocity, qboolean tracePath, int ignoreEntNum, int enemyNum,
00010 float minSpeed, float maxSpeed, float idealSpeed, qboolean mustHit );
00011 extern void G_SoundOnEnt (gentity_t *ent, soundChannel_t channel, const char *soundPath);
00012
00013 #include "../namespace_begin.h"
00014 extern qboolean BG_CrouchAnim( int anim );
00015 #include "../namespace_end.h"
00016
00017
00018
00019 #define MELEE_DIST_SQUARED 6400//80*80
00020 #define MIN_LOB_DIST_SQUARED 65536//256*256
00021 #define MAX_LOB_DIST_SQUARED 200704//448*448
00022 #define REPEATER_ALT_SIZE 3 // half of bbox size
00023 #define GENERATOR_HEALTH 25
00024 #define TURN_ON 0x00000000
00025 #define TURN_OFF 0x00000100
00026 #define GALAK_SHIELD_HEALTH 500
00027
00028 static vec3_t shieldMins = {-60, -60, -24 };
00029 static vec3_t shieldMaxs = {60, 60, 80};
00030
00031 extern qboolean NPC_CheckPlayerTeamStealth( void );
00032
00033 static qboolean enemyLOS4;
00034 static qboolean enemyCS4;
00035 static qboolean hitAlly4;
00036 static qboolean faceEnemy4;
00037 static qboolean move4;
00038 static qboolean shoot4;
00039 static float enemyDist4;
00040 static vec3_t impactPos4;
00041
00042 void NPC_GalakMech_Precache( void )
00043 {
00044 G_SoundIndex( "sound/weapons/galak/skewerhit.wav" );
00045 G_SoundIndex( "sound/weapons/galak/lasercharge.wav" );
00046 G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
00047 G_SoundIndex( "sound/weapons/galak/laserdamage.wav" );
00048
00049 G_EffectIndex( "galak/trace_beam" );
00050 G_EffectIndex( "galak/beam_warmup" );
00051
00052 G_EffectIndex( "env/med_explode2");
00053 G_EffectIndex( "env/small_explode2");
00054 G_EffectIndex( "galak/explode");
00055 G_EffectIndex( "blaster/smoke_bolton");
00056
00057 }
00058
00059 void NPC_GalakMech_Init( gentity_t *ent )
00060 {
00061 if (ent->NPC->behaviorState != BS_CINEMATIC)
00062 {
00063 ent->client->ps.stats[STAT_ARMOR] = GALAK_SHIELD_HEALTH;
00064 ent->NPC->investigateCount = ent->NPC->investigateDebounceTime = 0;
00065 ent->flags |= FL_SHIELDED;
00066
00067
00068
00069 VectorSet( ent->r.mins, -60, -60, -24 );
00070 VectorSet( ent->r.maxs, 60, 60, 80 );
00071 ent->flags |= FL_NO_KNOCKBACK;
00072 TIMER_Set( ent, "attackDelay", 0 );
00073 TIMER_Set( ent, "flee", 0 );
00074 TIMER_Set( ent, "smackTime", 0 );
00075 TIMER_Set( ent, "beamDelay", 0 );
00076 TIMER_Set( ent, "noLob", 0 );
00077 TIMER_Set( ent, "noRapid", 0 );
00078 TIMER_Set( ent, "talkDebounce", 0 );
00079
00080 NPC_SetSurfaceOnOff( ent, "torso_shield", TURN_ON );
00081 NPC_SetSurfaceOnOff( ent, "torso_galakface", TURN_OFF );
00082 NPC_SetSurfaceOnOff( ent, "torso_galakhead", TURN_OFF );
00083 NPC_SetSurfaceOnOff( ent, "torso_eyes_mouth", TURN_OFF );
00084 NPC_SetSurfaceOnOff( ent, "torso_collar", TURN_OFF );
00085 NPC_SetSurfaceOnOff( ent, "torso_galaktorso", TURN_OFF );
00086 }
00087 else
00088 {
00089
00090 NPC_SetSurfaceOnOff( ent, "torso_shield", TURN_OFF );
00091 NPC_SetSurfaceOnOff( ent, "torso_galakface", TURN_ON );
00092 NPC_SetSurfaceOnOff( ent, "torso_galakhead", TURN_ON );
00093 NPC_SetSurfaceOnOff( ent, "torso_eyes_mouth", TURN_ON );
00094 NPC_SetSurfaceOnOff( ent, "torso_collar", TURN_ON );
00095 NPC_SetSurfaceOnOff( ent, "torso_galaktorso", TURN_ON );
00096 }
00097
00098 }
00099
00100
00101 static void GM_CreateExplosion( gentity_t *self, const int boltID, qboolean doSmall )
00102 {
00103 if ( boltID >=0 )
00104 {
00105 mdxaBone_t boltMatrix;
00106 vec3_t org, dir;
00107
00108 trap_G2API_GetBoltMatrix( self->ghoul2, 0,
00109 boltID,
00110 &boltMatrix, self->r.currentAngles, self->r.currentOrigin, level.time,
00111 NULL, self->modelScale );
00112
00113 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, org );
00114 BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, dir );
00115
00116 if ( doSmall )
00117 {
00118 G_PlayEffectID( G_EffectIndex("env/small_explode2"), org, dir );
00119 }
00120 else
00121 {
00122 G_PlayEffectID( G_EffectIndex("env/med_explode2"), org, dir );
00123 }
00124 }
00125 }
00126
00127
00128
00129
00130
00131
00132
00133 void GM_Dying( gentity_t *self )
00134 {
00135 if ( level.time - self->s.time < 4000 )
00136 {
00137
00138
00139 self->client->ps.electrifyTime = level.time + 1000;
00140 if ( TIMER_Done( self, "dyingExplosion" ) )
00141 {
00142 int newBolt;
00143 switch ( Q_irand( 1, 14 ) )
00144 {
00145
00146 case 1:
00147 if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_hand" ))
00148 {
00149 GM_CreateExplosion( self, trap_G2API_AddBolt(self->ghoul2, 0, "*flasha"), qtrue );
00150 NPC_SetSurfaceOnOff( self, "r_hand", TURN_OFF );
00151 }
00152 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_arm_middle" ))
00153 {
00154 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*r_arm_elbow" );
00155 NPC_SetSurfaceOnOff( self, "r_arm_middle", TURN_OFF );
00156 }
00157 break;
00158 case 2:
00159
00160 if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_hand" ))
00161 {
00162 GM_CreateExplosion( self, trap_G2API_AddBolt(self->ghoul2, 0, "*flashc"), qfalse );
00163 NPC_SetSurfaceOnOff( self, "l_hand", TURN_OFF );
00164 }
00165 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_wrist" ))
00166 {
00167 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_arm_cap_l_hand" );
00168 NPC_SetSurfaceOnOff( self, "l_arm_wrist", TURN_OFF );
00169 }
00170 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_middle" ))
00171 {
00172 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_arm_cap_l_hand" );
00173 NPC_SetSurfaceOnOff( self, "l_arm_middle", TURN_OFF );
00174 }
00175 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_augment" ))
00176 {
00177 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_arm_elbow" );
00178 NPC_SetSurfaceOnOff( self, "l_arm_augment", TURN_OFF );
00179 }
00180 break;
00181 case 3:
00182 case 4:
00183 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*hip_fr" );
00184 GM_CreateExplosion( self, newBolt, qfalse );
00185 break;
00186 case 5:
00187 case 6:
00188 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*shldr_l" );
00189 GM_CreateExplosion( self, newBolt, qfalse );
00190 break;
00191 case 7:
00192 case 8:
00193 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*uchest_r" );
00194 GM_CreateExplosion( self, newBolt, qfalse );
00195 break;
00196 case 9:
00197 case 10:
00198 GM_CreateExplosion( self, self->client->renderInfo.headBolt, qfalse );
00199 break;
00200 case 11:
00201 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_leg_knee" );
00202 GM_CreateExplosion( self, newBolt, qtrue );
00203 break;
00204 case 12:
00205 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*r_leg_knee" );
00206 GM_CreateExplosion( self, newBolt, qtrue );
00207 break;
00208 case 13:
00209 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_leg_foot" );
00210 GM_CreateExplosion( self, newBolt, qtrue );
00211 break;
00212 case 14:
00213 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*r_leg_foot" );
00214 GM_CreateExplosion( self, newBolt, qtrue );
00215 break;
00216 }
00217
00218 TIMER_Set( self, "dyingExplosion", Q_irand( 300, 1100 ) );
00219 }
00220 }
00221 else
00222 {
00223 G_PlayEffectID( G_EffectIndex("galak/explode"), self->r.currentOrigin, vec3_origin );
00224
00225
00226 self->nextthink = level.time + FRAMETIME;
00227 self->think = G_FreeEntity;
00228 }
00229 }
00230
00231
00232
00233
00234
00235
00236
00237 extern void NPC_SetPainEvent( gentity_t *self );
00238 void NPC_GM_Pain(gentity_t *self, gentity_t *attacker, int damage)
00239 {
00240 vec3_t point;
00241 gentity_t *inflictor = attacker;
00242 int hitLoc = 1;
00243 int mod = gPainMOD;
00244
00245 VectorCopy(gPainPoint, point);
00246
00247
00248 if (0)
00249 {
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272 }
00273 else
00274 {
00275 if ( point )
00276 {
00277
00278
00279
00280 }
00281 }
00282
00283 if ( !self->lockCount && self->client->ps.torsoTimer <= 0 )
00284 {
00285 if ( self->count < 4 && self->health > 100 && hitLoc != HL_GENERIC1 )
00286 {
00287 if ( self->delay < level.time )
00288 {
00289 int speech;
00290 switch( self->count )
00291 {
00292 default:
00293 case 0:
00294 speech = EV_PUSHED1;
00295 break;
00296 case 1:
00297 speech = EV_PUSHED2;
00298 break;
00299 case 2:
00300 speech = EV_PUSHED3;
00301 break;
00302 case 3:
00303 speech = EV_DETECTED1;
00304 break;
00305 }
00306 self->count++;
00307 self->NPC->blockedSpeechDebounceTime = 0;
00308 G_AddVoiceEvent( self, speech, Q_irand( 3000, 5000 ) );
00309 self->delay = level.time + Q_irand( 5000, 7000 );
00310 }
00311 }
00312 else
00313 {
00314 NPC_Pain(self, attacker, damage);
00315 }
00316 }
00317 else if ( hitLoc == HL_GENERIC1 )
00318 {
00319 NPC_SetPainEvent( self );
00320
00321
00322 self->client->ps.electrifyTime = level.time + Q_irand(500, 2500);
00323 }
00324
00325 if ( inflictor && inflictor->lastEnemy == self )
00326 {
00327 if ( mod == MOD_REPEATER_ALT && !Q_irand( 0, 2 ) )
00328 {
00329 if ( TIMER_Done( self, "noRapid" ) )
00330 {
00331 self->NPC->scriptFlags &= ~SCF_ALT_FIRE;
00332 self->alt_fire = qfalse;
00333 TIMER_Set( self, "noLob", Q_irand( 2000, 6000 ) );
00334 }
00335 else
00336 {
00337 TIMER_Set( self, "noLob", Q_irand( 1000, 2000 ) );
00338 }
00339 }
00340 else if ( mod == MOD_REPEATER && !Q_irand( 0, 5 ) )
00341 {
00342 if ( TIMER_Done( self, "noLob" ) )
00343 {
00344 self->NPC->scriptFlags |= SCF_ALT_FIRE;
00345 self->alt_fire = qtrue;
00346 TIMER_Set( self, "noRapid", Q_irand( 2000, 6000 ) );
00347 }
00348 else
00349 {
00350 TIMER_Set( self, "noRapid", Q_irand( 1000, 2000 ) );
00351 }
00352 }
00353 }
00354 }
00355
00356
00357
00358
00359
00360
00361
00362 static void GM_HoldPosition( void )
00363 {
00364 NPC_FreeCombatPoint( NPCInfo->combatPoint, qtrue );
00365 if ( !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
00366 {
00367 NPCInfo->goalEntity = NULL;
00368 }
00369 }
00370
00371
00372
00373
00374
00375
00376 static qboolean GM_Move( void )
00377 {
00378 qboolean moved;
00379 navInfo_t info;
00380
00381 NPCInfo->combatMove = qtrue;
00382
00383 moved = NPC_MoveToGoal( qtrue );
00384
00385
00386 NAV_GetLastMove( &info );
00387
00388
00389
00390 if ( info.flags & NIF_COLLISION )
00391 {
00392 if ( info.blocker == NPC->enemy )
00393 {
00394 GM_HoldPosition();
00395 }
00396 }
00397
00398
00399 if ( moved == qfalse )
00400 {
00401 if ( !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
00402 {
00403 GM_HoldPosition();
00404 }
00405 }
00406
00407 return moved;
00408 }
00409
00410
00411
00412
00413
00414
00415
00416 void NPC_BSGM_Patrol( void )
00417 {
00418 if ( NPC_CheckPlayerTeamStealth() )
00419 {
00420 NPC_UpdateAngles( qtrue, qtrue );
00421 return;
00422 }
00423
00424
00425 if ( UpdateGoal() )
00426 {
00427 ucmd.buttons |= BUTTON_WALKING;
00428 NPC_MoveToGoal( qtrue );
00429 }
00430
00431 NPC_UpdateAngles( qtrue, qtrue );
00432 }
00433
00434
00435
00436
00437
00438
00439
00440 static void GM_CheckMoveState( void )
00441 {
00442 if ( trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
00443 {
00444 move4 = qtrue;
00445 }
00446
00447
00448 if ( ( NPCInfo->goalEntity != NPC->enemy ) && ( NPCInfo->goalEntity != NULL ) )
00449 {
00450
00451 if ( NAV_HitNavGoal( NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPCInfo->goalEntity->r.currentOrigin, 16, qfalse ) ||
00452 ( !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) && enemyLOS4 && enemyDist4 <= 10000 ) )
00453 {
00454 NPC_ReachedGoal();
00455
00456 TIMER_Set( NPC, "attackDelay", Q_irand( 250, 500 ) );
00457 return;
00458 }
00459 }
00460 }
00461
00462
00463
00464
00465
00466
00467
00468 static void GM_CheckFireState( void )
00469 {
00470 if ( enemyCS4 )
00471 {
00472 return;
00473 }
00474
00475 if ( !VectorCompare( NPC->client->ps.velocity, vec3_origin ) )
00476 {
00477 return;
00478 }
00479
00480
00481 if ( !hitAlly4 && NPCInfo->enemyLastSeenTime > 0 )
00482 {
00483 if ( level.time - NPCInfo->enemyLastSeenTime < 10000 )
00484 {
00485 if ( !Q_irand( 0, 10 ) )
00486 {
00487
00488 vec3_t muzzle, dir, angles;
00489 qboolean tooClose = qfalse;
00490 qboolean tooFar = qfalse;
00491 float distThreshold;
00492 float dist;
00493
00494 CalcEntitySpot( NPC, SPOT_HEAD, muzzle );
00495 if ( VectorCompare( impactPos4, vec3_origin ) )
00496 {
00497 trace_t tr;
00498
00499 vec3_t forward, end;
00500 AngleVectors( NPC->client->ps.viewangles, forward, NULL, NULL );
00501 VectorMA( muzzle, 8192, forward, end );
00502 trap_Trace( &tr, muzzle, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
00503 VectorCopy( tr.endpos, impactPos4 );
00504 }
00505
00506
00507 distThreshold = 16384;
00508 if ( NPC->s.weapon == WP_REPEATER )
00509 {
00510 if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
00511 {
00512 distThreshold = 65536;
00513 }
00514 }
00515
00516 dist = DistanceSquared( impactPos4, muzzle );
00517
00518 if ( dist < distThreshold )
00519 {
00520 tooClose = qtrue;
00521 }
00522 else if ( level.time - NPCInfo->enemyLastSeenTime > 5000 )
00523 {
00524
00525 distThreshold = 65536;
00526 if ( NPC->s.weapon == WP_REPEATER )
00527 {
00528 if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
00529 {
00530 distThreshold = 262144;
00531 }
00532 }
00533 dist = DistanceSquared( impactPos4, NPCInfo->enemyLastSeenLocation );
00534 if ( dist > distThreshold )
00535 {
00536 tooFar = qtrue;
00537 }
00538 }
00539
00540 if ( !tooClose && !tooFar )
00541 {
00542 VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir );
00543 VectorNormalize( dir );
00544 vectoangles( dir, angles );
00545
00546 NPCInfo->desiredYaw = angles[YAW];
00547 NPCInfo->desiredPitch = angles[PITCH];
00548
00549 shoot4 = qtrue;
00550 faceEnemy4 = qfalse;
00551 return;
00552 }
00553 }
00554 }
00555 }
00556 }
00557
00558 void NPC_GM_StartLaser( void )
00559 {
00560 if ( !NPC->lockCount )
00561 {
00562
00563 #if 0
00564 NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_RAISEWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00565 #endif
00566 TIMER_Set( NPC, "beamDelay", NPC->client->ps.torsoTimer );
00567 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer+3000 );
00568 NPC->lockCount = 1;
00569
00570 G_PlayEffectID( G_EffectIndex("galak/beam_warmup"), NPC->r.currentOrigin, vec3_origin );
00571 G_SoundOnEnt( NPC, CHAN_AUTO, "sound/weapons/galak/lasercharge.wav" );
00572 }
00573 }
00574
00575 void GM_StartGloat( void )
00576 {
00577 NPC->wait = 0;
00578 NPC_SetSurfaceOnOff( NPC, "torso_galakface", TURN_ON );
00579 NPC_SetSurfaceOnOff( NPC, "torso_galakhead", TURN_ON );
00580 NPC_SetSurfaceOnOff( NPC, "torso_eyes_mouth", TURN_ON );
00581 NPC_SetSurfaceOnOff( NPC, "torso_collar", TURN_ON );
00582 NPC_SetSurfaceOnOff( NPC, "torso_galaktorso", TURN_ON );
00583
00584 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND2TO1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00585 NPC->client->ps.legsTimer += 500;
00586 NPC->client->ps.torsoTimer += 500;
00587 }
00588
00589
00590
00591
00592
00593
00594 void NPC_BSGM_Attack( void )
00595 {
00596
00597 if ( NPC->painDebounceTime > level.time )
00598 {
00599 NPC_UpdateAngles( qtrue, qtrue );
00600 return;
00601 }
00602
00603 #if 0
00604
00605 if ( NPC->enemy && NPC->enemy->health <= 0
00606 && !NPC->enemy->s.number )
00607 {
00608 if ( NPC->client->ps.torsoAnim == BOTH_STAND2TO1 )
00609 {
00610 if ( NPC->client->ps.torsoTimer <= 500 )
00611 {
00612 G_AddVoiceEvent( NPC, Q_irand( EV_VICTORY1, EV_VICTORY3 ), 3000 );
00613 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00614 NPC->client->ps.legsTimer += 500;
00615 NPC->client->ps.torsoTimer += 500;
00616 }
00617 }
00618 else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1START )
00619 {
00620 if ( NPC->client->ps.torsoTimer <= 500 )
00621 {
00622 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STARTGESTURE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00623 NPC->client->ps.legsTimer += 500;
00624 NPC->client->ps.torsoTimer += 500;
00625 }
00626 }
00627 else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STARTGESTURE )
00628 {
00629 if ( NPC->client->ps.torsoTimer <= 500 )
00630 {
00631 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00632 NPC->client->ps.legsTimer += 500;
00633 NPC->client->ps.torsoTimer += 500;
00634 }
00635 }
00636 else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STOP )
00637 {
00638 if ( NPC->client->ps.torsoTimer <= 500 )
00639 {
00640 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00641 NPC->client->ps.legsTimer = -1;
00642 NPC->client->ps.torsoTimer = -1;
00643 }
00644 }
00645 else if ( NPC->wait )
00646 {
00647 if ( TIMER_Done( NPC, "gloatTime" ) )
00648 {
00649 GM_StartGloat();
00650 }
00651 else if ( DistanceHorizontalSquared( NPC->client->renderInfo.eyePoint, NPC->enemy->r.currentOrigin ) > 4096 && (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )
00652 {
00653 NPCInfo->goalEntity = NPC->enemy;
00654 GM_Move();
00655 }
00656 else
00657 {
00658 GM_StartGloat();
00659 }
00660 }
00661 NPC_FaceEnemy( qtrue );
00662 NPC_UpdateAngles( qtrue, qtrue );
00663 return;
00664 }
00665 #endif
00666
00667
00668 if ( NPC_CheckEnemyExt(qfalse) == qfalse || !NPC->enemy )
00669 {
00670 NPC->enemy = NULL;
00671 NPC_BSGM_Patrol();
00672 return;
00673 }
00674
00675 enemyLOS4 = enemyCS4 = qfalse;
00676 move4 = qtrue;
00677 faceEnemy4 = qfalse;
00678 shoot4 = qfalse;
00679 hitAlly4 = qfalse;
00680 VectorClear( impactPos4 );
00681 enemyDist4 = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00682
00683
00684
00685 if (0)
00686 {
00687 shoot4 = qfalse;
00688 if ( TIMER_Done( NPC, "smackTime" ) && !NPCInfo->blockedDebounceTime )
00689 {
00690
00691 if ( enemyDist4 < MELEE_DIST_SQUARED && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) )
00692 {
00693 vec3_t smackDir;
00694 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir );
00695 smackDir[2] += 30;
00696 VectorNormalize( smackDir );
00697
00698 G_Sound( NPC->enemy, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) );
00699 G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_CRUSH );
00700 if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 )
00701 {
00702 int knockAnim = BOTH_KNOCKDOWN1;
00703 if ( BG_CrouchAnim( NPC->enemy->client->ps.legsAnim ) )
00704 {
00705 knockAnim = BOTH_KNOCKDOWN4;
00706 }
00707
00708 smackDir[2] = 1;
00709 VectorNormalize( smackDir );
00710 G_Throw( NPC->enemy, smackDir, 50 );
00711 NPC_SetAnim( NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00712 }
00713 else
00714 {
00715
00716 G_Throw( NPC->enemy, smackDir, 100 );
00717
00718 NPC_SetAnim( NPC->enemy, SETANIM_BOTH, BOTH_KNOCKDOWN5, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00719 }
00720
00721 NPCInfo->blockedDebounceTime = 1;
00722 }
00723 }
00724 }
00725 else if ( NPC->lockCount )
00726 {
00727 shoot4 = qfalse;
00728 if ( NPC->lockCount == 1 )
00729 {
00730 if ( TIMER_Done( NPC, "beamDelay" ) )
00731 {
00732 int laserAnim;
00733
00734 if (1)
00735 {
00736 laserAnim = BOTH_ATTACK2;
00737 }
00738
00739
00740
00741
00742
00743
00744 NPC_SetAnim( NPC, SETANIM_BOTH, laserAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00745 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
00746
00747 NPC->lockCount = 2;
00748 G_PlayEffectID( G_EffectIndex("galak/trace_beam"), NPC->r.currentOrigin, vec3_origin );
00749 NPC->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
00750 if ( !NPCInfo->coverTarg )
00751 {
00752 NPCInfo->coverTarg = G_Spawn();
00753 if ( NPCInfo->coverTarg )
00754 {
00755 G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
00756 NPCInfo->coverTarg->r.svFlags |= SVF_BROADCAST;
00757 NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
00758 }
00759 }
00760 }
00761 }
00762 else
00763 {
00764 if ( NPC->client->ps.torsoTimer <= 0 )
00765 {
00766 NPC->lockCount = 0;
00767 G_FreeEntity( NPCInfo->coverTarg );
00768 NPC->s.loopSound = 0;
00769 #if 0
00770 NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_DROPWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00771 #endif
00772 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer );
00773 }
00774 else
00775 {
00776
00777 trace_t trace;
00778 vec3_t end, mins={-3,-3,-3}, maxs={3,3,3};
00779 VectorMA( NPC->client->renderInfo.muzzlePoint, 1024, NPC->client->renderInfo.muzzleDir, end );
00780 trap_Trace( &trace, NPC->client->renderInfo.muzzlePoint, mins, maxs, end, NPC->s.number, MASK_SHOT );
00781 if ( trace.allsolid || trace.startsolid )
00782 {
00783 if ( NPCInfo->coverTarg )
00784 {
00785 G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
00786 }
00787 }
00788 else
00789 {
00790 if ( trace.fraction < 1.0f )
00791 {
00792 gentity_t *traceEnt = &g_entities[trace.entityNum];
00793 if ( traceEnt && traceEnt->takedamage )
00794 {
00795 G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
00796 G_Damage( traceEnt, NPC, NPC, NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_UNKNOWN );
00797 }
00798 }
00799 if ( NPCInfo->coverTarg )
00800 {
00801 G_SetOrigin( NPCInfo->coverTarg, trace.endpos );
00802 }
00803 if ( !Q_irand( 0, 5 ) )
00804 {
00805 G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
00806 }
00807 }
00808 }
00809 }
00810 }
00811 else
00812 {
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829 if (
00830 1
00831 && enemyDist4 < MELEE_DIST_SQUARED
00832 && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f )
00833 && NPC->enemy->localAnimIndex <= 1 )
00834 {
00835 if ( TIMER_Done( NPC, "attackDelay" ) )
00836 {
00837
00838 int swingAnim = BOTH_ATTACK1;
00839 #if 0
00840 if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH )
00841 {
00842 swingAnim = Q_irand( BOTH_ATTACK4, BOTH_ATTACK5 );
00843 }
00844 else
00845 {
00846 swingAnim = BOTH_ATTACK5;
00847 }
00848 #endif
00849
00850 NPC_SetAnim( NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00851 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
00852
00853 TIMER_Set( NPC, "smackTime", 600 );
00854 NPCInfo->blockedDebounceTime = 0;
00855
00856 }
00857 }
00858 else if ( !NPC->lockCount && NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH
00859 && TIMER_Done( NPC, "attackDelay" )
00860 && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f )
00861 && ((!Q_irand( 0, 10*(2-g_spskill.integer))&& enemyDist4 > MIN_LOB_DIST_SQUARED&& enemyDist4 < MAX_LOB_DIST_SQUARED)
00862 ||(!TIMER_Done( NPC, "noLob" )&&!TIMER_Done( NPC, "noRapid" )))
00863 && NPC->enemy->s.weapon != WP_TURRET )
00864 {
00865 shoot4 = qfalse;
00866 NPC_GM_StartLaser();
00867 }
00868 else if ( enemyDist4 < MIN_LOB_DIST_SQUARED
00869 && (NPC->enemy->s.weapon != WP_TURRET || Q_stricmp( "PAS", NPC->enemy->classname ))
00870 && TIMER_Done( NPC, "noRapid" ) )
00871 {
00872 if ( (NPC->client->ps.weapon == WP_REPEATER) && (NPCInfo->scriptFlags & SCF_ALT_FIRE) )
00873 {
00874 NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
00875 NPC->alt_fire = qfalse;
00876
00877 NPC_ChangeWeapon( WP_REPEATER );
00878 }
00879 }
00880 else if ( (enemyDist4 > MAX_LOB_DIST_SQUARED || (NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname )))
00881 && TIMER_Done( NPC, "noLob" ) )
00882 {
00883 if ( (NPC->client->ps.weapon == WP_REPEATER) && !(NPCInfo->scriptFlags & SCF_ALT_FIRE) )
00884 {
00885 NPCInfo->scriptFlags |= SCF_ALT_FIRE;
00886 NPC->alt_fire = qtrue;
00887
00888 NPC_ChangeWeapon( WP_REPEATER );
00889 }
00890 }
00891 }
00892
00893
00894 if ( NPC_ClearLOS4( NPC->enemy ) )
00895 {
00896 NPCInfo->enemyLastSeenTime = level.time;
00897 enemyLOS4 = qtrue;
00898
00899 if ( NPC->client->ps.weapon == WP_NONE )
00900 {
00901 enemyCS4 = qfalse;
00902 NPC_AimAdjust( -1 );
00903 }
00904 else
00905 {
00906 if ( ((NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist4 < MIN_LOB_DIST_SQUARED )
00907 {
00908 enemyCS4 = qfalse;
00909 hitAlly4 = qtrue;
00910
00911 }
00912 else
00913 {
00914 int hit = NPC_ShotEntity( NPC->enemy, impactPos4 );
00915 gentity_t *hitEnt = &g_entities[hit];
00916 if ( hit == NPC->enemy->s.number
00917 || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
00918 || ( hitEnt && hitEnt->takedamage ) )
00919 {
00920 enemyCS4 = qtrue;
00921 NPC_AimAdjust( 2 );
00922 VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
00923 }
00924 else
00925 {
00926 NPC_AimAdjust( 1 );
00927 if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam )
00928 {
00929 hitAlly4 = qtrue;
00930 }
00931 else
00932 {
00933 }
00934 }
00935 }
00936 }
00937 }
00938 else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
00939 {
00940 int hit;
00941 gentity_t *hitEnt;
00942
00943 if ( TIMER_Done( NPC, "talkDebounce" ) && !Q_irand( 0, 10 ) )
00944 {
00945 if ( NPCInfo->enemyCheckDebounceTime < 8 )
00946 {
00947 int speech = -1;
00948 switch( NPCInfo->enemyCheckDebounceTime )
00949 {
00950 case 0:
00951 case 1:
00952 case 2:
00953 speech = EV_CHASE1 + NPCInfo->enemyCheckDebounceTime;
00954 break;
00955 case 3:
00956 case 4:
00957 case 5:
00958 speech = EV_COVER1 + NPCInfo->enemyCheckDebounceTime-3;
00959 break;
00960 case 6:
00961 case 7:
00962 speech = EV_ESCAPING1 + NPCInfo->enemyCheckDebounceTime-6;
00963 break;
00964 }
00965 NPCInfo->enemyCheckDebounceTime++;
00966 if ( speech != -1 )
00967 {
00968