00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "b_local.h"
00010 #include "g_nav.h"
00011 #include "../icarus/Q3_Interface.h"
00012
00013 extern qboolean showBBoxes;
00014 extern vec3_t NPCDEBUG_BLUE;
00015 extern void G_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha );
00016 extern void NPC_CheckGetNewWeapon( void );
00017
00018 #include "../namespace_begin.h"
00019 extern qboolean PM_InKnockDown( playerState_t *ps );
00020 #include "../namespace_end.h"
00021
00022 extern void NPC_AimAdjust( int change );
00023 extern qboolean NPC_SomeoneLookingAtMe(gentity_t *ent);
00024
00025
00026
00027
00028
00029 void NPC_BSAdvanceFight (void)
00030 {
00031
00032
00033
00034 if ( NPCInfo->captureGoal )
00035 {
00036
00037
00038
00039 NPC_SetMoveGoal( NPC, NPCInfo->captureGoal->r.currentOrigin, 16, qtrue, -1, NULL );
00040
00041
00042 NPCInfo->goalTime = level.time + 100000;
00043 }
00044
00045
00046
00047 NPC_CheckEnemy(qtrue, qfalse, qtrue);
00048
00049
00050 if( NPC->enemy )
00051 {
00052 vec3_t delta, forward;
00053 vec3_t angleToEnemy;
00054 vec3_t hitspot, muzzle, diff, enemy_org, enemy_head;
00055 float distanceToEnemy;
00056 qboolean attack_ok = qfalse;
00057 qboolean dead_on = qfalse;
00058 float attack_scale = 1.0;
00059 float aim_off;
00060 float max_aim_off = 64;
00061
00062
00063 VectorMA(NPC->enemy->r.absmin, 0.5, NPC->enemy->r.maxs, enemy_org);
00064 CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
00065
00066 VectorSubtract (enemy_org, muzzle, delta);
00067 vectoangles ( delta, angleToEnemy );
00068 distanceToEnemy = VectorNormalize(delta);
00069
00070 if(!NPC_EnemyTooFar(NPC->enemy, distanceToEnemy*distanceToEnemy, qtrue))
00071 {
00072 attack_ok = qtrue;
00073 }
00074
00075 if(attack_ok)
00076 {
00077 NPC_UpdateShootAngles(angleToEnemy, qfalse, qtrue);
00078
00079 NPCInfo->enemyLastVisibility = enemyVisibility;
00080 enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV);
00081
00082 if(enemyVisibility == VIS_FOV)
00083 {
00084
00085 attack_ok = qtrue;
00086 CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_head);
00087
00088 if(attack_ok)
00089 {
00090 trace_t tr;
00091 gentity_t *traceEnt;
00092
00093 trap_Trace ( &tr, muzzle, NULL, NULL, enemy_org, NPC->s.number, MASK_SHOT );
00094 traceEnt = &g_entities[tr.entityNum];
00095 if( traceEnt != NPC->enemy &&
00096 (!traceEnt || !traceEnt->client || !NPC->client->enemyTeam || NPC->client->enemyTeam != traceEnt->client->playerTeam) )
00097 {
00098 attack_scale *= 0.75;
00099 trap_Trace ( &tr, muzzle, NULL, NULL, enemy_head, NPC->s.number, MASK_SHOT );
00100 traceEnt = &g_entities[tr.entityNum];
00101 }
00102
00103 VectorCopy( tr.endpos, hitspot );
00104
00105 if( traceEnt == NPC->enemy || (traceEnt->client && NPC->client->enemyTeam && NPC->client->enemyTeam == traceEnt->client->playerTeam) )
00106 {
00107 dead_on = qtrue;
00108 }
00109 else
00110 {
00111 attack_scale *= 0.5;
00112 if(NPC->client->playerTeam)
00113 {
00114 if(traceEnt && traceEnt->client && traceEnt->client->playerTeam)
00115 {
00116 if(NPC->client->playerTeam == traceEnt->client->playerTeam)
00117 {
00118 attack_ok = qfalse;
00119 }
00120 }
00121 }
00122 }
00123 }
00124
00125 if( attack_ok )
00126 {
00127
00128 VectorSubtract (hitspot, muzzle, delta);
00129 vectoangles ( delta, angleToEnemy );
00130 NPC->NPC->desiredPitch = angleToEnemy[PITCH];
00131 NPC_UpdateShootAngles(angleToEnemy, qtrue, qfalse);
00132
00133 if( !dead_on )
00134 {
00135
00136 AngleVectors (NPCInfo->shootAngles, forward, NULL, NULL);
00137 VectorMA ( muzzle, distanceToEnemy, forward, hitspot);
00138 VectorSubtract(hitspot, enemy_org, diff);
00139 aim_off = VectorLength(diff);
00140 if(aim_off > random() * max_aim_off)
00141 {
00142 attack_scale *= 0.75;
00143
00144 VectorSubtract(hitspot, enemy_head, diff);
00145 aim_off = VectorLength(diff);
00146 if(aim_off > random() * max_aim_off)
00147 {
00148 attack_ok = qfalse;
00149 }
00150 }
00151 attack_scale *= (max_aim_off - aim_off + 1)/max_aim_off;
00152 }
00153 }
00154 }
00155 }
00156
00157 if( attack_ok )
00158 {
00159 if( NPC_CheckAttack( attack_scale ))
00160 {
00161 enemyVisibility = VIS_SHOOT;
00162 WeaponThink(qtrue);
00163 }
00164 else
00165 attack_ok = qfalse;
00166 }
00167
00168
00169
00170 }
00171 else
00172 {
00173 NPC_UpdateShootAngles(NPC->client->ps.viewangles, qtrue, qtrue);
00174 }
00175
00176 if(!ucmd.forwardmove && !ucmd.rightmove)
00177 {
00178 if(trap_ICARUS_IsInitialized(NPC->s.number))
00179 {
00180 trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
00181 }
00182 }
00183 }
00184
00185 void Disappear(gentity_t *self)
00186 {
00187
00188 self->s.eFlags |= EF_NODRAW;
00189 self->think = 0;
00190 self->nextthink = -1;
00191 }
00192
00193 void MakeOwnerInvis (gentity_t *self);
00194 void BeamOut (gentity_t *self)
00195 {
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206 self->nextthink = level.time + 1500;
00207 self->think = Disappear;
00208 self->client->squadname = NULL;
00209 self->client->playerTeam = self->s.teamowner = TEAM_FREE;
00210
00211 }
00212
00213 void NPC_BSCinematic( void )
00214 {
00215
00216 if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
00217 {
00218 WeaponThink( qtrue );
00219 }
00220
00221 if ( UpdateGoal() )
00222 {
00223
00224 NPC_MoveToGoal( qtrue );
00225 }
00226
00227 if ( NPCInfo->watchTarget )
00228 {
00229
00230 vec3_t eyes, viewSpot, viewvec, viewangles;
00231
00232 CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
00233 CalcEntitySpot( NPCInfo->watchTarget, SPOT_HEAD_LEAN, viewSpot );
00234
00235 VectorSubtract( viewSpot, eyes, viewvec );
00236
00237 vectoangles( viewvec, viewangles );
00238
00239 NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw = viewangles[YAW];
00240 NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch = viewangles[PITCH];
00241 }
00242
00243 NPC_UpdateAngles( qtrue, qtrue );
00244 }
00245
00246 void NPC_BSWait( void )
00247 {
00248 NPC_UpdateAngles( qtrue, qtrue );
00249 }
00250
00251
00252 void NPC_BSInvestigate (void)
00253 {
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407 }
00408
00409 qboolean NPC_CheckInvestigate( int alertEventNum )
00410 {
00411 gentity_t *owner = level.alertEvents[alertEventNum].owner;
00412 int invAdd = level.alertEvents[alertEventNum].level;
00413 vec3_t soundPos;
00414 float soundRad = level.alertEvents[alertEventNum].radius;
00415 float earshot = NPCInfo->stats.earshot;
00416
00417 VectorCopy( level.alertEvents[alertEventNum].position, soundPos );
00418
00419
00420 if ( !owner )
00421 {
00422 return qfalse;
00423 }
00424
00425 if ( owner->s.eType != ET_PLAYER && owner->s.eType != ET_NPC && owner == NPCInfo->goalEntity )
00426 {
00427 return qfalse;
00428 }
00429
00430 if ( owner->s.eFlags & EF_NODRAW )
00431 {
00432 return qfalse;
00433 }
00434
00435 if ( owner->flags & FL_NOTARGET )
00436 {
00437 return qfalse;
00438 }
00439
00440 if ( soundRad < earshot )
00441 {
00442 return qfalse;
00443 }
00444
00445
00446 if ( !trap_InPVS( soundPos, NPC->r.currentOrigin ) )
00447 {
00448 return qfalse;
00449 }
00450
00451 if ( owner->client && owner->client->playerTeam && NPC->client->playerTeam && owner->client->playerTeam != NPC->client->playerTeam )
00452 {
00453 if( (float)NPCInfo->investigateCount >= (NPCInfo->stats.vigilance*200) && owner )
00454 {
00455 if ( ValidEnemy( owner ) )
00456 {
00457 G_SetEnemy( NPC, owner );
00458 NPCInfo->goalEntity = NPC->enemy;
00459 NPCInfo->goalRadius = 12;
00460 NPCInfo->behaviorState = BS_HUNT_AND_KILL;
00461 return qtrue;
00462 }
00463 }
00464 else
00465 {
00466 NPCInfo->investigateCount += invAdd;
00467 }
00468
00469 G_ActivateBehavior(NPC, BSET_AWAKE);
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479 NPCInfo->eventOwner = owner;
00480 VectorCopy( soundPos, NPCInfo->investigateGoal );
00481 if ( NPCInfo->investigateCount > 20 )
00482 {
00483 NPCInfo->investigateDebounceTime = level.time + 10000;
00484 }
00485 else
00486 {
00487 NPCInfo->investigateDebounceTime = level.time + (NPCInfo->investigateCount*500);
00488 }
00489 NPCInfo->tempBehavior = BS_INVESTIGATE;
00490 return qtrue;
00491 }
00492
00493 return qfalse;
00494 }
00495
00496
00497
00498
00499
00500 void NPC_BSSleep( void )
00501 {
00502 int alertEvent = NPC_CheckAlertEvents( qtrue, qfalse, -1, qfalse, AEL_MINOR );
00503
00504
00505 if ( alertEvent >= 0 )
00506 {
00507 G_ActivateBehavior(NPC, BSET_AWAKE);
00508 return;
00509 }
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521 }
00522
00523 extern qboolean NPC_MoveDirClear( int forwardmove, int rightmove, qboolean reset );
00524 void NPC_BSFollowLeader (void)
00525 {
00526 vec3_t vec;
00527 float leaderDist;
00528 visibility_t leaderVis;
00529 int curAnim;
00530
00531 if ( !NPC->client->leader )
00532 {
00533 if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
00534 {
00535 NPCInfo->tempBehavior = BS_DEFAULT;
00536 }
00537 else
00538 {
00539 NPCInfo->tempBehavior = BS_STAND_GUARD;
00540 NPC_BSStandGuard();
00541 }
00542 return;
00543 }
00544
00545 if ( !NPC->enemy )
00546 {
00547 NPC_CheckEnemy( NPCInfo->confusionTime<level.time, qfalse, qtrue );
00548 if ( NPC->enemy )
00549 {
00550 NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
00551 }
00552 else
00553 {
00554 if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
00555 {
00556 int eventID = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_MINOR );
00557 if ( level.alertEvents[eventID].level >= AEL_SUSPICIOUS && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
00558 {
00559 NPCInfo->lastAlertID = level.alertEvents[eventID].ID;
00560 if ( !level.alertEvents[eventID].owner ||
00561 !level.alertEvents[eventID].owner->client ||
00562 level.alertEvents[eventID].owner->health <= 0 ||
00563 level.alertEvents[eventID].owner->client->playerTeam != NPC->client->enemyTeam )
00564 {
00565 }
00566 else
00567 {
00568
00569 G_SetEnemy( NPC, level.alertEvents[eventID].owner );
00570 NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
00571 NPCInfo->enemyLastSeenTime = level.time;
00572 TIMER_Set( NPC, "attackDelay", Q_irand( 500, 1000 ) );
00573 }
00574 }
00575
00576 }
00577 }
00578 if ( !NPC->enemy )
00579 {
00580 if ( NPC->client->leader
00581 && NPC->client->leader->enemy
00582 && NPC->client->leader->enemy != NPC
00583 && ( (NPC->client->leader->enemy->client&&NPC->client->leader->enemy->client->playerTeam==NPC->client->enemyTeam)
00584 ||(0&&NPC->client->leader->enemy->alliedTeam==NPC->client->enemyTeam) )
00585 && NPC->client->leader->enemy->health > 0 )
00586 {
00587 G_SetEnemy( NPC, NPC->client->leader->enemy );
00588 NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
00589 NPCInfo->enemyLastSeenTime = level.time;
00590 }
00591 }
00592 }
00593 else
00594 {
00595 if ( NPC->enemy->health <= 0 || (NPC->enemy->flags&FL_NOTARGET) )
00596 {
00597 G_ClearEnemy( NPC );
00598 if ( NPCInfo->enemyCheckDebounceTime > level.time + 1000 )
00599 {
00600 NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 1000, 2000 );
00601 }
00602 }
00603 else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time )
00604 {
00605 NPC_CheckEnemy( (NPCInfo->confusionTime<level.time||NPCInfo->tempBehavior!=BS_FOLLOW_LEADER), qfalse, qtrue );
00606 }
00607 }
00608
00609 if ( NPC->enemy && NPC->client->ps.weapon )
00610 {
00611 if ( NPC->client->ps.weapon == WP_SABER )
00612 {
00613 if ( NPCInfo->tempBehavior != BS_FOLLOW_LEADER )
00614 {
00615
00616 NPCInfo->tempBehavior = BS_HUNT_AND_KILL;
00617 NPC_UpdateAngles(qtrue, qtrue);
00618 return;
00619 }
00620 }
00621
00622 enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );
00623 if ( enemyVisibility > VIS_PVS )
00624 {
00625 vec3_t enemy_org, muzzle, delta, angleToEnemy;
00626 float distanceToEnemy;
00627
00628 CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org );
00629 NPC_AimWiggle( enemy_org );
00630
00631 CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
00632
00633 VectorSubtract( enemy_org, muzzle, delta);
00634 vectoangles( delta, angleToEnemy );
00635 distanceToEnemy = VectorNormalize( delta );
00636
00637 NPCInfo->desiredYaw = angleToEnemy[YAW];
00638 NPCInfo->desiredPitch = angleToEnemy[PITCH];
00639 NPC_UpdateFiringAngles( qtrue, qtrue );
00640
00641 if ( enemyVisibility >= VIS_SHOOT )
00642 {
00643 NPC_AimAdjust( 2 );
00644 if ( NPC_GetHFOVPercentage( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.hfov ) > 0.6f
00645 && NPC_GetHFOVPercentage( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.vfov ) > 0.5f )
00646 {
00647 WeaponThink( qtrue );
00648 }
00649 }
00650 else
00651 {
00652 NPC_AimAdjust( 1 );
00653 }
00654
00655
00656 }
00657 else
00658 {
00659 NPC_AimAdjust( -1 );
00660 }
00661 }
00662 else
00663 {
00664 vec3_t head, leaderHead, delta, angleToLeader;
00665
00666 CalcEntitySpot( NPC->client->leader, SPOT_HEAD, leaderHead );
00667 CalcEntitySpot( NPC, SPOT_HEAD, head );
00668 VectorSubtract (leaderHead, head, delta);
00669 vectoangles ( delta, angleToLeader );
00670 VectorNormalize(delta);
00671 NPC->NPC->desiredYaw = angleToLeader[YAW];
00672 NPC->NPC->desiredPitch = angleToLeader[PITCH];
00673
00674 NPC_UpdateAngles(qtrue, qtrue);
00675 }
00676
00677
00678 leaderVis = NPC_CheckVisibility( NPC->client->leader, CHECK_PVS|CHECK_360|CHECK_SHOOT );
00679
00680
00681
00682 curAnim = NPC->client->ps.legsAnim;
00683 if ( curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 )
00684 {
00685
00686 float followDist = 96.0f;
00687 float backupdist, walkdist, minrundist;
00688 float leaderHDist;
00689
00690 if ( NPCInfo->followDist )
00691 {
00692 followDist = NPCInfo->followDist;
00693 }
00694 backupdist = followDist/2.0f;
00695 walkdist = followDist*0.83;
00696 minrundist = followDist*1.33;
00697
00698 VectorSubtract(NPC->client->leader->r.currentOrigin, NPC->r.currentOrigin, vec);
00699 leaderDist = VectorLength( vec );
00700
00701 vec[2] = 0;
00702 leaderHDist = VectorLength( vec );
00703 if( leaderHDist > backupdist && (leaderVis != VIS_SHOOT || leaderDist > walkdist) )
00704 {
00705 NPCInfo->goalEntity = NPC->client->leader;
00706
00707 NPC_SlideMoveToGoal();
00708 if ( leaderVis == VIS_SHOOT && leaderDist < minrundist )
00709 {
00710 ucmd.buttons |= BUTTON_WALKING;
00711 }
00712 }
00713 else if ( leaderDist < backupdist )
00714 {
00715 NPCInfo->goalEntity = NPC->client->leader;
00716 NPC_SlideMoveToGoal();
00717
00718
00719 ucmd.forwardmove = -ucmd.forwardmove;
00720 ucmd.rightmove = -ucmd.rightmove;
00721 VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir );
00722 }
00723
00724 if ( ucmd.forwardmove || ucmd.rightmove || VectorCompare( vec3_origin, NPC->client->ps.moveDir ) )
00725 {
00726 NPC_MoveDirClear( ucmd.forwardmove, ucmd.rightmove, qtrue );
00727 }
00728 }
00729 }
00730 #define APEX_HEIGHT 200.0f
00731 #define PARA_WIDTH (sqrt(APEX_HEIGHT)+sqrt(APEX_HEIGHT))
00732 #define JUMP_SPEED 200.0f
00733 void NPC_BSJump (void)
00734 {
00735 vec3_t dir, angles, p1, p2, apex;
00736 float time, height, forward, z, xy, dist, yawError, apexHeight;
00737
00738 if( !NPCInfo->goalEntity )
00739 {
00740 return;
00741 }
00742
00743 if ( NPCInfo->jumpState != JS_JUMPING && NPCInfo->jumpState != JS_LANDING )
00744 {
00745
00746 VectorSubtract(NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir);
00747 vectoangles(dir, angles);
00748 NPCInfo->desiredPitch = NPCInfo->lockedDesiredPitch = AngleNormalize360(angles[PITCH]);
00749 NPCInfo->desiredYaw = NPCInfo->lockedDesiredYaw = AngleNormalize360(angles[YAW]);
00750 }
00751
00752 NPC_UpdateAngles ( qtrue, qtrue );
00753 yawError = AngleDelta ( NPC->client->ps.viewangles[YAW], NPCInfo->desiredYaw );
00754
00755
00756 switch ( NPCInfo->jumpState )
00757 {
00758 case JS_FACING:
00759 if ( yawError < MIN_ANGLE_ERROR )
00760 {
00761 NPC_SetAnim(NPC, SETANIM_LEGS, BOTH_CROUCH1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
00762 NPCInfo->jumpState = JS_CROUCHING;
00763 }
00764 break;
00765 case JS_CROUCHING:
00766 if ( NPC->client->ps.legsTimer > 0 )
00767 {
00768 return;
00769 }
00770
00771
00772
00773 if ( NPC->r.currentOrigin[2] > NPCInfo->goalEntity->r.currentOrigin[2] )
00774 {
00775 VectorCopy( NPC->r.currentOrigin, p1 );
00776 VectorCopy( NPCInfo->goalEntity->r.currentOrigin, p2 );
00777 }
00778 else if ( NPC->r.currentOrigin[2] < NPCInfo->goalEntity->r.currentOrigin[2] )
00779 {
00780 VectorCopy( NPCInfo->goalEntity->r.currentOrigin, p1 );
00781 VectorCopy( NPC->r.currentOrigin, p2 );
00782 }
00783 else
00784 {
00785 VectorCopy( NPC->r.currentOrigin, p1 );
00786 VectorCopy( NPCInfo->goalEntity->r.currentOrigin, p2 );
00787 }
00788
00789
00790 VectorSubtract( p2, p1, dir );
00791 dir[2] = 0;
00792
00793
00794 xy = VectorNormalize( dir );
00795 z = p1[2] - p2[2];
00796
00797 apexHeight = APEX_HEIGHT/2;
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814 z = (sqrt(apexHeight + z) - sqrt(apexHeight));
00815
00816 assert(z >= 0);
00817
00818
00819
00820 xy -= z;
00821 xy *= 0.5;
00822
00823 assert(xy > 0);
00824
00825 VectorMA( p1, xy, dir, apex );
00826 apex[2] += apexHeight;
00827
00828 VectorCopy(apex, NPC->pos1);
00829
00830
00831 height = apex[2] - NPC->r.currentOrigin[2];
00832 time = sqrt( height / ( .5 * NPC->client->ps.gravity ) );
00833 if ( !time )
00834 {
00835
00836 return;
00837 }
00838
00839
00840 VectorSubtract ( apex, NPC->r.currentOrigin, NPC->client->ps.velocity );
00841 NPC->client->ps.velocity[2] = 0;
00842 dist = VectorNormalize( NPC->client->ps.velocity );
00843
00844 forward = dist / time;
00845 VectorScale( NPC->client->ps.velocity, forward, NPC->client->ps.velocity );
00846
00847 NPC->client->ps.velocity[2] = time * NPC->client->ps.gravity;
00848
00849
00850
00851 NPC->flags |= FL_NO_KNOCKBACK;
00852 NPCInfo->jumpState = JS_JUMPING;
00853
00854 break;
00855 case JS_JUMPING:
00856
00857 if ( showBBoxes )
00858 {
00859 VectorAdd(NPC->r.mins, NPC->pos1, p1);
00860 VectorAdd(NPC->r.maxs, NPC->pos1, p2);
00861 G_Cube( p1, p2, NPCDEBUG_BLUE, 0.5 );
00862 }
00863
00864 if ( NPC->s.groundEntityNum != ENTITYNUM_NONE)
00865 {
00866
00867 VectorClear(NPC->client->ps.velocity);
00868 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_LAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
00869 NPCInfo->jumpState = JS_LANDING;
00870
00871 }
00872 else if ( NPC->client->ps.legsTimer > 0 )
00873 {
00874
00875 return;
00876 }
00877 else
00878 {
00879 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_OVERRIDE);
00880 }
00881 break;
00882 case JS_LANDING:
00883 if ( NPC->client->ps.legsTimer > 0 )
00884 {
00885 return;
00886 }
00887 else
00888 {
00889 NPCInfo->jumpState = JS_WAITING;
00890
00891
00892
00893 NPC_ClearGoal();
00894 NPCInfo->goalTime = level.time;
00895 NPCInfo->aiFlags &= ~NPCAI_MOVING;
00896 ucmd.forwardmove = 0;
00897 NPC->flags &= ~FL_NO_KNOCKBACK;
00898
00899 trap_ICARUS_TaskIDComplete( NPC, TID_MOVE_NAV );
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912 }
00913 break;
00914 case JS_WAITING:
00915 default:
00916 NPCInfo->jumpState = JS_FACING;
00917 break;
00918 }
00919 }
00920
00921 void NPC_BSRemove (void)
00922 {
00923 NPC_UpdateAngles ( qtrue, qtrue );
00924 if( !trap_InPVS( NPC->r.currentOrigin, g_entities[0].r.currentOrigin ) )
00925 {
00926 G_UseTargets2( NPC, NPC, NPC->target3 );
00927 NPC->s.eFlags |= EF_NODRAW;
00928 NPC->s.eType = ET_INVISIBLE;
00929 NPC->r.contents = 0;
00930 NPC->health = 0;
00931 NPC->targetname = NULL;
00932
00933
00934 NPC->think = G_FreeEntity;
00935 NPC->nextthink = level.time + FRAMETIME;
00936 }
00937 }
00938
00939 void NPC_BSSearch (void)
00940 {
00941 NPC_CheckEnemy(qtrue, qfalse, qtrue);
00942
00943 if ( NPC->enemy )
00944 {
00945 if( NPCInfo->tempBehavior == BS_SEARCH )
00946 {
00947 NPCInfo->tempBehavior = BS_DEFAULT;
00948 }
00949 else
00950 {
00951 NPCInfo->behaviorState = BS_HUNT_AND_KILL;
00952 NPC_BSRunAndShoot();
00953 }
00954 return;
00955 }
00956
00957
00958
00959
00960
00961
00962
00963 if ( !NPCInfo->investigateDebounceTime )
00964 {
00965 float minGoalReachedDistSquared = 32*32;
00966 vec3_t vec;
00967
00968
00969 NPCInfo->goalEntity = NPCInfo->tempGoal;
00970
00971 VectorSubtract ( NPCInfo->tempGoal->r.currentOrigin, NPC->r.currentOrigin, vec);
00972 if ( vec[2] < 24 )
00973 {
00974 vec[2] = 0;
00975 }
00976
00977 if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
00978 {
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988 minGoalReachedDistSquared = 32*32;
00989 }
00990
00991 if ( VectorLengthSquared( vec ) < minGoalReachedDistSquared )
00992 {
00993
00994 NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
00995
00996 if ( ( NPCInfo->homeWp == WAYPOINT_NONE ) || ( NPC->waypoint == WAYPOINT_NONE ) )
00997 {
00998
00999 if( NPCInfo->tempBehavior == BS_SEARCH )
01000 {
01001 NPCInfo->tempBehavior = BS_DEFAULT;
01002 }
01003 else
01004 {
01005 NPCInfo->behaviorState = BS_STAND_GUARD;
01006 NPC_BSRunAndShoot();
01007 }
01008 return;
01009 }
01010
01011 if ( NPC->waypoint == NPCInfo->homeWp )
01012 {
01013
01014 if ( NPCInfo->aiFlags & NPCAI_ENROUTE_TO_HOMEWP )
01015 {
01016 NPCInfo->aiFlags &= ~NPCAI_ENROUTE_TO_HOMEWP;
01017 G_ActivateBehavior( NPC, BSET_LOSTENEMY );
01018 }
01019
01020 }
01021
01022
01023
01024 if( !Q_irand(0, 1) )
01025 {
01026 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL);
01027 }
01028 else
01029 {
01030 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL);
01031 }
01032 NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000);
01033 }
01034 else
01035 {
01036 NPC_MoveToGoal( qtrue );
01037 }
01038 }
01039 else
01040 {
01041
01042 if ( NPCInfo->investigateDebounceTime > level.time )
01043 {
01044
01045
01046 if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
01047 {
01048 if ( !Q_irand( 0, 30 ) )
01049 {
01050 int numEdges = trap_Nav_GetNodeNumEdges( NPCInfo->tempGoal->waypoint );
01051
01052 if ( numEdges != WAYPOINT_NONE )
01053 {
01054 int branchNum = Q_irand( 0, numEdges - 1 );
01055
01056 vec3_t branchPos, lookDir;
01057
01058 int nextWp = trap_Nav_GetNodeEdge( NPCInfo->tempGoal->waypoint, branchNum );
01059 trap_Nav_GetNodePosition( nextWp, branchPos );
01060
01061 VectorSubtract( branchPos, NPCInfo->tempGoal->r.currentOrigin, lookDir );
01062 NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + flrand( -45, 45 ) );
01063 }
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074 }
01075 }
01076
01077 }
01078 else
01079 {
01080 NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01081
01082 if ( NPC->waypoint == NPCInfo->homeWp )
01083 {
01084 int numEdges = trap_Nav_GetNodeNumEdges( NPCInfo->tempGoal->waypoint );
01085
01086 if ( numEdges != WAYPOINT_NONE )
01087 {
01088 int branchNum = Q_irand( 0, numEdges - 1 );
01089
01090 int nextWp = trap_Nav_GetNodeEdge( NPCInfo->homeWp, branchNum );
01091 trap_Nav_GetNodePosition( nextWp, NPCInfo->tempGoal->r.currentOrigin );
01092 NPCInfo->tempGoal->waypoint = nextWp;
01093 }
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104 }
01105 else
01106 {
01107 trap_Nav_GetNodePosition( NPCInfo->homeWp, NPCInfo->tempGoal->r.currentOrigin );
01108 NPCInfo->tempGoal->waypoint = NPCInfo->homeWp;
01109
01110
01111
01112
01113
01114 }
01115
01116 NPCInfo->investigateDebounceTime = 0;
01117
01118 NPCInfo->goalEntity = NPCInfo->tempGoal;
01119 NPC_MoveToGoal( qtrue );
01120 }
01121 }
01122
01123 NPC_UpdateAngles( qtrue, qtrue );
01124 }
01125
01126
01127
01128
01129
01130
01131
01132 void NPC_BSSearchStart( int homeWp, bState_t bState )
01133 {
01134
01135 if ( homeWp == WAYPOINT_NONE )
01136 {
01137 homeWp = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01138 if( NPC->waypoint == WAYPOINT_NONE )
01139 {
01140 NPC->waypoint = homeWp;
01141 }
01142 }
01143 NPCInfo->homeWp = homeWp;
01144 NPCInfo->tempBehavior = bState;
01145 NPCInfo->aiFlags |= NPCAI_ENROUTE_TO_HOMEWP;
01146 NPCInfo->investigateDebounceTime = 0;
01147 trap_Nav_GetNodePosition( homeWp, NPCInfo->tempGoal->r.currentOrigin );
01148 NPCInfo->tempGoal->waypoint = homeWp;
01149
01150 }
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160 void NPC_BSNoClip ( void )
01161 {
01162 if ( UpdateGoal() )
01163 {
01164 vec3_t dir, forward, right, angles, up = {0, 0, 1};
01165 float fDot, rDot, uDot;
01166
01167 VectorSubtract( NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir );
01168
01169 vectoangles( dir, angles );
01170 NPCInfo->desiredYaw = angles[YAW];
01171
01172 AngleVectors( NPC->r.currentAngles, forward, right, NULL );
01173
01174 VectorNormalize( dir );
01175
01176 fDot = DotProduct(forward, dir) * 127;
01177 rDot = DotProduct(right, dir) * 127;
01178 uDot = DotProduct(up, dir) * 127;
01179
01180 ucmd.forwardmove = floor(fDot);
01181 ucmd.rightmove = floor(rDot);
01182 ucmd.upmove = floor(uDot);
01183 }
01184 else
01185 {
01186
01187 VectorClear( NPC->client->ps.velocity );
01188 }
01189
01190 NPC_UpdateAngles( qtrue, qtrue );
01191 }
01192
01193 void NPC_BSWander (void)
01194 {
01195 if ( !NPCInfo->investigateDebounceTime )
01196 {
01197 float minGoalReachedDistSquared = 64;
01198 vec3_t vec;
01199
01200
01201 NPCInfo->goalEntity = NPCInfo->tempGoal;
01202
01203 VectorSubtract ( NPCInfo->tempGoal->r.currentOrigin, NPC->r.currentOrigin, vec);
01204
01205 if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
01206 {
01207 minGoalReachedDistSquared = 64;
01208 }
01209
01210 if ( VectorLengthSquared( vec ) < minGoalReachedDistSquared )
01211 {
01212
01213 NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01214
01215 if( !Q_irand(0, 1) )
01216 {
01217 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL);
01218 }
01219 else
01220 {
01221 NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL);
01222 }
01223
01224 NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000);
01225 }
01226 else
01227 {
01228
01229 NPC_MoveToGoal( qtrue );
01230 }
01231 }
01232 else
01233 {
01234
01235 if ( NPCInfo->investigateDebounceTime > level.time )
01236 {
01237
01238
01239 if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE )
01240 {
01241 if ( !Q_irand( 0, 30 ) )
01242 {
01243 int numEdges = trap_Nav_GetNodeNumEdges( NPCInfo->tempGoal->waypoint );
01244
01245 if ( numEdges != WAYPOINT_NONE )
01246 {
01247 int branchNum = Q_irand( 0, numEdges - 1 );
01248
01249 vec3_t branchPos, lookDir;
01250
01251 int nextWp = trap_Nav_GetNodeEdge( NPCInfo->tempGoal->waypoint, branchNum );
01252 trap_Nav_GetNodePosition( nextWp, branchPos );
01253
01254 VectorSubtract( branchPos, NPCInfo->tempGoal->r.currentOrigin, lookDir );
01255 NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + flrand( -45, 45 ) );
01256 }
01257 }
01258 }
01259 }
01260 else
01261 {
01262 NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE );
01263
01264 if ( NPC->waypoint != WAYPOINT_NONE )
01265 {
01266 int numEdges = trap_Nav_GetNodeNumEdges( NPC->waypoint );
01267
01268 if ( numEdges != WAYPOINT_NONE )
01269 {
01270 int branchNum = Q_irand( 0, numEdges - 1 );
01271
01272 int nextWp = trap_Nav_GetNodeEdge( NPC->waypoint, branchNum );
01273 trap_Nav_GetNodePosition( nextWp, NPCInfo->tempGoal->r.currentOrigin );
01274 NPCInfo->tempGoal->waypoint = nextWp;
01275 }
01276
01277 NPCInfo->investigateDebounceTime = 0;
01278
01279 NPCInfo->goalEntity = NPCInfo->tempGoal;
01280 NPC_MoveToGoal( qtrue );
01281 }
01282 }
01283 }
01284
01285 NPC_UpdateAngles( qtrue, qtrue );
01286 }
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
01315 extern void WP_DropWeapon( gentity_t *dropper, vec3_t velocity );
01316 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
01317 void NPC_Surrender( void )
01318 {
01319 if ( NPC->client->ps.weaponTime || PM_InKnockDown( &NPC->client->ps ) )
01320 {
01321 return;
01322 }
01323 if ( NPC->s.weapon != WP_NONE &&
01324 NPC->s.weapon != WP_STUN_BATON &&
01325 NPC->s.weapon != WP_SABER )
01326 {
01327
01328 }
01329 if ( NPCInfo->surrenderTime < level.time - 5000 )
01330 {
01331
01332 NPCInfo->blockedSpeechDebounceTime = 0;
01333 G_AddVoiceEvent( NPC, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 3000 );
01334 }
01335
01336
01337 NPCInfo->surrenderTime = level.time + 1000;
01338
01339 }
01340
01341 qboolean NPC_CheckSurrender( void )
01342 {
01343 if ( !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV )
01344 && NPC->client->ps.groundEntityNum != ENTITYNUM_NONE
01345 && !NPC->client->ps.weaponTime && !PM_InKnockDown( &NPC->client->ps )
01346 && NPC->enemy && NPC->enemy->client && NPC->enemy->enemy == NPC && NPC->enemy->s.weapon != WP_NONE && NPC->enemy->s.weapon != WP_STUN_BATON
01347 && NPC->enemy->health > 20 && NPC->enemy->painDebounceTime < level.time - 3000 && NPC->enemy->client->ps.fd.forcePowerDebounce[FP_SABER_DEFENSE] < level.time - 1000 )
01348 {
01349
01350 if ( NPC->s.weapon != WP_ROCKET_LAUNCHER
01351 && NPC->s.weapon != WP_REPEATER
01352 && NPC->s.weapon != WP_FLECHETTE
01353 && NPC->s.weapon != WP_SABER )
01354 {
01355
01356 if ( NPC->s.weapon != WP_NONE )
01357 {
01358
01359 if ( NPC->health > 25 )
01360 {
01361 return qfalse;
01362 }
01363
01364 if (NPC_SomeoneLookingAtMe(NPC) && NPC->painDebounceTime > level.time)
01365 {
01366
01367 }
01368 else
01369 {
01370 if ( !InFOV( NPC->enemy, NPC, 60, 30 ) )
01371 {
01372 return qfalse;
01373 }
01374 else if ( DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ) < 65536 )
01375 {
01376 return qfalse;
01377 }
01378 else if ( !trap_InPVS( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ) )
01379 {
01380 return qfalse;
01381 }
01382 }
01383 }
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439 }
01440 }
01441 return qfalse;
01442 }
01443
01444 void NPC_BSFlee( void )
01445 {
01446 gentity_t *goal;
01447
01448 if ( TIMER_Done( NPC, "flee" ) && NPCInfo->tempBehavior == BS_FLEE )
01449 {
01450 NPCInfo->tempBehavior = BS_DEFAULT;
01451 NPCInfo->squadState = SQUAD_IDLE;
01452
01453
01454
01455 }
01456 if ( NPC_CheckSurrender() )
01457 {
01458 return;
01459 }
01460 goal = NPCInfo->goalEntity;
01461 if ( !goal )
01462 {
01463 goal = NPCInfo->lastGoalEntity;
01464 if ( !goal )
01465 {
01466 goal = NPCInfo->tempGoal;
01467 }
01468 }
01469
01470 if ( goal )
01471 {
01472 qboolean moved;
01473 qboolean reverseCourse = qtrue;
01474
01475
01476
01477
01478 if ( NPC->waypoint == WAYPOINT_NONE )
01479 {
01480 NPC->waypoint = NAV_GetNearestNode( NPC, NPC->lastWaypoint );
01481 }
01482 if ( NPC->waypoint != WAYPOINT_NONE )
01483 {
01484 int numEdges = trap_Nav_GetNodeNumEdges( NPC->waypoint );
01485
01486 if ( numEdges != WAYPOINT_NONE )
01487 {
01488 vec3_t dangerDir;
01489 int nextWp;
01490 int branchNum;
01491
01492 VectorSubtract( NPCInfo->investigateGoal, NPC->r.currentOrigin, dangerDir );
01493 VectorNormalize( dangerDir );
01494
01495 for ( branchNum = 0; branchNum < numEdges; branchNum++ )
01496 {
01497 vec3_t branchPos, runDir;
01498
01499 nextWp = trap_Nav_GetNodeEdge( NPC->waypoint, branchNum );
01500 trap_Nav_GetNodePosition( nextWp, branchPos );
01501
01502 VectorSubtract( branchPos, NPC->r.currentOrigin, runDir );
01503 VectorNormalize( runDir );
01504 if ( DotProduct( runDir, dangerDir ) > flrand( 0, 0.5 ) )
01505 {
01506 continue;
01507 }
01508
01509 NPC_SetMoveGoal( NPC, branchPos, 0, qtrue, -1, NULL );
01510 reverseCourse = qfalse;
01511 break;
01512 }
01513 }
01514 }
01515
01516 moved = NPC_MoveToGoal( qfalse );
01517
01518 if ( NPC->s.weapon == WP_NONE && (moved == qfalse || reverseCourse) )
01519 {
01520 NPC_Surrender();
01521 NPC_UpdateAngles( qtrue, qtrue );
01522 return;
01523 }
01524
01525
01526 if ( moved == qfalse )
01527 {
01528 vec3_t dir;
01529 float dist;
01530 if ( reverseCourse )
01531 {
01532 VectorSubtract( NPC->r.currentOrigin, goal->r.currentOrigin, dir );
01533 }
01534 else
01535 {
01536 VectorSubtract( goal->r.currentOrigin, NPC->r.currentOrigin, dir );
01537 }
01538 NPCInfo->distToGoal = dist = VectorNormalize( dir );
01539 NPCInfo->desiredYaw = vectoyaw( dir );
01540 NPCInfo->desiredPitch = 0;
01541 ucmd.forwardmove = 127;
01542 }
01543 else if ( reverseCourse )
01544 {
01545
01546
01547
01548 NPCInfo->desiredYaw *= -1;
01549 }
01550
01551
01552 ucmd.buttons &= ~BUTTON_WALKING;
01553
01554 }
01555 NPC_UpdateAngles( qtrue, qtrue );
01556
01557 NPC_CheckGetNewWeapon();
01558 }
01559
01560 void NPC_StartFlee( gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax )
01561 {
01562 int cp = -1;
01563
01564 if ( trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
01565 {
01566 return;
01567 }
01568
01569
01570 if ( G_ActivateBehavior( NPC, BSET_FLEE ) )
01571 {
01572 return;
01573 }
01574
01575 if ( enemy )
01576 {
01577 G_SetEnemy( NPC, enemy );
01578 }
01579
01580
01581 if ( dangerLevel > AEL_DANGER || NPC->s.weapon == WP_NONE || ((!NPCInfo->group || NPCInfo->group->numGroup <= 1) && NPC->health <= 10 ) )
01582 {
01583 cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, dangerPoint, CP_COVER|CP_AVOID|CP_HAS_ROUTE|CP_NO_PVS, 128, -1 );
01584 }
01585
01586 if ( cp == -1 )
01587 {
01588 cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, dangerPoint, CP_COVER|CP_AVOID|CP_HAS_ROUTE, 128, -1 );
01589 if ( cp == -1 )
01590 {
01591 cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, dangerPoint, CP_COVER|CP_HAS_ROUTE, 128, -1 );
01592 if ( cp == -1 )
01593 {
01594 cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, dangerPoint, CP_HAS_ROUTE, 128, -1 );
01595 }
01596 }
01597 }
01598
01599
01600 if ( cp != -1 )
01601 {
01602 NPC_SetCombatPoint( cp );
01603 NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp, NULL );
01604 NPCInfo->behaviorState = BS_HUNT_AND_KILL;
01605 NPCInfo->tempBehavior = BS_DEFAULT;
01606 }
01607 else
01608 {
01609 if ( NPC->s.weapon != WP_NONE )
01610 {
01611 return;
01612 }
01613 else
01614 {
01615
01616 NPCInfo->tempBehavior = BS_FLEE;
01617
01618 NPC_SetMoveGoal( NPC, dangerPoint, 0, qtrue, -1, NULL );
01619
01620 VectorCopy( dangerPoint, NPCInfo->investigateGoal );
01621 }
01622 }
01623
01624 TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
01625
01626 NPCInfo->squadState = SQUAD_RETREAT;
01627 TIMER_Set( NPC, "flee", Q_irand( fleeTimeMin, fleeTimeMax ) );
01628 TIMER_Set( NPC, "panic", Q_irand( 1000, 4000 ) );
01629
01630 if (NPC->client->NPC_class != CLASS_PROTOCOL)
01631 {
01632 TIMER_Set( NPC, "duck", 0 );
01633 }
01634 }
01635
01636 void G_StartFlee( gentity_t *self, gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax )
01637 {
01638 if ( !self->NPC )
01639 {
01640 return;
01641 }
01642 SaveNPCGlobals();
01643 SetNPCGlobals( self );
01644
01645 NPC_StartFlee( enemy, dangerPoint, dangerLevel, fleeTimeMin, fleeTimeMax );
01646
01647 RestoreNPCGlobals();
01648 }
01649
01650 void NPC_BSEmplaced( void )
01651 {
01652 qboolean enemyLOS = qfalse;
01653 qboolean enemyCS = qfalse;
01654 qboolean faceEnemy = qfalse;
01655 qboolean shoot = qfalse;
01656 vec3_t impactPos;
01657
01658
01659 if ( NPC->painDebounceTime > level.time )
01660 {
01661 NPC_UpdateAngles( qtrue, qtrue );
01662 return;
01663 }
01664
01665 if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
01666 {
01667 WeaponThink( qtrue );
01668 }
01669
01670
01671 if ( NPC_CheckEnemyExt(qfalse) == qfalse )
01672 {
01673 if ( !Q_irand( 0, 30 ) )
01674 {
01675 NPCInfo->desiredYaw = NPC->s.angles[1] + Q_irand( -90, 90 );
01676 }
01677 if ( !Q_irand( 0, 30 ) )
01678 {
01679 NPCInfo->desiredPitch = Q_irand( -20, 20 );
01680 }
01681 NPC_UpdateAngles( qtrue, qtrue );
01682 return;
01683 }
01684
01685 if ( NPC_ClearLOS4( NPC->enemy ) )
01686 {
01687 int hit;
01688 gentity_t *hitEnt;
01689
01690 enemyLOS = qtrue;
01691
01692 hit = NPC_ShotEntity( NPC->enemy, impactPos );
01693 hitEnt = &g_entities[hit];
01694
01695 if ( hit == NPC->enemy->s.number || ( hitEnt && hitEnt->takedamage ) )
01696 {
01697 enemyCS = qtrue;
01698 NPC_AimAdjust( 2 );
01699 VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
01700 }
01701 }
01702
01703
01704
01705
01706
01707
01708
01709
01710
01711 if ( enemyLOS )
01712 {
01713 faceEnemy = qtrue;
01714 }
01715 if ( enemyCS )
01716 {
01717 shoot = qtrue;
01718 }
01719
01720 if ( faceEnemy )
01721 {
01722 NPC_FaceEnemy( qtrue );
01723 }
01724 else
01725 {
01726 NPC_UpdateAngles( qtrue, qtrue );
01727 }
01728
01729 if ( NPCInfo->scriptFlags & SCF_DONT_FIRE )
01730 {
01731 shoot = qfalse;
01732 }
01733
01734 if ( NPC->enemy && NPC->enemy->enemy )
01735 {
01736 if ( NPC->enemy->s.weapon == WP_SABER && NPC->enemy->enemy->s.weapon == WP_SABER )
01737 {
01738 shoot = qfalse;
01739 }
01740 }
01741 if ( shoot )
01742 {
01743 if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) )
01744 {
01745 WeaponThink( qtrue );
01746 }
01747 }
01748 }