00001
00002 #include "b_local.h"
00003 #include "g_nav.h"
00004
00005 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
00006 extern void G_SetEnemy( gentity_t *self, gentity_t *enemy );
00007 extern qboolean NPC_CheckLookTarget( gentity_t *self );
00008 extern void NPC_ClearLookTarget( gentity_t *self );
00009 extern void NPC_Jedi_RateNewEnemy( gentity_t *self, gentity_t *enemy );
00010 extern int NAV_FindClosestWaypointForPoint2( vec3_t point );
00011 extern int NAV_GetNearestNode( gentity_t *self, int lastNode );
00012 extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum );
00013 extern qboolean PM_DroidMelee( int npc_class );
00014
00015 void ChangeWeapon( gentity_t *ent, int newWeapon );
00016
00017 void G_ClearEnemy (gentity_t *self)
00018 {
00019 NPC_CheckLookTarget( self );
00020
00021 if ( self->enemy )
00022 {
00023 if( self->client && self->client->renderInfo.lookTarget == self->enemy->s.number )
00024 {
00025 NPC_ClearLookTarget( self );
00026 }
00027
00028 if ( self->NPC && self->enemy == self->NPC->goalEntity )
00029 {
00030 self->NPC->goalEntity = NULL;
00031 }
00032
00033 }
00034
00035 self->enemy = NULL;
00036 }
00037
00038
00039
00040
00041
00042
00043
00044 #define ANGER_ALERT_RADIUS 512
00045 #define ANGER_ALERT_SOUND_RADIUS 256
00046
00047 void G_AngerAlert( gentity_t *self )
00048 {
00049 if ( self && self->NPC && (self->NPC->scriptFlags&SCF_NO_GROUPS) )
00050 {
00051 return;
00052 }
00053 if ( !TIMER_Done( self, "interrogating" ) )
00054 {
00055 return;
00056 }
00057
00058 G_AlertTeam( self, self->enemy, ANGER_ALERT_RADIUS, ANGER_ALERT_SOUND_RADIUS );
00059 }
00060
00061
00062
00063
00064
00065
00066
00067 qboolean G_TeamEnemy( gentity_t *self )
00068 {
00069 int i;
00070 gentity_t *ent;
00071
00072 if ( !self->client || self->client->playerTeam == TEAM_FREE )
00073 {
00074 return qfalse;
00075 }
00076 if ( self && self->NPC && (self->NPC->scriptFlags&SCF_NO_GROUPS) )
00077 {
00078 return qfalse;
00079 }
00080
00081 for( i = 1; i < level.num_entities; i++ )
00082 {
00083 ent = &g_entities[i];
00084
00085 if ( ent == self )
00086 {
00087 continue;
00088 }
00089
00090 if ( ent->health <= 0 )
00091 {
00092 continue;
00093 }
00094
00095 if ( !ent->client )
00096 {
00097 continue;
00098 }
00099
00100 if ( ent->client->playerTeam != self->client->playerTeam )
00101 {
00102 continue;
00103 }
00104
00105 if ( ent->enemy )
00106 {
00107 if ( !ent->enemy->client || ent->enemy->client->playerTeam != self->client->playerTeam )
00108 {
00109 return qtrue;
00110 }
00111 }
00112 }
00113
00114 return qfalse;
00115 }
00116
00117 void G_AttackDelay( gentity_t *self, gentity_t *enemy )
00118 {
00119 if ( enemy && self->client && self->NPC )
00120 {
00121 vec3_t fwd, dir;
00122 int attDelay;
00123
00124 VectorSubtract( self->client->renderInfo.eyePoint, enemy->r.currentOrigin, dir );
00125 VectorNormalize( dir );
00126 AngleVectors( self->client->renderInfo.eyeAngles, fwd, NULL, NULL );
00127
00128
00129 attDelay = (4-g_spskill.integer)*500;
00130 if ( self->client->playerTeam == NPCTEAM_PLAYER )
00131 {
00132 attDelay = 2000-attDelay;
00133 }
00134 attDelay += floor( (DotProduct( fwd, dir )+1.0f) * 2000.0f );
00135
00136
00137
00138
00139
00140 switch ( self->client->NPC_class )
00141 {
00142 case CLASS_IMPERIAL:
00143 attDelay += Q_irand( 500, 1500 );
00144 break;
00145 case CLASS_STORMTROOPER:
00146 if ( self->NPC->rank >= RANK_LT )
00147 {
00148 attDelay -= Q_irand( 500, 1500 );
00149 }
00150 else
00151 {
00152 attDelay -= Q_irand( 0, 1000 );
00153 }
00154 break;
00155 case CLASS_SWAMPTROOPER:
00156 attDelay -= Q_irand( 1000, 2000 );
00157 break;
00158 case CLASS_IMPWORKER:
00159 attDelay += Q_irand( 1000, 2500 );
00160 break;
00161 case CLASS_TRANDOSHAN:
00162 attDelay -= Q_irand( 500, 1500 );
00163 break;
00164 case CLASS_JAN:
00165 case CLASS_LANDO:
00166 case CLASS_PRISONER:
00167 case CLASS_REBEL:
00168 attDelay -= Q_irand( 500, 1500 );
00169 break;
00170 case CLASS_GALAKMECH:
00171 case CLASS_ATST:
00172 attDelay -= Q_irand( 1000, 2000 );
00173 break;
00174 case CLASS_REELO:
00175 case CLASS_UGNAUGHT:
00176 case CLASS_JAWA:
00177 return;
00178 break;
00179 case CLASS_MINEMONSTER:
00180 case CLASS_MURJJ:
00181 return;
00182 break;
00183 case CLASS_INTERROGATOR:
00184 case CLASS_PROBE:
00185 case CLASS_MARK1:
00186 case CLASS_MARK2:
00187 case CLASS_SENTRY:
00188 return;
00189 break;
00190 case CLASS_REMOTE:
00191 case CLASS_SEEKER:
00192 return;
00193 break;
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207 }
00208
00209 switch ( self->s.weapon )
00210 {
00211 case WP_NONE:
00212 case WP_SABER:
00213 return;
00214 break;
00215 case WP_BRYAR_PISTOL:
00216 break;
00217 case WP_BLASTER:
00218 if ( self->NPC->scriptFlags & SCF_ALT_FIRE )
00219 {
00220 attDelay += Q_irand( 0, 500 );
00221 }
00222 else
00223 {
00224 attDelay -= Q_irand( 0, 500 );
00225 }
00226 break;
00227 case WP_BOWCASTER:
00228 attDelay += Q_irand( 0, 500 );
00229 break;
00230 case WP_REPEATER:
00231 if ( !(self->NPC->scriptFlags&SCF_ALT_FIRE) )
00232 {
00233 attDelay += Q_irand( 0, 500 );
00234 }
00235 break;
00236 case WP_FLECHETTE:
00237 attDelay += Q_irand( 500, 1500 );
00238 break;
00239 case WP_ROCKET_LAUNCHER:
00240 attDelay += Q_irand( 500, 1500 );
00241 break;
00242
00243
00244
00245
00246 case WP_DISRUPTOR:
00247 return;
00248 break;
00249 case WP_THERMAL:
00250 return;
00251 break;
00252 case WP_STUN_BATON:
00253 return;
00254 break;
00255 case WP_EMPLACED_GUN:
00256 return;
00257 break;
00258 case WP_TURRET:
00259 return;
00260 break;
00261
00262
00263 break;
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282 }
00283
00284 if ( self->client->playerTeam == NPCTEAM_PLAYER )
00285 {
00286 if ( attDelay > 2000 )
00287 {
00288 attDelay = 2000;
00289 }
00290 }
00291
00292
00293 if ( attDelay > 4000+((2-g_spskill.integer)*3000) )
00294 {
00295 attDelay = 4000+((2-g_spskill.integer)*3000);
00296 }
00297 TIMER_Set( self, "attackDelay", attDelay );
00298
00299 if ( attDelay > 4000 )
00300 {
00301 attDelay = 4000 - Q_irand(500, 1500);
00302 }
00303 else
00304 {
00305 attDelay -= Q_irand(500, 1500);
00306 }
00307
00308 TIMER_Set( self, "roamTime", attDelay );
00309 }
00310 }
00311
00312 void G_ForceSaberOn(gentity_t *ent)
00313 {
00314 if (ent->client->ps.saberInFlight)
00315 {
00316 return;
00317 }
00318
00319 if (!ent->client->ps.saberHolstered)
00320 {
00321 return;
00322 }
00323
00324 if (ent->client->ps.weapon != WP_SABER)
00325 {
00326 return;
00327 }
00328
00329
00330 ent->client->ps.saberHolstered = 0;
00331
00332 if (ent->client->saber[0].soundOn)
00333 {
00334 G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOn);
00335 }
00336 if (ent->client->saber[1].soundOn)
00337 {
00338 G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOn);
00339 }
00340 }
00341
00342
00343
00344
00345
00346
00347
00348 void G_AimSet( gentity_t *self, int aim );
00349 void G_SetEnemy( gentity_t *self, gentity_t *enemy )
00350 {
00351 int event = 0;
00352
00353
00354 if ( enemy == NULL )
00355 return;
00356
00357
00358 if ( enemy->inuse == 0 )
00359 {
00360 return;
00361 }
00362
00363
00364 if ( enemy->flags & FL_NOTARGET )
00365 return;
00366
00367 if ( !self->NPC )
00368 {
00369 self->enemy = enemy;
00370 return;
00371 }
00372
00373 if ( self->NPC->confusionTime > level.time )
00374 {
00375 return;
00376 }
00377
00378 #ifdef _DEBUG
00379 if ( self->s.number )
00380 {
00381 assert( enemy != self );
00382 }
00383 #endif// _DEBUG
00384
00385
00386
00387
00388
00389
00390 if ( self->client && self->NPC && enemy->client && enemy->client->playerTeam == self->client->playerTeam )
00391 {
00392 if ( self->NPC->charmedTime > level.time )
00393 {
00394 return;
00395 }
00396 }
00397
00398 if ( self->NPC && self->client && self->client->ps.weapon == WP_SABER )
00399 {
00400
00401 NPC_Jedi_RateNewEnemy( self, enemy );
00402 }
00403
00404
00405
00406
00407 if ( self->enemy == NULL )
00408 {
00409
00410 if ( self->health > 0 )
00411 {
00412 G_ForceSaberOn(self);
00413 }
00414
00415
00416 G_ClearEnemy( self );
00417 self->enemy = enemy;
00418
00419
00420 if ( self->client->playerTeam == NPCTEAM_PLAYER && enemy->s.number == 0 )
00421 {
00422 self->client->enemyTeam = NPCTEAM_PLAYER;
00423 }
00424
00425
00426 if( G_ActivateBehavior( self, BSET_ANGER ) )
00427 {
00428 }
00429 else if ( self->client && enemy->client && self->client->playerTeam != enemy->client->playerTeam )
00430 {
00431
00432
00433
00434 if (1)
00435 {
00436 if ( !G_TeamEnemy( self ) )
00437 {
00438 event = Q_irand(EV_ANGER1, EV_ANGER3);
00439 }
00440 }
00441
00442 if ( event )
00443 {
00444 G_AddVoiceEvent( self, event, 2000 );
00445 }
00446 }
00447
00448 if ( self->s.weapon == WP_BLASTER || self->s.weapon == WP_REPEATER ||
00449 self->s.weapon == WP_THERMAL
00450 || self->s.weapon == WP_BOWCASTER )
00451 {
00452
00453
00454 if ( self->client->playerTeam == NPCTEAM_PLAYER )
00455 {
00456 G_AimSet( self, Q_irand( self->NPC->stats.aim - (5*(g_spskill.integer)), self->NPC->stats.aim - g_spskill.integer ) );
00457 }
00458 else
00459 {
00460 int minErr = 3;
00461 int maxErr = 12;
00462 if ( self->client->NPC_class == CLASS_IMPWORKER )
00463 {
00464 minErr = 15;
00465 maxErr = 30;
00466 }
00467 else if ( self->client->NPC_class == CLASS_STORMTROOPER && self->NPC && self->NPC->rank <= RANK_CREWMAN )
00468 {
00469 minErr = 5;
00470 maxErr = 15;
00471 }
00472
00473 G_AimSet( self, Q_irand( self->NPC->stats.aim - (maxErr*(3-g_spskill.integer)), self->NPC->stats.aim - (minErr*(3-g_spskill.integer)) ) );
00474 }
00475 }
00476
00477
00478 if ( Q_stricmp( "desperado", self->NPC_type ) != 0 && Q_stricmp( "paladin", self->NPC_type ) != 0 )
00479 {
00480 if ( self->client->ps.fd.forceGripBeingGripped < level.time )
00481 {
00482 G_AngerAlert( self );
00483 }
00484 }
00485
00486
00487 G_AttackDelay( self, enemy );
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510 return;
00511 }
00512
00513
00514
00515 if ( event )
00516 {
00517 G_AddVoiceEvent( self, event, 2000 );
00518 }
00519
00520
00521 G_ClearEnemy(self);
00522 self->enemy = enemy;
00523 }
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570 void ChangeWeapon( gentity_t *ent, int newWeapon )
00571 {
00572 if ( !ent || !ent->client || !ent->NPC )
00573 {
00574 return;
00575 }
00576
00577 ent->client->ps.weapon = newWeapon;
00578 ent->client->pers.cmd.weapon = newWeapon;
00579 ent->NPC->shotTime = 0;
00580 ent->NPC->burstCount = 0;
00581 ent->NPC->attackHold = 0;
00582 ent->NPC->currentAmmo = ent->client->ps.ammo[weaponData[newWeapon].ammoIndex];
00583
00584 switch ( newWeapon )
00585 {
00586 case WP_BRYAR_PISTOL:
00587 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00588 ent->NPC->burstSpacing = 1000;
00589 break;
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618 case WP_SABER:
00619 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00620 ent->NPC->burstSpacing = 0;
00621 break;
00622
00623 case WP_DISRUPTOR:
00624 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00625 if ( ent->NPC->scriptFlags & SCF_ALT_FIRE )
00626 {
00627 switch( g_spskill.integer )
00628 {
00629 case 0:
00630 ent->NPC->burstSpacing = 2500;
00631 break;
00632 case 1:
00633 ent->NPC->burstSpacing = 2000;
00634 break;
00635 case 2:
00636 ent->NPC->burstSpacing = 1500;
00637 break;
00638 }
00639 }
00640 else
00641 {
00642 ent->NPC->burstSpacing = 1000;
00643 }
00644 break;
00645
00646 case WP_BOWCASTER:
00647 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00648
00649 if ( g_spskill.integer == 0 )
00650 ent->NPC->burstSpacing = 1000;
00651 else if ( g_spskill.integer == 1 )
00652 ent->NPC->burstSpacing = 750;
00653 else
00654 ent->NPC->burstSpacing = 500;
00655 break;
00656
00657 case WP_REPEATER:
00658 if ( ent->NPC->scriptFlags & SCF_ALT_FIRE )
00659 {
00660 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00661 ent->NPC->burstSpacing = 2000;
00662 }
00663 else
00664 {
00665 ent->NPC->aiFlags |= NPCAI_BURST_WEAPON;
00666 ent->NPC->burstMin = 3;
00667 ent->NPC->burstMean = 6;
00668 ent->NPC->burstMax = 10;
00669 if ( g_spskill.integer == 0 )
00670 ent->NPC->burstSpacing = 1500;
00671 else if ( g_spskill.integer == 1 )
00672 ent->NPC->burstSpacing = 1000;
00673 else
00674 ent->NPC->burstSpacing = 500;
00675 }
00676 break;
00677
00678 case WP_DEMP2:
00679 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00680 ent->NPC->burstSpacing = 1000;
00681 break;
00682
00683 case WP_FLECHETTE:
00684 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00685 if ( ent->NPC->scriptFlags & SCF_ALT_FIRE )
00686 {
00687 ent->NPC->burstSpacing = 2000;
00688 }
00689 else
00690 {
00691 ent->NPC->burstSpacing = 1000;
00692 }
00693 break;
00694
00695 case WP_ROCKET_LAUNCHER:
00696 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00697
00698 if ( g_spskill.integer == 0 )
00699 ent->NPC->burstSpacing = 2500;
00700 else if ( g_spskill.integer == 1 )
00701 ent->NPC->burstSpacing = 2000;
00702 else
00703 ent->NPC->burstSpacing = 1500;
00704 break;
00705
00706 case WP_THERMAL:
00707 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00708
00709 if ( g_spskill.integer == 0 )
00710 ent->NPC->burstSpacing = 3000;
00711 else if ( g_spskill.integer == 1 )
00712 ent->NPC->burstSpacing = 2500;
00713 else
00714 ent->NPC->burstSpacing = 2000;
00715 break;
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736 case WP_BLASTER:
00737 if ( ent->NPC->scriptFlags & SCF_ALT_FIRE )
00738 {
00739 ent->NPC->aiFlags |= NPCAI_BURST_WEAPON;
00740 ent->NPC->burstMin = 3;
00741 ent->NPC->burstMean = 3;
00742 ent->NPC->burstMax = 3;
00743 if ( g_spskill.integer == 0 )
00744 ent->NPC->burstSpacing = 1500;
00745 else if ( g_spskill.integer == 1 )
00746 ent->NPC->burstSpacing = 1000;
00747 else
00748 ent->NPC->burstSpacing = 500;
00749 }
00750 else
00751 {
00752 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00753 if ( g_spskill.integer == 0 )
00754 ent->NPC->burstSpacing = 1000;
00755 else if ( g_spskill.integer == 1 )
00756 ent->NPC->burstSpacing = 750;
00757 else
00758 ent->NPC->burstSpacing = 500;
00759
00760 }
00761 break;
00762
00763 case WP_STUN_BATON:
00764 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00765 ent->NPC->burstSpacing = 1000;
00766 break;
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783 case WP_EMPLACED_GUN:
00784
00785 if ( ent->client && ent->client->NPC_class == CLASS_REELO )
00786 {
00787 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00788 ent->NPC->burstSpacing = 1000;
00789
00790
00791
00792
00793
00794
00795 }
00796 else
00797 {
00798 ent->NPC->aiFlags |= NPCAI_BURST_WEAPON;
00799 ent->NPC->burstMin = 2;
00800 ent->NPC->burstMean = 2;
00801 ent->NPC->burstMax = 2;
00802
00803 if ( ent->parent )
00804 {
00805 if ( g_spskill.integer == 0 )
00806 {
00807 ent->NPC->burstSpacing = ent->parent->wait + 400;
00808 ent->NPC->burstMin = ent->NPC->burstMax = 1;
00809 }
00810 else if ( g_spskill.integer == 1 )
00811 {
00812 ent->NPC->burstSpacing = ent->parent->wait + 200;
00813 }
00814 else
00815 {
00816 ent->NPC->burstSpacing = ent->parent->wait;
00817 }
00818 }
00819 else
00820 {
00821 if ( g_spskill.integer == 0 )
00822 {
00823 ent->NPC->burstSpacing = 1200;
00824 ent->NPC->burstMin = ent->NPC->burstMax = 1;
00825 }
00826 else if ( g_spskill.integer == 1 )
00827 {
00828 ent->NPC->burstSpacing = 1000;
00829 }
00830 else
00831 {
00832 ent->NPC->burstSpacing = 800;
00833 }
00834 }
00835 }
00836 break;
00837
00838 default:
00839 ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON;
00840 break;
00841 }
00842 }
00843
00844 void NPC_ChangeWeapon( int newWeapon )
00845 {
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873 }
00874
00875
00876
00877
00878 void NPC_ApplyWeaponFireDelay(void)
00879 {
00880 if ( NPC->attackDebounceTime > level.time )
00881 {
00882
00883 return;
00884 }
00885
00886 switch(client->ps.weapon)
00887 {
00888
00889
00890
00891
00892
00893
00894
00895 case WP_THERMAL:
00896 if ( client->ps.clientNum )
00897 {
00898
00899
00900
00901
00902 client->ps.weaponTime = 700;
00903 }
00904 break;
00905
00906 case WP_STUN_BATON:
00907
00908 if (1)
00909 {
00910 client->ps.weaponTime = 300;
00911 }
00912 break;
00913
00914 default:
00915 client->ps.weaponTime = 0;
00916 break;
00917 }
00918 }
00919
00920
00921
00922
00923
00924
00925 void ShootThink( void )
00926 {
00927 int delay;
00928
00929 ucmd.buttons &= ~BUTTON_ATTACK;
00930
00931
00932
00933
00934
00935 if ( client->ps.weapon == WP_NONE )
00936 return;
00937
00938 if ( client->ps.weaponstate != WEAPON_READY && client->ps.weaponstate != WEAPON_FIRING && client->ps.weaponstate != WEAPON_IDLE)
00939 return;
00940
00941 if ( level.time < NPCInfo->shotTime )
00942 {
00943 return;
00944 }
00945
00946 ucmd.buttons |= BUTTON_ATTACK;
00947
00948 NPCInfo->currentAmmo = client->ps.ammo[weaponData[client->ps.weapon].ammoIndex];
00949
00950 NPC_ApplyWeaponFireDelay();
00951
00952 if ( NPCInfo->aiFlags & NPCAI_BURST_WEAPON )
00953 {
00954 if ( !NPCInfo->burstCount )
00955 {
00956 NPCInfo->burstCount = Q_irand( NPCInfo->burstMin, NPCInfo->burstMax );
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968 delay = 0;
00969 }
00970 else
00971 {
00972 NPCInfo->burstCount--;
00973 if ( NPCInfo->burstCount == 0 )
00974 {
00975 delay = NPCInfo->burstSpacing;
00976 }
00977 else
00978 {
00979 delay = 0;
00980 }
00981 }
00982
00983 if ( !delay )
00984 {
00985
00986 if ( client->ps.weapon == WP_EMPLACED_GUN )
00987 {
00988 if ( NPC->parent )
00989 {
00990 if ( g_spskill.integer == 0 )
00991 {
00992 delay = NPC->parent->random + 150;
00993 }
00994 else if ( g_spskill.integer == 1 )
00995 {
00996 delay = NPC->parent->random + 100;
00997 }
00998 else
00999 {
01000 delay = NPC->parent->random;
01001 }
01002 }
01003 else
01004 {
01005 if ( g_spskill.integer == 0 )
01006 {
01007 delay = 350;
01008 }
01009 else if ( g_spskill.integer == 1 )
01010 {
01011 delay = 300;
01012 }
01013 else
01014 {
01015 delay = 200;
01016 }
01017 }
01018 }
01019 }
01020 }
01021 else
01022 {
01023 delay = NPCInfo->burstSpacing;
01024 }
01025
01026 NPCInfo->shotTime = level.time + delay;
01027 NPC->attackDebounceTime = level.time + NPC_AttackDebounceForWeapon();
01028 }
01029
01030
01031
01032
01033
01034
01035
01036 void WeaponThink( qboolean inCombat )
01037 {
01038 if ( client->ps.weaponstate == WEAPON_RAISING || client->ps.weaponstate == WEAPON_DROPPING )
01039 {
01040 ucmd.weapon = client->ps.weapon;
01041 ucmd.buttons &= ~BUTTON_ATTACK;
01042 return;
01043 }
01044
01045
01046
01047 if(NPC->client->ps.ammo[ weaponData[client->ps.weapon].ammoIndex ] < 10)
01048
01049 {
01050 Add_Ammo (NPC, client->ps.weapon, 100);
01051 }
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083 {
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099 }
01100
01101 ucmd.weapon = client->ps.weapon;
01102 ShootThink();
01103 }
01104
01105
01106
01107
01108
01109 qboolean HaveWeapon( int weapon )
01110 {
01111 return ( client->ps.stats[STAT_WEAPONS] & ( 1 << weapon ) );
01112 }
01113
01114 qboolean EntIsGlass (gentity_t *check)
01115 {
01116 if(check->classname &&
01117 !Q_stricmp("func_breakable", check->classname) &&
01118 check->count == 1 && check->health <= 100)
01119 {
01120 return qtrue;
01121 }
01122
01123 return qfalse;
01124 }
01125
01126 qboolean ShotThroughGlass (trace_t *tr, gentity_t *target, vec3_t spot, int mask)
01127 {
01128 gentity_t *hit = &g_entities[ tr->entityNum ];
01129 if(hit != target && EntIsGlass(hit))
01130 {
01131 int skip = hit->s.number;
01132 vec3_t muzzle;
01133
01134 VectorCopy(tr->endpos, muzzle);
01135 trap_Trace (tr, muzzle, NULL, NULL, spot, skip, mask );
01136 return qtrue;
01137 }
01138
01139 return qfalse;
01140 }
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150 qboolean CanShoot ( gentity_t *ent, gentity_t *shooter )
01151 {
01152 trace_t tr;
01153 vec3_t muzzle;
01154 vec3_t spot, diff;
01155 gentity_t *traceEnt;
01156
01157 CalcEntitySpot( shooter, SPOT_WEAPON, muzzle );
01158 CalcEntitySpot( ent, SPOT_ORIGIN, spot );
01159
01160 trap_Trace ( &tr, muzzle, NULL, NULL, spot, shooter->s.number, MASK_SHOT );
01161 traceEnt = &g_entities[ tr.entityNum ];
01162
01163
01164 if (tr.startsolid && (shooter->NPC) && (shooter->NPC->touchedByPlayer) )
01165 {
01166 traceEnt = shooter->NPC->touchedByPlayer;
01167 }
01168
01169 if ( ShotThroughGlass( &tr, ent, spot, MASK_SHOT ) )
01170 {
01171 traceEnt = &g_entities[ tr.entityNum ];
01172 }
01173
01174
01175 if ( traceEnt == ent )
01176 {
01177 return qtrue;
01178 }
01179
01180 else
01181 {
01182 CalcEntitySpot( ent, SPOT_HEAD, spot );
01183 trap_Trace ( &tr, muzzle, NULL, NULL, spot, shooter->s.number, MASK_SHOT );
01184 traceEnt = &g_entities[ tr.entityNum ];
01185 if ( traceEnt == ent)
01186 {
01187 return qtrue;
01188 }
01189 }
01190
01191
01192
01193 VectorSubtract(spot, tr.endpos, diff);
01194 if(VectorLength(diff) < random() * 32)
01195 {
01196 return qtrue;
01197 }
01198
01199
01200 if ( !traceEnt->client )
01201 {
01202 return qfalse;
01203 }
01204
01205
01206
01207
01208 if ( traceEnt->health <= 0 )
01209 {
01210 return qtrue;
01211 }
01212
01213
01214 if ( traceEnt->client && ( traceEnt->client->playerTeam == shooter->client->playerTeam ) )
01215 {
01216 return qfalse;
01217 }
01218
01219
01220 return qtrue;
01221 }
01222
01223
01224
01225
01226
01227
01228
01229 void NPC_CheckPossibleEnemy( gentity_t *other, visibility_t vis )
01230 {
01231
01232 if ( other == NPC->enemy )
01233 return;
01234
01235 if ( other->flags & FL_NOTARGET )
01236 return;
01237
01238
01239 if ( NPC->enemy && vis == VIS_FOV )
01240 {
01241 if ( NPCInfo->enemyLastSeenTime - level.time < 2000 )
01242 {
01243 return;
01244 }
01245 if ( enemyVisibility == VIS_UNKNOWN )
01246 {
01247 enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_360|CHECK_FOV );
01248 }
01249 if ( enemyVisibility == VIS_FOV )
01250 {
01251 return;
01252 }
01253 }
01254
01255 if ( !NPC->enemy )
01256 {
01257 G_SetEnemy( NPC, other );
01258 }
01259
01260 if ( vis == VIS_FOV )
01261 {
01262 NPCInfo->enemyLastSeenTime = level.time;
01263 VectorCopy( other->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
01264 NPCInfo->enemyLastHeardTime = 0;
01265 VectorClear( NPCInfo->enemyLastHeardLocation );
01266 }
01267 else
01268 {
01269 NPCInfo->enemyLastSeenTime = 0;
01270 VectorClear( NPCInfo->enemyLastSeenLocation );
01271 NPCInfo->enemyLastHeardTime = level.time;
01272 VectorCopy( other->r.currentOrigin, NPCInfo->enemyLastHeardLocation );
01273 }
01274 }
01275
01276
01277
01278
01279
01280
01281
01282