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->tem