00001 #include "b_local.h"
00002 #include "g_nav.h"
00003
00004 extern qboolean G_EntIsUnlockedDoor( int entityNum );
00005 extern qboolean G_EntIsDoor( int entityNum );
00006 extern qboolean G_EntIsBreakable( int entityNum );
00007 extern qboolean G_EntIsRemovableUsable( int entNum );
00008 extern qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result );
00009
00010
00011 void G_Line( vec3_t start, vec3_t end, vec3_t color, float alpha )
00012 {
00013
00014 }
00015
00016 void G_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha )
00017 {
00018
00019 }
00020
00021 void G_CubeOutline( vec3_t mins, vec3_t maxs, int time, unsigned int color, float alpha )
00022 {
00023
00024 }
00025
00026 void G_DrawEdge( vec3_t start, vec3_t end, int type )
00027 {
00028
00029 }
00030
00031 void G_DrawNode( vec3_t origin, int type )
00032 {
00033
00034 }
00035
00036 void G_DrawCombatPoint( vec3_t origin, int type )
00037 {
00038
00039 }
00040
00041 void TAG_ShowTags( int flags )
00042 {
00043
00044 }
00045
00046 qboolean FlyingCreature( gentity_t *ent )
00047 {
00048 if (ent->client && ent->client->ps.gravity <= 0)
00049 {
00050 return qtrue;
00051 }
00052 return qfalse;
00053 }
00054
00055 qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t *trace, int clipmask );
00056 void NAV_StoreWaypoint( gentity_t *ent );
00057
00058 extern vec3_t NPCDEBUG_RED;
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068 void NPC_Blocked( gentity_t *self, gentity_t *blocker )
00069 {
00070 if ( self->NPC == NULL )
00071 return;
00072
00073
00074 if ( self->NPC->blockedSpeechDebounceTime > level.time )
00075 return;
00076
00077
00078 if ( G_ActivateBehavior( self, BSET_BLOCKED ) )
00079 {
00080 return;
00081 }
00082
00083
00084 if ( blocker->client && ( blocker->client->playerTeam == self->client->enemyTeam ) )
00085 {
00086 G_SetEnemy( self, blocker );
00087 return;
00088 }
00089
00090
00091
00092
00093 if ( ( blocker->s.number == 0 ) && ( ( blocker->client->playerTeam == self->client->playerTeam ) ) )
00094 {
00095
00096
00097
00098
00099
00100 }
00101
00102 self->NPC->blockedSpeechDebounceTime = level.time + MIN_BLOCKED_SPEECH_TIME + ( random() * 4000 );
00103 self->NPC->blockingEntNum = blocker->s.number;
00104 }
00105
00106
00107
00108
00109
00110
00111
00112 void NPC_SetMoveGoal( gentity_t *ent, vec3_t point, int radius, qboolean isNavGoal, int combatPoint, gentity_t *targetEnt )
00113 {
00114
00115 if ( ent->NPC == NULL )
00116 {
00117 return;
00118 }
00119
00120 if ( ent->NPC->tempGoal == NULL )
00121 {
00122 return;
00123 }
00124
00125
00126
00127 VectorCopy( point, ent->NPC->tempGoal->r.currentOrigin );
00128
00129
00130 VectorCopy( ent->r.mins, ent->NPC->tempGoal->r.mins );
00131 VectorCopy( ent->r.mins, ent->NPC->tempGoal->r.maxs );
00132
00133 ent->NPC->tempGoal->target = NULL;
00134 ent->NPC->tempGoal->clipmask = ent->clipmask;
00135 ent->NPC->tempGoal->flags &= ~FL_NAVGOAL;
00136 if ( targetEnt && targetEnt->waypoint >= 0 )
00137 {
00138 ent->NPC->tempGoal->waypoint = targetEnt->waypoint;
00139 }
00140 else
00141 {
00142 ent->NPC->tempGoal->waypoint = WAYPOINT_NONE;
00143 }
00144 ent->NPC->tempGoal->noWaypointTime = 0;
00145
00146 if ( isNavGoal )
00147 {
00148 assert(ent->NPC->tempGoal->parent);
00149 ent->NPC->tempGoal->flags |= FL_NAVGOAL;
00150 }
00151
00152 ent->NPC->tempGoal->combatPoint = combatPoint;
00153 ent->NPC->tempGoal->enemy = targetEnt;
00154
00155 ent->NPC->goalEntity = ent->NPC->tempGoal;
00156 ent->NPC->goalRadius = radius;
00157
00158 trap_LinkEntity( ent->NPC->goalEntity );
00159 }
00160
00161
00162
00163
00164
00165
00166
00167 qboolean NAV_HitNavGoal( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t dest, int radius, qboolean flying )
00168 {
00169 vec3_t dmins, dmaxs, pmins, pmaxs;
00170
00171 if ( radius & NAVGOAL_USE_RADIUS )
00172 {
00173 radius &= ~NAVGOAL_USE_RADIUS;
00174
00175
00176
00177
00178 if ( !flying )
00179 {
00180 vec3_t diff;
00181 VectorSubtract( point, dest, diff );
00182 if ( fabs(diff[2]) <= 24 )
00183 {
00184 diff[2] = 0;
00185 }
00186 return ( VectorLengthSquared( diff ) <= (radius*radius) );
00187 }
00188 else
00189 {
00190 return ( DistanceSquared(dest, point) <= (radius*radius) );
00191 }
00192
00193
00194
00195
00196 }
00197 else
00198 {
00199
00200 VectorSet( dmins, -radius, -radius, -radius );
00201 VectorSet( dmaxs, radius, radius, radius );
00202
00203
00204 VectorAdd( dmins, dest, dmins );
00205 VectorAdd( dmaxs, dest, dmaxs );
00206
00207
00208 VectorAdd( point, mins, pmins );
00209 VectorAdd( point, maxs, pmaxs );
00210
00211
00212 return G_BoundsOverlap( pmins, pmaxs, dmins, dmaxs );
00213 }
00214 }
00215
00216
00217
00218
00219
00220
00221
00222 qboolean NAV_ClearPathToPoint( gentity_t *self, vec3_t pmins, vec3_t pmaxs, vec3_t point, int clipmask, int okToHitEntNum )
00223 {
00224
00225
00226
00227 vec3_t mins, maxs;
00228 trace_t trace;
00229
00230
00231 if ( !trap_InPVS( self->r.currentOrigin, point ) )
00232 return qfalse;
00233
00234 if ( self->flags & FL_NAVGOAL )
00235 {
00236 if ( !self->parent )
00237 {
00238
00239 assert(self->parent);
00240 return qfalse;
00241 }
00242 VectorCopy( self->parent->r.mins, mins );
00243 VectorCopy( self->parent->r.maxs, maxs );
00244 }
00245 else
00246 {
00247 VectorCopy( pmins, mins );
00248 VectorCopy( pmaxs, maxs );
00249 }
00250
00251 if ( self->client || ( self->flags & FL_NAVGOAL ) )
00252 {
00253
00254 mins[2] += STEPSIZE;
00255
00256
00257 if ( mins[2] > maxs[2] )
00258 {
00259 mins[2] = maxs[2];
00260 }
00261 }
00262
00263 if ( self->flags & FL_NAVGOAL )
00264 {
00265
00266 trap_Trace( &trace, point, mins, maxs, self->r.currentOrigin, self->parent->s.number, (clipmask|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP)&~CONTENTS_BODY );
00267 if ( trace.startsolid&&(trace.contents&CONTENTS_BOTCLIP) )
00268 {
00269 clipmask &= ~CONTENTS_BOTCLIP;
00270 trap_Trace( &trace, point, mins, maxs, self->r.currentOrigin, self->parent->s.number, (clipmask|CONTENTS_MONSTERCLIP)&~CONTENTS_BODY );
00271 }
00272
00273 if ( trace.startsolid || trace.allsolid )
00274 {
00275 return qfalse;
00276 }
00277
00278
00279 if ( trace.fraction == 1.0 )
00280 {
00281 return qtrue;
00282 }
00283
00284 if ( okToHitEntNum != ENTITYNUM_NONE && trace.entityNum == okToHitEntNum )
00285 {
00286 return qtrue;
00287 }
00288
00289
00290 if ( NAV_HitNavGoal( self->r.currentOrigin, self->parent->r.mins, self->parent->r.maxs, trace.endpos, NPCInfo->goalRadius, FlyingCreature( self->parent ) ) )
00291 {
00292 return qtrue;
00293 }
00294 else
00295 {
00296 if ( NAVDEBUG_showCollision )
00297 {
00298 if ( trace.entityNum < ENTITYNUM_WORLD && (&g_entities[trace.entityNum] != NULL) && g_entities[trace.entityNum].s.eType != ET_MOVER )
00299 {
00300 vec3_t p1, p2;
00301 G_DrawEdge( point, trace.endpos, EDGE_PATH );
00302 VectorAdd(g_entities[trace.entityNum].r.mins, g_entities[trace.entityNum].r.currentOrigin, p1);
00303 VectorAdd(g_entities[trace.entityNum].r.maxs, g_entities[trace.entityNum].r.currentOrigin, p2);
00304 G_CubeOutline( p1, p2, FRAMETIME, 0x0000ff, 0.5 );
00305 }
00306
00307 }
00308 }
00309 }
00310 else
00311 {
00312 trap_Trace( &trace, self->r.currentOrigin, mins, maxs, point, self->s.number, clipmask|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);
00313 if ( trace.startsolid&&(trace.contents&CONTENTS_BOTCLIP) )
00314 {
00315 clipmask &= ~CONTENTS_BOTCLIP;
00316 trap_Trace( &trace, self->r.currentOrigin, mins, maxs, point, self->s.number, clipmask|CONTENTS_MONSTERCLIP);
00317 }
00318
00319 if( ( ( trace.startsolid == qfalse ) && ( trace.allsolid == qfalse ) ) && ( trace.fraction == 1.0f ) )
00320 {
00321 return qtrue;
00322 }
00323
00324 if ( okToHitEntNum != ENTITYNUM_NONE && trace.entityNum == okToHitEntNum )
00325 {
00326 return qtrue;
00327 }
00328
00329 if ( NAVDEBUG_showCollision )
00330 {
00331 if ( trace.entityNum < ENTITYNUM_WORLD && (&g_entities[trace.entityNum] != NULL) && g_entities[trace.entityNum].s.eType != ET_MOVER )
00332 {
00333 vec3_t p1, p2;
00334 G_DrawEdge( self->r.currentOrigin, trace.endpos, EDGE_PATH );
00335 VectorAdd(g_entities[trace.entityNum].r.mins, g_entities[trace.entityNum].r.currentOrigin, p1);
00336 VectorAdd(g_entities[trace.entityNum].r.maxs, g_entities[trace.entityNum].r.currentOrigin, p2);
00337 G_CubeOutline( p1, p2, FRAMETIME, 0x0000ff, 0.5 );
00338 }
00339
00340 }
00341 }
00342
00343 return qfalse;
00344 }
00345
00346
00347
00348
00349
00350
00351
00352 int NAV_FindClosestWaypointForEnt( gentity_t *ent, int targWp )
00353 {
00354
00355 return trap_Nav_GetNearestNode( ent, ent->waypoint, NF_CLEAR_PATH, targWp );
00356 }
00357
00358 int NAV_FindClosestWaypointForPoint( gentity_t *ent, vec3_t point )
00359 {
00360 int bestWP;
00361
00362 gentity_t *marker = G_Spawn();
00363
00364 if ( !marker )
00365 {
00366 return WAYPOINT_NONE;
00367 }
00368
00369 G_SetOrigin( marker, point );
00370
00371 VectorCopy( ent->r.mins, marker->r.mins );
00372 VectorCopy( ent->r.mins, marker->r.maxs );
00373
00374 marker->clipmask = ent->clipmask;
00375 marker->waypoint = WAYPOINT_NONE;
00376
00377 bestWP = trap_Nav_GetNearestNode( marker, marker->waypoint, NF_CLEAR_PATH, WAYPOINT_NONE );
00378
00379 G_FreeEntity( marker );
00380
00381 return bestWP;
00382 }
00383
00384 int NAV_FindClosestWaypointForPoint2( vec3_t point )
00385 {
00386 int bestWP;
00387
00388 gentity_t *marker = G_Spawn();
00389
00390 if ( !marker )
00391 {
00392 return WAYPOINT_NONE;
00393 }
00394
00395 G_SetOrigin( marker, point );
00396
00397 VectorSet( marker->r.mins, -16, -16, -6 );
00398 VectorSet( marker->r.maxs, 16, 16, 32 );
00399
00400 marker->clipmask = MASK_NPCSOLID;
00401 marker->waypoint = WAYPOINT_NONE;
00402
00403 bestWP = trap_Nav_GetNearestNode( marker, marker->waypoint, NF_CLEAR_PATH, WAYPOINT_NONE );
00404
00405 G_FreeEntity( marker );
00406
00407 return bestWP;
00408 }
00409
00410
00411
00412
00413
00414
00415
00416 void NAV_ClearBlockedInfo( gentity_t *self )
00417 {
00418 self->NPC->aiFlags &= ~NPCAI_BLOCKED;
00419 self->NPC->blockingEntNum = ENTITYNUM_WORLD;
00420 }
00421
00422
00423
00424
00425
00426
00427
00428 void NAV_SetBlockedInfo( gentity_t *self, int entId )
00429 {
00430 self->NPC->aiFlags |= NPCAI_BLOCKED;
00431 self->NPC->blockingEntNum = entId;
00432 }
00433
00434
00435
00436
00437
00438
00439
00440 int NAV_Steer( gentity_t *self, vec3_t dir, float distance )
00441 {
00442 vec3_t right_test, left_test;
00443 vec3_t deviation;
00444 trace_t tr;
00445 float right_push;
00446 float left_push;
00447 float right_ang = dir[YAW] + 45;
00448 float left_ang = dir[YAW] - 45;
00449
00450
00451 VectorCopy( dir, deviation );
00452 deviation[YAW] = right_ang;
00453
00454 AngleVectors( deviation, right_test, NULL, NULL );
00455
00456 deviation[YAW] = left_ang;
00457
00458 AngleVectors( deviation, left_test, NULL, NULL );
00459
00460
00461 VectorMA( self->r.currentOrigin, distance, right_test, right_test );
00462 VectorMA( self->r.currentOrigin, distance, left_test, left_test );
00463
00464
00465 if ( NAVDEBUG_showCollision )
00466 {
00467 G_DrawEdge( self->r.currentOrigin, right_test, EDGE_PATH );
00468 G_DrawEdge( self->r.currentOrigin, left_test, EDGE_PATH );
00469 }
00470
00471
00472 NAV_CheckAhead( self, right_test, &tr, self->clipmask|CONTENTS_BOTCLIP );
00473
00474 right_push = -45 * ( 1.0f - tr.fraction );
00475
00476
00477 NAV_CheckAhead( self, left_test, &tr, self->clipmask|CONTENTS_BOTCLIP );
00478
00479 left_push = 45 * ( 1.0f - tr.fraction );
00480
00481
00482 VectorCopy( dir, deviation );
00483 deviation[YAW] += ( left_push + right_push );
00484
00485 return deviation[YAW];
00486 }
00487
00488
00489
00490
00491
00492
00493
00494 qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t *trace, int clipmask )
00495 {
00496 vec3_t mins;
00497 float radius;
00498 float dist;
00499 float tFrac;
00500
00501
00502 VectorSet( mins, self->r.mins[0], self->r.mins[1], self->r.mins[2] + STEPSIZE );
00503
00504 trap_Trace( trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, clipmask );
00505
00506 if ( trace->startsolid&&(trace->contents&CONTENTS_BOTCLIP) )
00507 {
00508 clipmask &= ~CONTENTS_BOTCLIP;
00509 trap_Trace( trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, clipmask );
00510 }
00511
00512 if ( ( trace->allsolid == qfalse ) && ( trace->startsolid == qfalse ) && ( trace->fraction == 1.0f ) )
00513 return qtrue;
00514
00515
00516 if ( fabs( self->r.currentOrigin[2] - end[2] ) > 48 )
00517 return qfalse;
00518
00519
00520 radius = ( self->r.maxs[0] > self->r.maxs[1] ) ? self->r.maxs[0] : self->r.maxs[1];
00521 dist = Distance( self->r.currentOrigin, end );
00522 tFrac = 1.0f - ( radius / dist );
00523
00524 if ( trace->fraction >= tFrac )
00525 return qtrue;
00526
00527
00528 if ( trace->entityNum < ENTITYNUM_WORLD )
00529 {
00530 gentity_t *blocker = &g_entities[trace->entityNum];
00531
00532 if VALIDSTRING( blocker->classname )
00533 {
00534 if ( G_EntIsUnlockedDoor( blocker->s.number ) )
00535
00536 {
00537
00538 if ( DistanceSquared( self->r.currentOrigin, trace->endpos ) < MIN_DOOR_BLOCK_DIST_SQR )
00539 return qfalse;
00540
00541 return qtrue;
00542 }
00543 }
00544 }
00545
00546 return qfalse;
00547 }
00548
00549
00550
00551
00552
00553
00554
00555 static qboolean NAV_TestBypass( gentity_t *self, float yaw, float blocked_dist, vec3_t movedir )
00556 {
00557 trace_t tr;
00558 vec3_t avoidAngles;
00559 vec3_t block_test, block_pos;
00560
00561 VectorClear( avoidAngles );
00562 avoidAngles[YAW] = yaw;
00563
00564 AngleVectors( avoidAngles, block_test, NULL, NULL );
00565 VectorMA( self->r.currentOrigin, blocked_dist, block_test, block_pos );
00566
00567 if ( NAVDEBUG_showCollision )
00568 {
00569 G_DrawEdge( self->r.currentOrigin, block_pos, EDGE_BLOCKED );
00570 }
00571
00572
00573 if ( NAV_CheckAhead( self, block_pos, &tr, ( self->clipmask & ~CONTENTS_BODY )|CONTENTS_BOTCLIP ) )
00574 {
00575 VectorCopy( block_test, movedir );
00576
00577 return qtrue;
00578 }
00579
00580 return qfalse;
00581 }
00582
00583
00584
00585
00586
00587
00588
00589 qboolean NAV_Bypass( gentity_t *self, gentity_t *blocker, vec3_t blocked_dir, float blocked_dist, vec3_t movedir )
00590 {
00591 float dot, yaw, avoidRadius, arcAngle;
00592 vec3_t right;
00593
00594
00595 if ( NAVDEBUG_showCollision )
00596 {
00597 G_DrawEdge( self->r.currentOrigin, blocker->r.currentOrigin, EDGE_NORMAL );
00598 }
00599
00600 AngleVectors( self->r.currentAngles, NULL, right, NULL );
00601
00602
00603 yaw = vectoyaw( blocked_dir );
00604
00605
00606 avoidRadius = sqrt( ( blocker->r.maxs[0] * blocker->r.maxs[0] ) + ( blocker->r.maxs[1] * blocker->r.maxs[1] ) ) +
00607 sqrt( ( self->r.maxs[0] * self->r.maxs[0] ) + ( self->r.maxs[1] * self->r.maxs[1] ) );
00608
00609
00610 arcAngle = ( blocked_dist <= avoidRadius ) ? 135 : ( ( avoidRadius / blocked_dist ) * 90 );
00611
00612
00613
00614
00615
00616 if ( blocker->client && !VectorCompare( blocker->client->ps.velocity, vec3_origin ) )
00617 {
00618 vec3_t blocker_movedir;
00619 VectorNormalize2( blocker->client->ps.velocity, blocker_movedir );
00620 dot = DotProduct( blocker_movedir, blocked_dir );
00621 if ( dot < 0.35f && dot > -0.35f )
00622 {
00623 vec3_t block_pos;
00624 trace_t tr;
00625 VectorScale( blocker_movedir, -1, blocker_movedir );
00626 VectorMA( self->r.currentOrigin, blocked_dist, blocker_movedir, block_pos );
00627 if ( NAV_CheckAhead( self, block_pos, &tr, ( self->clipmask & ~CONTENTS_BODY )|CONTENTS_BOTCLIP ) )
00628 {
00629 VectorCopy( blocker_movedir, movedir );
00630 return qtrue;
00631 }
00632 }
00633 }
00634
00635
00636
00637 dot = DotProduct( blocked_dir, right );
00638
00639
00640 if ( dot < 0.0f )
00641 arcAngle *= -1;
00642
00643
00644 if ( NAV_TestBypass( self, AngleNormalize360( yaw + arcAngle ), blocked_dist, movedir ) )
00645 return qtrue;
00646
00647
00648 if ( NAV_TestBypass( self, AngleNormalize360( yaw + ( arcAngle * 0.5f ) ), blocked_dist, movedir ) )
00649 return qtrue;
00650
00651
00652 if ( NAV_TestBypass( self, AngleNormalize360( yaw + ( arcAngle * -1 ) ), blocked_dist, movedir ) )
00653 return qtrue;
00654
00655
00656 if ( NAV_TestBypass( self, AngleNormalize360( yaw + ( ( arcAngle * -1 ) * 0.5f ) ), blocked_dist, movedir ) )
00657 return qtrue;
00658
00659
00660 return qfalse;
00661 }
00662
00663
00664
00665
00666
00667
00668
00669 qboolean NAV_MoveBlocker( gentity_t *self, vec3_t shove_dir )
00670 {
00671
00672
00673
00674
00675 vec3_t temp_dir, forward;
00676
00677 vectoangles( shove_dir, temp_dir );
00678
00679 temp_dir[YAW] += 45;
00680 AngleVectors( temp_dir, forward, NULL, NULL );
00681
00682 VectorScale( forward, SHOVE_SPEED, self->client->ps.velocity );
00683 self->client->ps.velocity[2] += SHOVE_LIFT;
00684
00685
00686
00687 return qtrue;
00688 }
00689
00690
00691
00692
00693
00694
00695
00696 qboolean NAV_ResolveBlock( gentity_t *self, gentity_t *blocker, vec3_t blocked_dir )
00697 {
00698
00699 if ( ( blocker->NPC ) && ( blocker->NPC->blockingEntNum == self->s.number ) )
00700 return qtrue;
00701
00702
00703 NPC_Blocked( self, blocker );
00704 NPC_FaceEntity( blocker, qtrue );
00705
00706 return qfalse;
00707 }
00708
00709
00710
00711
00712
00713
00714
00715 qboolean NAV_TrueCollision( gentity_t *self, gentity_t *blocker, vec3_t movedir, vec3_t blocked_dir )
00716 {
00717 vec3_t velocityDir;
00718 float speed, dot;
00719 vec3_t testPos;
00720 vec3_t ptmins, ptmaxs, tmins, tmaxs;
00721
00722
00723 if ( blocker->client == NULL )
00724 return qfalse;
00725
00726
00727 speed = VectorNormalize2( self->client->ps.velocity, velocityDir );
00728
00729
00730 dot = DotProduct( movedir, velocityDir );
00731
00732 if ( dot < 0.85 )
00733 return qfalse;
00734
00735 VectorMA( self->r.currentOrigin, speed*FRAMETIME, velocityDir, testPos );
00736
00737 VectorAdd( blocker->r.currentOrigin, blocker->r.mins, tmins );
00738 VectorAdd( blocker->r.currentOrigin, blocker->r.maxs, tmaxs );
00739
00740 VectorAdd( testPos, self->r.mins, ptmins );
00741 VectorAdd( testPos, self->r.maxs, ptmaxs );
00742
00743 if ( G_BoundsOverlap( ptmins, ptmaxs, tmins, tmaxs ) )
00744 {
00745 VectorCopy( velocityDir, blocked_dir );
00746 return qtrue;
00747 }
00748
00749 return qfalse;
00750 }
00751
00752
00753
00754
00755
00756
00757
00758 qboolean NAV_StackedCanyon( gentity_t *self, gentity_t *blocker, vec3_t pathDir )
00759 {
00760 vec3_t perp, cross, test;
00761 float avoidRadius;
00762 int extraClip = CONTENTS_BOTCLIP;
00763 trace_t tr;
00764
00765 PerpendicularVector( perp, pathDir );
00766 CrossProduct( pathDir, perp, cross );
00767
00768 avoidRadius = sqrt( ( blocker->r.maxs[0] * blocker->r.maxs[0] ) + ( blocker->r.maxs[1] * blocker->r.maxs[1] ) ) +
00769 sqrt( ( self->r.maxs[0] * self->r.maxs[0] ) + ( self->r.maxs[1] * self->r.maxs[1] ) );
00770
00771 VectorMA( blocker->r.currentOrigin, avoidRadius, cross, test );
00772
00773 trap_Trace( &tr, test, self->r.mins, self->r.maxs, test, self->s.number, self->clipmask|extraClip );
00774 if ( tr.startsolid&&(tr.contents&CONTENTS_BOTCLIP) )
00775 {
00776 extraClip &= ~CONTENTS_BOTCLIP;
00777 trap_Trace( &tr, test, self->r.mins, self->r.maxs, test, self->s.number, self->clipmask|extraClip );
00778 }
00779
00780 if ( NAVDEBUG_showCollision )
00781 {
00782 vec3_t mins, maxs;
00783 vec3_t RED = { 1.0f, 0.0f, 0.0f };
00784
00785 VectorAdd( test, self->r.mins, mins );
00786 VectorAdd( test, self->r.maxs, maxs );
00787 G_Cube( mins, maxs, RED, 0.25 );
00788 }
00789
00790 if ( tr.startsolid == qfalse && tr.allsolid == qfalse )
00791 return qfalse;
00792
00793 VectorMA( blocker->r.currentOrigin, -avoidRadius, cross, test );
00794
00795 trap_Trace( &tr, test, self->r.mins, self->r.maxs, test, self->s.number, self->clipmask|extraClip );
00796 if ( tr.startsolid&&(tr.contents&CONTENTS_BOTCLIP) )
00797 {
00798 extraClip &= ~CONTENTS_BOTCLIP;
00799 trap_Trace( &tr, test, self->r.mins, self->r.maxs, test, self->s.number, self->clipmask|extraClip );
00800 }
00801
00802 if ( tr.startsolid == qfalse && tr.allsolid == qfalse )
00803 return qfalse;
00804
00805 if ( NAVDEBUG_showCollision )
00806 {
00807 vec3_t mins, maxs;
00808 vec3_t RED = { 1.0f, 0.0f, 0.0f };
00809
00810 VectorAdd( test, self->r.mins, mins );
00811 VectorAdd( test, self->r.maxs, maxs );
00812 G_Cube( mins, maxs, RED, 0.25 );
00813 }
00814
00815 return qtrue;
00816 }
00817
00818
00819
00820
00821
00822
00823
00824 qboolean NAV_ResolveEntityCollision( gentity_t *self, gentity_t *blocker, vec3_t movedir, vec3_t pathDir )
00825 {
00826 vec3_t blocked_dir;
00827 float blocked_dist;
00828
00829
00830 if ( G_EntIsUnlockedDoor( blocker->s.number ) )
00831
00832 {
00833 if ( DistanceSquared( self->r.currentOrigin, blocker->r.currentOrigin ) > MIN_DOOR_BLOCK_DIST_SQR )
00834 return qtrue;
00835 }
00836
00837 VectorSubtract( blocker->r.currentOrigin, self->r.currentOrigin, blocked_dir );
00838 blocked_dist = VectorNormalize( blocked_dir );
00839
00840
00841
00842
00843
00844
00845 if ( blocker->s.number == 0 )
00846 {
00847 if ( NAV_StackedCanyon( self, blocker, pathDir ) )
00848 {
00849 NPC_Blocked( self, blocker );
00850 NPC_FaceEntity( blocker, qtrue );
00851
00852 return qfalse;
00853 }
00854 }
00855
00856
00857 if ( NAV_Bypass( self, blocker, blocked_dir, blocked_dist, movedir ) )
00858 return qtrue;
00859
00860
00861 if ( NAV_ResolveBlock( self, blocker, blocked_dir ) )
00862 return qtrue;
00863
00864 return qfalse;
00865 }
00866
00867
00868
00869
00870
00871
00872
00873 qboolean NAV_TestForBlocked( gentity_t *self, gentity_t *goal, gentity_t *blocker, float distance, int *flags )
00874 {
00875 if ( goal == NULL )
00876 return qfalse;
00877
00878 if ( blocker->s.eType == ET_ITEM )
00879 return qfalse;
00880
00881 if ( NAV_HitNavGoal( blocker->r.currentOrigin, blocker->r.mins, blocker->r.maxs, goal->r.currentOrigin, 12, qfalse ) )
00882 {
00883 *flags |= NIF_BLOCKED;
00884
00885 if ( distance <= MIN_STOP_DIST )
00886 {
00887 NPC_Blocked( self, blocker );
00888 NPC_FaceEntity( blocker, qtrue );
00889 return qtrue;
00890 }
00891 }
00892
00893 return qfalse;
00894 }
00895
00896
00897
00898
00899
00900
00901
00902 qboolean NAV_AvoidCollision( gentity_t *self, gentity_t *goal, navInfo_t *info )
00903 {
00904 vec3_t movedir;
00905 vec3_t movepos;
00906
00907
00908 NAV_ClearBlockedInfo( NPC );
00909
00910
00911 if ( info->distance > MAX_COLL_AVOID_DIST )
00912 {
00913 info->distance = MAX_COLL_AVOID_DIST;
00914 }
00915
00916
00917 VectorMA( self->r.currentOrigin, info->distance, info->direction, movepos );
00918 VectorCopy( info->direction, movedir );
00919
00920 if ( self && self->NPC && (self->NPC->aiFlags&NPCAI_NO_COLL_AVOID) )
00921 {
00922 return qtrue;
00923 }
00924
00925 if ( NAV_CheckAhead( self, movepos, &info->trace, CONTENTS_BODY ) == qfalse )
00926 {
00927
00928 info->blocker = &g_entities[ info->trace.entityNum ];
00929 info->flags |= NIF_COLLISION;
00930
00931
00932 if ( goal == info->blocker )
00933 return qtrue;
00934
00935
00936
00937
00938
00939
00940 if ( NAV_TestForBlocked( self, goal, info->blocker, info->distance, &info->flags ) == qtrue )
00941 return qfalse;
00942
00943
00944 if ( info->flags & NIF_BLOCKED )
00945 return qtrue;
00946
00947
00948 if ( NAV_ResolveEntityCollision( self, info->blocker, movedir, info->pathDirection ) == qfalse )
00949 return qfalse;
00950
00951 VectorCopy( movedir, info->direction );
00952
00953 return qtrue;
00954 }
00955
00956
00957 if ( NAVDEBUG_showCollision )
00958 {
00959 G_DrawEdge( self->r.currentOrigin, movepos, EDGE_PATH );
00960 }
00961
00962 return qtrue;
00963 }
00964
00965
00966
00967
00968
00969
00970
00971 int NAV_TestBestNode( gentity_t *self, int startID, int endID, qboolean failEdge )
00972 {
00973 vec3_t end;
00974 trace_t trace;
00975 vec3_t mins;
00976 int clipmask = (NPC->clipmask&~CONTENTS_BODY)|CONTENTS_BOTCLIP;
00977
00978
00979 trap_Nav_GetNodePosition( endID, end );
00980
00981
00982 VectorSet( mins, self->r.mins[0], self->r.mins[1], self->r.mins[2] + STEPSIZE );
00983
00984 trap_Trace( &trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, clipmask );
00985
00986 if ( trace.startsolid&&(trace.contents&CONTENTS_BOTCLIP) )
00987 {
00988 clipmask &= ~CONTENTS_BOTCLIP;
00989 trap_Trace( &trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, clipmask );
00990 }
00991
00992 if ( ( trace.allsolid == qfalse ) && ( trace.startsolid == qfalse ) && ( trace.fraction == 1.0f ) )
00993 {
00994 return endID;
00995 }
00996
00997
00998 if ( self->s.weapon != WP_SABER && fabs( self->r.currentOrigin[2] - end[2] ) > 48 )
00999 {
01000 }
01001 else
01002 {
01003
01004 float radius = ( self->r.maxs[0] > self->r.maxs[1] ) ? self->r.maxs[0] : self->r.maxs[1];
01005 float dist = Distance( self->r.currentOrigin, end );
01006 float tFrac = 1.0f - ( radius / dist );
01007
01008 if ( trace.fraction >= tFrac )
01009 {
01010 return endID;
01011 }
01012 }
01013
01014
01015 if ( trace.entityNum < ENTITYNUM_WORLD )
01016 {
01017 gentity_t *blocker = &g_entities[trace.entityNum];
01018
01019 if VALIDSTRING( blocker->classname )
01020 {
01021 if ( G_EntIsUnlockedDoor( blocker->s.number ) )
01022
01023 {
01024
01025 if ( DistanceSquared( self->r.currentOrigin, trace.endpos ) < MIN_DOOR_BLOCK_DIST_SQR )
01026 {
01027 return startID;
01028 }
01029
01030 if ( self->s.weapon != WP_SABER && fabs( self->r.currentOrigin[2] - end[2] ) > 48 )
01031 {
01032 }
01033 else
01034 {
01035 return endID;
01036 }
01037 }
01038 else if ( G_EntIsDoor( blocker->s.number ) )
01039 {
01040
01041 if ( failEdge )
01042 {
01043 trap_Nav_AddFailedEdge( self->s.number, startID, endID );
01044 }
01045 }
01046 else if ( G_EntIsBreakable( blocker->s.number ) )
01047 {
01048
01049 if ( failEdge )
01050 {
01051 trap_Nav_AddFailedEdge( self->s.number, startID, endID );
01052 }
01053 }
01054 else if ( G_EntIsRemovableUsable( blocker->s.number ) )
01055 {
01056
01057 if ( failEdge )
01058 {
01059 trap_Nav_AddFailedEdge( self->s.number, startID, endID );
01060 }
01061 }
01062 else if ( blocker->targetname && blocker->s.solid == SOLID_BMODEL && ((blocker->r.contents&CONTENTS_MONSTERCLIP)|| (blocker->r.contents&CONTENTS_BOTCLIP)) )
01063 {
01064
01065 if ( failEdge )
01066 {
01067 trap_Nav_AddFailedEdge( self->s.number, startID, endID );
01068 }
01069 }
01070 }
01071 }
01072
01073
01074 return startID;
01075 }
01076
01077
01078
01079
01080
01081
01082
01083 int NAV_GetNearestNode( gentity_t *self, int lastNode )
01084 {
01085 return trap_Nav_GetNearestNode( self, lastNode, NF_CLEAR_PATH, WAYPOINT_NONE );
01086 }
01087
01088
01089
01090
01091
01092
01093
01094 qboolean NAV_MicroError( vec3_t start, vec3_t end )
01095 {
01096 if ( VectorCompare( start, end ) )
01097 {
01098 if ( DistanceSquared( NPC->r.currentOrigin, start ) < (8*8) )
01099 {
01100 return qtrue;
01101 }
01102 }
01103
01104 return qfalse;
01105 }
01106
01107
01108
01109
01110
01111
01112
01113 int NAV_MoveToGoal( gentity_t *self, navInfo_t *info )
01114 {
01115 int bestNode;
01116 vec3_t origin, end;
01117
01118
01119 if( self->NPC->goalEntity == NULL )
01120 return WAYPOINT_NONE;
01121
01122
01123 if ( self->NPC->goalEntity->s.number == 0 )
01124 {
01125
01126 if ( self->NPC->goalEntity->waypoint == WAYPOINT_NONE )
01127 return WAYPOINT_NONE;
01128
01129
01130 }
01131 else
01132 {
01133
01134 if ( ( self->NPC->goalEntity->waypoint = NAV_GetNearestNode( self->NPC->goalEntity, self->NPC->goalEntity->waypoint ) ) == WAYPOINT_NONE )
01135 return WAYPOINT_NONE;
01136 }
01137
01138
01139 if ( ( self->waypoint = NAV_GetNearestNode( self, self->lastWaypoint ) ) == WAYPOINT_NONE )
01140 return WAYPOINT_NONE;
01141
01142 bestNode = trap_Nav_GetBestNode( self->waypoint, self->NPC->goalEntity->waypoint, NODE_NONE );
01143
01144 if ( bestNode == WAYPOINT_NONE )
01145 {
01146 if ( NAVDEBUG_showEnemyPath )
01147 {
01148 vec3_t origin, torigin;
01149
01150 trap_Nav_GetNodePosition( self->NPC->goalEntity->waypoint, torigin );
01151 trap_Nav_GetNodePosition( self->waypoint, origin );
01152
01153 G_DrawNode( torigin, NODE_GOAL );
01154 G_DrawNode( origin, NODE_GOAL );
01155 G_DrawNode( self->NPC->goalEntity->r.currentOrigin, NODE_START );
01156 }
01157
01158 return WAYPOINT_NONE;
01159 }
01160
01161
01162 bestNode = NAV_TestBestNode( self, bestNode, self->NPC->goalEntity->waypoint, qfalse );
01163
01164
01165
01166
01167 trap_Nav_GetNodePosition( bestNode, origin );
01168 trap_Nav_GetNodePosition( self->waypoint, end );
01169
01170
01171
01172
01173
01174
01175 if ( NAV_CheckAhead( self, origin, &info->trace, (self->clipmask&~CONTENTS_BODY)|CONTENTS_BOTCLIP ) == qfalse )
01176 {
01177
01178 G_FindClosestPointOnLineSegment( origin, end, self->r.currentOrigin, origin );
01179
01180
01181 if ( NAV_CheckAhead( self, origin, &info->trace, (self->clipmask&~CONTENTS_BODY)|CONTENTS_BOTCLIP ) == qfalse )
01182 {
01183
01184 bestNode = self->waypoint;
01185 trap_Nav_GetNodePosition( bestNode, origin );
01186 }
01187 }
01188
01189
01190 VectorSubtract( origin, self->r.currentOrigin, info->direction );
01191 info->distance = VectorNormalize( info->direction );
01192
01193 VectorSubtract( end, origin, info->pathDirection );
01194 VectorNormalize( info->pathDirection );
01195
01196
01197 if ( NAVDEBUG_showEnemyPath )
01198 {
01199 vec3_t dest, start;
01200
01201
01202 trap_Nav_GetNodePosition( self->NPC->goalEntity->waypoint, dest );
01203 trap_Nav_GetNodePosition( bestNode, start );
01204
01205
01206 G_DrawNode( start, NODE_START );
01207 G_DrawNode( dest, NODE_GOAL );
01208 trap_Nav_ShowPath( self->waypoint, self->NPC->goalEntity->waypoint );
01209 }
01210
01211 return bestNode;
01212 }
01213
01214
01215
01216
01217
01218
01219
01220 unsigned int waypoint_testDirection( vec3_t origin, float yaw, unsigned int minDist )
01221 {
01222