00001
00002
00003 #include "b_local.h"
00004
00005 extern int eventClearTime;
00006
00007
00008
00009
00010
00011 qboolean G_ClearLineOfSight(const vec3_t point1, const vec3_t point2, int ignore, int clipmask)
00012 {
00013 trace_t tr;
00014 gentity_t *hit;
00015
00016 trap_Trace ( &tr, point1, NULL, NULL, point2, ignore, clipmask );
00017 if ( tr.fraction == 1.0 )
00018 {
00019 return qtrue;
00020 }
00021
00022 hit = &g_entities[ tr.entityNum ];
00023 if(EntIsGlass(hit))
00024 {
00025 vec3_t newpoint1;
00026 VectorCopy(tr.endpos, newpoint1);
00027 trap_Trace (&tr, newpoint1, NULL, NULL, point2, hit->s.number, clipmask );
00028
00029 if ( tr.fraction == 1.0 )
00030 {
00031 return qtrue;
00032 }
00033 }
00034
00035 return qfalse;
00036 }
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 qboolean CanSee ( gentity_t *ent )
00048 {
00049 trace_t tr;
00050 vec3_t eyes;
00051 vec3_t spot;
00052
00053 CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
00054
00055 CalcEntitySpot( ent, SPOT_ORIGIN, spot );
00056 trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
00057 ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
00058 if ( tr.fraction == 1.0 )
00059 {
00060 return qtrue;
00061 }
00062
00063 CalcEntitySpot( ent, SPOT_HEAD, spot );
00064 trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
00065 ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
00066 if ( tr.fraction == 1.0 )
00067 {
00068 return qtrue;
00069 }
00070
00071 CalcEntitySpot( ent, SPOT_LEGS, spot );
00072 trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
00073 ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
00074 if ( tr.fraction == 1.0 )
00075 {
00076 return qtrue;
00077 }
00078
00079 return qfalse;
00080 }
00081
00082 qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold )
00083 {
00084 vec3_t dir, forward, angles;
00085 float dot;
00086
00087 VectorSubtract( spot, from, dir );
00088 dir[2] = 0;
00089 VectorNormalize( dir );
00090
00091 VectorCopy( fromAngles, angles );
00092 angles[0] = 0;
00093 AngleVectors( angles, forward, NULL, NULL );
00094
00095 dot = DotProduct( dir, forward );
00096
00097 return (dot > threshHold);
00098 }
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109 qboolean InFOV3( vec3_t spot, vec3_t from, vec3_t fromAngles, int hFOV, int vFOV )
00110 {
00111 vec3_t deltaVector, angles, deltaAngles;
00112
00113 VectorSubtract ( spot, from, deltaVector );
00114 vectoangles ( deltaVector, angles );
00115
00116 deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00117 deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
00118
00119 if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
00120 {
00121 return qtrue;
00122 }
00123
00124 return qfalse;
00125 }
00126
00127
00128
00129 qboolean InFOV2( vec3_t origin, gentity_t *from, int hFOV, int vFOV )
00130 {
00131 vec3_t fromAngles, eyes;
00132
00133 if( from->client )
00134 {
00135 VectorCopy(from->client->ps.viewangles, fromAngles);
00136 }
00137 else
00138 {
00139 VectorCopy(from->s.angles, fromAngles);
00140 }
00141
00142 CalcEntitySpot( from, SPOT_HEAD, eyes );
00143
00144 return InFOV3( origin, eyes, fromAngles, hFOV, vFOV );
00145 }
00146
00147
00148
00149 qboolean InFOV ( gentity_t *ent, gentity_t *from, int hFOV, int vFOV )
00150 {
00151 vec3_t eyes;
00152 vec3_t spot;
00153 vec3_t deltaVector;
00154 vec3_t angles, fromAngles;
00155 vec3_t deltaAngles;
00156
00157 if( from->client )
00158 {
00159 if( !VectorCompare( from->client->renderInfo.eyeAngles, vec3_origin ) )
00160 {
00161
00162 VectorCopy( from->client->renderInfo.eyeAngles, fromAngles );
00163 }
00164 else
00165 {
00166 VectorCopy( from->client->ps.viewangles, fromAngles );
00167 }
00168 }
00169 else
00170 {
00171 VectorCopy(from->s.angles, fromAngles);
00172 }
00173
00174 CalcEntitySpot( from, SPOT_HEAD_LEAN, eyes );
00175
00176 CalcEntitySpot( ent, SPOT_ORIGIN, spot );
00177 VectorSubtract ( spot, eyes, deltaVector);
00178
00179 vectoangles ( deltaVector, angles );
00180 deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00181 deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
00182 if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
00183 {
00184 return qtrue;
00185 }
00186
00187 CalcEntitySpot( ent, SPOT_HEAD, spot );
00188 VectorSubtract ( spot, eyes, deltaVector);
00189 vectoangles ( deltaVector, angles );
00190 deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00191 deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
00192 if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
00193 {
00194 return qtrue;
00195 }
00196
00197 CalcEntitySpot( ent, SPOT_LEGS, spot );
00198 VectorSubtract ( spot, eyes, deltaVector);
00199 vectoangles ( deltaVector, angles );
00200 deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
00201 deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
00202 if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
00203 {
00204 return qtrue;
00205 }
00206
00207 return qfalse;
00208 }
00209
00210 qboolean InVisrange ( gentity_t *ent )
00211 {
00212
00213 vec3_t eyes;
00214 vec3_t spot;
00215 vec3_t deltaVector;
00216 float visrange = (NPCInfo->stats.visrange*NPCInfo->stats.visrange);
00217
00218 CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
00219
00220 CalcEntitySpot( ent, SPOT_ORIGIN, spot );
00221 VectorSubtract ( spot, eyes, deltaVector);
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245 if(VectorLengthSquared(deltaVector) > visrange)
00246 {
00247 return qfalse;
00248 }
00249
00250 return qtrue;
00251 }
00252
00253
00254
00255
00256
00257 visibility_t NPC_CheckVisibility ( gentity_t *ent, int flags )
00258 {
00259
00260 if ( !flags )
00261 {
00262 return VIS_NOT;
00263 }
00264
00265
00266 if ( flags & CHECK_PVS )
00267 {
00268 if ( !trap_InPVS ( ent->r.currentOrigin, NPC->r.currentOrigin ) )
00269 {
00270 return VIS_NOT;
00271 }
00272 }
00273 if ( !(flags & (CHECK_360|CHECK_FOV|CHECK_SHOOT)) )
00274 {
00275 return VIS_PVS;
00276 }
00277
00278
00279 if (flags & CHECK_VISRANGE)
00280 {
00281 if( !InVisrange ( ent ) )
00282 {
00283 return VIS_PVS;
00284 }
00285 }
00286
00287
00288
00289 if ( flags & CHECK_360 )
00290 {
00291 if ( !CanSee ( ent ) )
00292 {
00293 return VIS_PVS;
00294 }
00295 }
00296 if ( !(flags & (CHECK_FOV|CHECK_SHOOT)) )
00297 {
00298 return VIS_360;
00299 }
00300
00301
00302 if ( flags & CHECK_FOV )
00303 {
00304 if ( !InFOV ( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov) )
00305 {
00306 return VIS_360;
00307 }
00308 }
00309
00310 if ( !(flags & CHECK_SHOOT) )
00311 {
00312 return VIS_FOV;
00313 }
00314
00315
00316 if ( flags & CHECK_SHOOT )
00317 {
00318 if ( !CanShoot ( ent, NPC ) )
00319 {
00320 return VIS_FOV;
00321 }
00322 }
00323
00324 return VIS_SHOOT;
00325 }
00326
00327
00328
00329
00330
00331
00332 static int G_CheckSoundEvents( gentity_t *self, float maxHearDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
00333 {
00334 int bestEvent = -1;
00335 int bestAlert = -1;
00336 int bestTime = -1;
00337 int i;
00338 float dist, radius;
00339
00340 maxHearDist *= maxHearDist;
00341
00342 for ( i = 0; i < level.numAlertEvents; i++ )
00343 {
00344
00345 if ( i == ignoreAlert )
00346 continue;
00347
00348 if ( level.alertEvents[i].type != AET_SOUND )
00349 continue;
00350
00351 if ( level.alertEvents[i].level < minAlertLevel )
00352 continue;
00353
00354 if ( mustHaveOwner && !level.alertEvents[i].owner )
00355 continue;
00356
00357 dist = DistanceSquared( level.alertEvents[i].position, self->r.currentOrigin );
00358
00359
00360 if ( dist > maxHearDist )
00361 continue;
00362
00363 radius = level.alertEvents[i].radius * level.alertEvents[i].radius;
00364 if ( dist > radius )
00365 continue;
00366
00367 if ( level.alertEvents[i].addLight )
00368 {
00369 if ( G_ClearLOS5( self, level.alertEvents[i].position ) == qfalse )
00370 {
00371 continue;
00372 }
00373 }
00374
00375
00376 if ( level.alertEvents[i].level >= bestAlert
00377 || (level.alertEvents[i].level==bestAlert&&level.alertEvents[i].timestamp >= bestTime) )
00378 {
00379 bestEvent = i;
00380 bestAlert = level.alertEvents[i].level;
00381 bestTime = level.alertEvents[i].timestamp;
00382 }
00383 }
00384
00385 return bestEvent;
00386 }
00387
00388 float G_GetLightLevel( vec3_t pos, vec3_t fromDir )
00389 {
00390
00391
00392
00393
00394
00395
00396 float lightLevel;
00397
00398
00399 lightLevel = 255;
00400
00401 return lightLevel;
00402 }
00403
00404
00405
00406
00407
00408 static int G_CheckSightEvents( gentity_t *self, int hFOV, int vFOV, float maxSeeDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
00409 {
00410 int bestEvent = -1;
00411 int bestAlert = -1;
00412 int bestTime = -1;
00413 int i;
00414 float dist, radius;
00415
00416 maxSeeDist *= maxSeeDist;
00417 for ( i = 0; i < level.numAlertEvents; i++ )
00418 {
00419
00420 if ( i == ignoreAlert )
00421 continue;
00422
00423 if ( level.alertEvents[i].type != AET_SIGHT )
00424 continue;
00425
00426 if ( level.alertEvents[i].level < minAlertLevel )
00427 continue;
00428
00429 if ( mustHaveOwner && !level.alertEvents[i].owner )
00430 continue;
00431
00432
00433 dist = DistanceSquared( level.alertEvents[i].position, self->r.currentOrigin );
00434
00435
00436 if ( dist > maxSeeDist )
00437 continue;
00438
00439 radius = level.alertEvents[i].radius * level.alertEvents[i].radius;
00440 if ( dist > radius )
00441 continue;
00442
00443
00444 if ( InFOV2( level.alertEvents[i].position, self, hFOV, vFOV ) == qfalse )
00445 continue;
00446
00447 if ( G_ClearLOS5( self, level.alertEvents[i].position ) == qfalse )
00448 continue;
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458 if ( level.alertEvents[i].level >= bestAlert
00459 || (level.alertEvents[i].level==bestAlert&&level.alertEvents[i].timestamp >= bestTime) )
00460 {
00461 bestEvent = i;
00462 bestAlert = level.alertEvents[i].level;
00463 bestTime = level.alertEvents[i].timestamp;
00464 }
00465 }
00466
00467 return bestEvent;
00468 }
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478 int G_CheckAlertEvents( gentity_t *self, qboolean checkSight, qboolean checkSound, float maxSeeDist, float maxHearDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
00479 {
00480 int bestSoundEvent = -1;
00481 int bestSightEvent = -1;
00482 int bestSoundAlert = -1;
00483 int bestSightAlert = -1;
00484
00485 if ( &g_entities[0] == NULL || g_entities[0].health <= 0 )
00486 {
00487
00488 return -1;
00489 }
00490
00491
00492 bestSoundEvent = G_CheckSoundEvents( self, maxHearDist, ignoreAlert, mustHaveOwner, minAlertLevel );
00493
00494 if ( bestSoundEvent >= 0 )
00495 {
00496 bestSoundAlert = level.alertEvents[bestSoundEvent].level;
00497 }
00498
00499
00500 if ( self->NPC )
00501 {
00502 bestSightEvent = G_CheckSightEvents( self, self->NPC->stats.hfov, self->NPC->stats.vfov, maxSeeDist, ignoreAlert, mustHaveOwner, minAlertLevel );
00503 }
00504 else
00505 {
00506 bestSightEvent = G_CheckSightEvents( self, 80, 80, maxSeeDist, ignoreAlert, mustHaveOwner, minAlertLevel );
00507 }
00508
00509 if ( bestSightEvent >= 0 )
00510 {
00511 bestSightAlert = level.alertEvents[bestSightEvent].level;
00512 }
00513
00514
00515
00516
00517 if ( bestSightEvent >= 0 && bestSightAlert > bestSoundAlert )
00518 {
00519
00520 vec3_t eyePoint, sightDir;
00521
00522 CalcEntitySpot( self, SPOT_HEAD_LEAN, eyePoint );
00523 VectorSubtract( level.alertEvents[bestSightEvent].position, eyePoint, sightDir );
00524 level.alertEvents[bestSightEvent].light = level.alertEvents[bestSightEvent].addLight + G_GetLightLevel( level.alertEvents[bestSightEvent].position, sightDir );
00525
00526 return bestSightEvent;
00527 }
00528
00529 return bestSoundEvent;
00530 }
00531
00532 int NPC_CheckAlertEvents( qboolean checkSight, qboolean checkSound, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
00533 {
00534 return G_CheckAlertEvents( NPC, checkSight, checkSound, NPCInfo->stats.visrange, NPCInfo->stats.earshot, ignoreAlert, mustHaveOwner, minAlertLevel );
00535 }
00536
00537 qboolean G_CheckForDanger( gentity_t *self, int alertEvent )
00538 {
00539 if ( alertEvent == -1 )
00540 {
00541 return qfalse;
00542 }
00543
00544 if ( level.alertEvents[alertEvent].level >= AEL_DANGER )
00545 {
00546 if ( !level.alertEvents[alertEvent].owner || !level.alertEvents[alertEvent].owner->client || (level.alertEvents[alertEvent].owner!=self&&level.alertEvents[alertEvent].owner->client->playerTeam!=self->client->playerTeam) )
00547 {
00548 if ( self->NPC )
00549 {
00550 if ( self->NPC->scriptFlags & SCF_DONT_FLEE )
00551 {
00552 return qfalse;
00553 }
00554 else
00555 {
00556 NPC_StartFlee( level.alertEvents[alertEvent].owner, level.alertEvents[alertEvent].position, level.alertEvents[alertEvent].level, 3000, 6000 );
00557 return qtrue;
00558 }
00559 }
00560 else
00561 {
00562 return qtrue;
00563 }
00564 }
00565 }
00566 return qfalse;
00567 }
00568 qboolean NPC_CheckForDanger( int alertEvent )
00569 {
00570 return G_CheckForDanger( NPC, alertEvent );
00571 }
00572
00573
00574
00575
00576
00577
00578 qboolean RemoveOldestAlert( void );
00579 void AddSoundEvent( gentity_t *owner, vec3_t position, float radius, alertEventLevel_e alertLevel, qboolean needLOS )
00580 {
00581
00582 if ( level.numAlertEvents >= MAX_ALERT_EVENTS )
00583 {
00584 if ( !RemoveOldestAlert() )
00585 {
00586 return;
00587 }
00588 }
00589
00590 if ( owner == NULL && alertLevel < AEL_DANGER )
00591 return;
00592
00593
00594
00595
00596
00597 VectorCopy( position, level.alertEvents[ level.numAlertEvents ].position );
00598
00599 level.alertEvents[ level.numAlertEvents ].radius = radius;
00600 level.alertEvents[ level.numAlertEvents ].level = alertLevel;
00601 level.alertEvents[ level.numAlertEvents ].type = AET_SOUND;
00602 level.alertEvents[ level.numAlertEvents ].owner = owner;
00603 if ( needLOS )
00604 {
00605 level.alertEvents[ level.numAlertEvents ].addLight = 1;
00606 }
00607 else
00608 {
00609 level.alertEvents[ level.numAlertEvents ].addLight = 0;
00610 }
00611 level.alertEvents[ level.numAlertEvents ].ID = level.curAlertID++;
00612 level.alertEvents[ level.numAlertEvents ].timestamp = level.time;
00613
00614 level.numAlertEvents++;
00615 }
00616
00617
00618
00619
00620
00621
00622
00623 void AddSightEvent( gentity_t *owner, vec3_t position, float radius, alertEventLevel_e alertLevel, float addLight )
00624 {
00625
00626 if ( level.numAlertEvents >= MAX_ALERT_EVENTS )
00627 {
00628 if ( !RemoveOldestAlert() )
00629 {
00630 return;
00631 }
00632 }
00633
00634 if ( owner == NULL && alertLevel < AEL_DANGER )
00635 return;
00636
00637
00638
00639
00640
00641 VectorCopy( position, level.alertEvents[ level.numAlertEvents ].position );
00642
00643 level.alertEvents[ level.numAlertEvents ].radius = radius;
00644 level.alertEvents[ level.numAlertEvents ].level = alertLevel;
00645 level.alertEvents[ level.numAlertEvents ].type = AET_SIGHT;
00646 level.alertEvents[ level.numAlertEvents ].owner = owner;
00647 level.alertEvents[ level.numAlertEvents ].addLight = addLight;
00648 level.alertEvents[ level.numAlertEvents ].ID = level.curAlertID++;
00649 level.alertEvents[ level.numAlertEvents ].timestamp = level.time;
00650
00651 level.numAlertEvents++;
00652 }
00653
00654
00655
00656
00657
00658
00659
00660 void ClearPlayerAlertEvents( void )
00661 {
00662 int curNumAlerts = level.numAlertEvents;
00663 int i;
00664
00665 for ( i = 0; i < curNumAlerts; i++ )
00666 {
00667
00668 if ( level.alertEvents[i].timestamp && level.alertEvents[i].timestamp + ALERT_CLEAR_TIME < level.time )
00669 {
00670
00671 level.numAlertEvents--;
00672
00673 if ( level.numAlertEvents > 0 )
00674 {
00675 if ( (i+1) < MAX_ALERT_EVENTS )
00676 {
00677 memmove( &level.alertEvents[i], &level.alertEvents[i+1], sizeof(alertEvent_t)*(MAX_ALERT_EVENTS-(i+1) ) );
00678 }
00679 }
00680 else
00681 {
00682 memset( &level.alertEvents[i], 0, sizeof( alertEvent_t ) );
00683 }
00684 }
00685 }
00686
00687 assert( level.numAlertEvents >= 0 );
00688
00689 if ( eventClearTime < level.time )
00690 {
00691 eventClearTime = level.time + ALERT_CLEAR_TIME;
00692 }
00693 }
00694
00695 qboolean RemoveOldestAlert( void )
00696 {
00697 int oldestEvent = -1, oldestTime = Q3_INFINITE;
00698 int i;
00699
00700 for ( i = 0; i < level.numAlertEvents; i++ )
00701 {
00702
00703 if ( level.alertEvents[i].timestamp < oldestTime )
00704 {
00705 oldestEvent = i;
00706 oldestTime = level.alertEvents[i].timestamp;
00707 }
00708 }
00709 if ( oldestEvent != -1 )
00710 {
00711
00712 level.numAlertEvents--;
00713
00714 if ( level.numAlertEvents > 0 )
00715 {
00716 if ( (oldestEvent+1) < MAX_ALERT_EVENTS )
00717 {
00718 memmove( &level.alertEvents[oldestEvent], &level.alertEvents[oldestEvent+1], sizeof(alertEvent_t)*(MAX_ALERT_EVENTS-(oldestEvent+1) ) );
00719 }
00720 }
00721 else
00722 {
00723 memset( &level.alertEvents[oldestEvent], 0, sizeof( alertEvent_t ) );
00724 }
00725 }
00726
00727 assert( level.numAlertEvents >= 0 );
00728
00729 return (level.numAlertEvents<MAX_ALERT_EVENTS);
00730 }
00731
00732
00733
00734
00735
00736
00737
00738
00739 qboolean G_ClearLOS( gentity_t *self, const vec3_t start, const vec3_t end )
00740 {
00741 trace_t tr;
00742 int traceCount = 0;
00743
00744
00745 trap_Trace ( &tr, start, NULL, NULL, end, ENTITYNUM_NONE, CONTENTS_OPAQUE );
00746 while ( tr.fraction < 1.0 && traceCount < 3 )
00747 {
00748 if ( tr.entityNum < ENTITYNUM_WORLD )
00749 {
00750 if ( &g_entities[tr.entityNum] != NULL && (g_entities[tr.entityNum].r.svFlags&SVF_GLASS_BRUSH) )
00751 {
00752 trap_Trace ( &tr, tr.endpos, NULL, NULL, end, tr.entityNum, MASK_OPAQUE );
00753 traceCount++;
00754 continue;
00755 }
00756 }
00757 return qfalse;
00758 }
00759
00760 if ( tr.fraction == 1.0 )
00761 return qtrue;
00762
00763 return qfalse;
00764 }
00765
00766
00767 qboolean G_ClearLOS2( gentity_t *self, gentity_t *ent, const vec3_t end )
00768 {
00769 vec3_t eyes;
00770
00771 CalcEntitySpot( ent, SPOT_HEAD_LEAN, eyes );
00772
00773 return G_ClearLOS( self, eyes, end );
00774 }
00775
00776
00777 qboolean G_ClearLOS3( gentity_t *self, const vec3_t start, gentity_t *ent )
00778 {
00779 vec3_t spot;
00780
00781
00782 CalcEntitySpot( ent, SPOT_ORIGIN, spot );
00783
00784 if ( G_ClearLOS( self, start, spot ) )
00785 return qtrue;
00786
00787
00788 CalcEntitySpot( ent, SPOT_HEAD_LEAN, spot );
00789
00790 if ( G_ClearLOS( self, start, spot ) )
00791 return qtrue;
00792
00793 return qfalse;
00794 }
00795
00796
00797 qboolean G_ClearLOS4( gentity_t *self, gentity_t *ent )
00798 {
00799 vec3_t eyes;
00800
00801
00802 CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes );
00803
00804 return G_ClearLOS3( self, eyes, ent );
00805 }
00806
00807
00808 qboolean G_ClearLOS5( gentity_t *self, const vec3_t end )
00809 {
00810 vec3_t eyes;
00811
00812
00813 CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes );
00814
00815 return G_ClearLOS( self, eyes, end );
00816 }
00817
00818
00819
00820
00821
00822
00823
00824 float NPC_GetHFOVPercentage( vec3_t spot, vec3_t from, vec3_t facing, float hFOV )
00825 {
00826 vec3_t deltaVector, angles;
00827 float delta;
00828
00829 VectorSubtract ( spot, from, deltaVector );
00830
00831 vectoangles ( deltaVector, angles );
00832
00833 delta = fabs( AngleDelta ( facing[YAW], angles[YAW] ) );
00834
00835 if ( delta > hFOV )
00836 return 0.0f;
00837
00838 return ( ( hFOV - delta ) / hFOV );
00839 }
00840
00841
00842
00843
00844
00845
00846
00847 float NPC_GetVFOVPercentage( vec3_t spot, vec3_t from, vec3_t facing, float vFOV )
00848 {
00849 vec3_t deltaVector, angles;
00850 float delta;
00851
00852 VectorSubtract ( spot, from, deltaVector );
00853
00854 vectoangles ( deltaVector, angles );
00855
00856 delta = fabs( AngleDelta ( facing[PITCH], angles[PITCH] ) );
00857
00858 if ( delta > vFOV )
00859 return 0.0f;
00860
00861 return ( ( vFOV - delta ) / vFOV );
00862 }
00863
00864 #define MAX_INTEREST_DIST ( 256 * 256 )
00865
00866
00867
00868
00869
00870
00871 int G_FindLocalInterestPoint( gentity_t *self )
00872 {
00873 int i, bestPoint = ENTITYNUM_NONE;
00874 float dist, bestDist = Q3_INFINITE;
00875 vec3_t diffVec, eyes;
00876
00877 CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes );
00878 for ( i = 0; i < level.numInterestPoints; i++ )
00879 {
00880
00881 if ( trap_InPVS( level.interestPoints[i].origin, eyes ) )
00882 {
00883 VectorSubtract( level.interestPoints[i].origin, eyes, diffVec );
00884 if ( (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 < 48 &&
00885 fabs(diffVec[2]) > (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 )
00886 {
00887 continue;
00888 }
00889 dist = VectorLengthSquared( diffVec );
00890
00891
00892 if ( dist < MAX_INTEREST_DIST && dist < bestDist )
00893 {
00894 if ( G_ClearLineOfSight( eyes, level.interestPoints[i].origin, self->s.number, MASK_OPAQUE ) )
00895 {
00896 bestDist = dist;
00897 bestPoint = i;
00898 }
00899 }
00900 }
00901 }
00902 if ( bestPoint != ENTITYNUM_NONE && level.interestPoints[bestPoint].target )
00903 {
00904 G_UseTargets2( self, self, level.interestPoints[bestPoint].target );
00905 }
00906 return bestPoint;
00907 }
00908
00909
00910
00911
00912
00913
00914
00915 void SP_target_interest( gentity_t *self )
00916 {
00917 if(level.numInterestPoints >= MAX_INTEREST_POINTS)
00918 {
00919 Com_Printf("ERROR: Too many interest points, limit is %d\n", MAX_INTEREST_POINTS);
00920 G_FreeEntity(self);
00921 return;
00922 }
00923
00924 VectorCopy(self->r.currentOrigin, level.interestPoints[level.numInterestPoints].origin);
00925
00926 if(self->target && self->target[0])
00927 {
00928 level.interestPoints[level.numInterestPoints].target = G_NewString( self->target );
00929 }
00930
00931 level.numInterestPoints++;
00932
00933 G_FreeEntity(self);
00934 }