1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6
7 This file is part of the OpenJK source code.
8
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22
23 #include "g_headers.h"
24 #include <algorithm>
25
26 #include "b_local.h"
27 #include "g_navigator.h"
28 #include "g_nav.h"
29
30 extern int NAVNEW_ClearPathBetweenPoints(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int ignore, int clipmask);
31 extern qboolean NAV_CheckNodeFailedForEnt( gentity_t *ent, int nodeNum );
32 extern qboolean G_EntIsUnlockedDoor( int entityNum );
33 extern qboolean G_EntIsDoor( int entityNum );
34 extern qboolean G_EntIsBreakable( int entityNum );
35 extern qboolean G_EntIsRemovableUsable( int entNum );
36
37 extern cvar_t *d_altRoutes;
38 extern cvar_t *d_patched;
39
40
41 static vec3_t wpMaxs = { 16, 16, 32 };
42 static vec3_t wpMins = { -16, -16, -24+STEPSIZE };//WTF: was 16??!!!
43
44
45 static byte CHECKED_NO = 0;
46 static byte CHECKED_FAILED = 1;
47 static byte CHECKED_PASSED = 2;
48
49 /*
50 -------------------------
51 CEdge
52 -------------------------
53 */
54
CEdge(void)55 CEdge::CEdge( void )
56 {
57 CEdge( -1, -1, -1 );
58 }
59
CEdge(int first,int second,int cost)60 CEdge::CEdge( int first, int second, int cost )
61 {
62 m_first = first;
63 m_second = second;
64 m_cost = cost;
65 }
66
~CEdge(void)67 CEdge::~CEdge( void )
68 {
69 }
70
71 /*
72 -------------------------
73 CNode
74 -------------------------
75 */
76
CNode(void)77 CNode::CNode( void )
78 {
79 m_numEdges = 0;
80 m_radius = 0;
81 m_ranks = NULL;
82 }
83
~CNode(void)84 CNode::~CNode( void )
85 {
86 m_edges.clear();
87
88 if ( m_ranks )
89 delete [] m_ranks;
90 }
91
92 /*
93 -------------------------
94 Create
95 -------------------------
96 */
97
Create(vec3_t position,int flags,int radius,int ID)98 CNode *CNode::Create( vec3_t position, int flags, int radius, int ID )
99 {
100 CNode *node = new CNode;
101
102 VectorCopy( position, node->m_position );
103
104 node->m_flags = flags;
105 node->m_ID = ID;
106 node->m_radius = radius;
107
108 return node;
109 }
110
111 /*
112 -------------------------
113 Create
114 -------------------------
115 */
116
Create(void)117 CNode *CNode::Create( void )
118 {
119 return new CNode;
120 }
121
122 /*
123 -------------------------
124 AddEdge
125 -------------------------
126 */
127
AddEdge(int ID,int cost,int flags)128 void CNode::AddEdge( int ID, int cost, int flags )
129 {
130 if ( m_numEdges )
131 {//already have at least 1
132 //see if it exists already
133 edge_v::iterator ei;
134 STL_ITERATE( ei, m_edges )
135 {
136 if ( (*ei).ID == ID )
137 {//found it
138 (*ei).cost = cost;
139 (*ei).flags = flags;
140 return;
141 }
142 }
143 }
144
145 edge_t edge;
146
147 edge.ID = ID;
148 edge.cost = cost;
149 edge.flags = flags;
150
151 STL_INSERT( m_edges, edge );
152
153 m_numEdges++;
154
155 assert( m_numEdges < 9 );//8 is the max
156 }
157
158 /*
159 -------------------------
160 GetEdge
161 -------------------------
162 */
163
GetEdgeNumToNode(int ID)164 int CNode::GetEdgeNumToNode( int ID )
165 {
166 int count = 0;
167
168 edge_v::iterator ei;
169 STL_ITERATE( ei, m_edges )
170 {
171 if ( (*ei).ID == ID )
172 {
173 return count;
174 }
175
176 count++;
177 }
178 return -1;
179 }
180
181 /*
182 -------------------------
183 AddRank
184 -------------------------
185 */
186
AddRank(int ID,int rank)187 void CNode::AddRank( int ID, int rank )
188 {
189 assert( m_ranks );
190
191 m_ranks[ ID ] = rank;
192 }
193
194 /*
195 -------------------------
196 Draw
197 -------------------------
198 */
199
Draw(qboolean showRadius)200 void CNode::Draw( qboolean showRadius )
201 {
202 CG_DrawNode( m_position, NODE_NORMAL );
203 if( showRadius )
204 {
205 CG_DrawRadius( m_position, m_radius, NODE_NORMAL );
206 }
207 }
208
209 /*
210 -------------------------
211 GetEdge
212 -------------------------
213 */
214
GetEdge(int edgeNum)215 int CNode::GetEdge( int edgeNum )
216 {
217 if ( edgeNum > m_numEdges )
218 return -1;
219
220 int count = 0;
221
222 edge_v::iterator ei;
223 STL_ITERATE( ei, m_edges )
224 {
225 if ( count == edgeNum )
226 {
227 return (*ei).ID;
228 }
229
230 count++;
231 }
232
233 return -1;
234 }
235
236 /*
237 -------------------------
238 GetEdgeCost
239 -------------------------
240 */
241
GetEdgeCost(int edgeNum)242 int CNode::GetEdgeCost( int edgeNum )
243 {
244 if ( edgeNum > m_numEdges )
245 return Q3_INFINITE; // return -1;
246
247 int count = 0;
248
249 edge_v::iterator ei;
250 STL_ITERATE( ei, m_edges )
251 {
252 if ( count == edgeNum )
253 {
254 return (*ei).cost;
255 }
256
257 count++;
258 }
259
260 return Q3_INFINITE; // return -1;
261 }
262
263 /*
264 -------------------------
265 GetEdgeFlags
266 -------------------------
267 */
268
GetEdgeFlags(int edgeNum)269 unsigned char CNode::GetEdgeFlags( int edgeNum )
270 {
271 if ( edgeNum > m_numEdges )
272 return 0;
273
274 int count = 0;
275
276 edge_v::iterator ei;
277 STL_ITERATE( ei, m_edges )
278 {
279 if ( count == edgeNum )
280 {
281 return (*ei).flags;
282 }
283
284 count++;
285 }
286
287 return 0;
288 }
289
290 /*
291 -------------------------
292 SetEdgeFlags
293 -------------------------
294 */
295
SetEdgeFlags(int edgeNum,int newFlags)296 void CNode::SetEdgeFlags( int edgeNum, int newFlags )
297 {
298 if ( edgeNum > m_numEdges )
299 return;
300
301 int count = 0;
302
303 edge_v::iterator ei;
304 STL_ITERATE( ei, m_edges )
305 {
306 if ( count == edgeNum )
307 {
308 (*ei).flags = newFlags;
309 return;
310 }
311
312 count++;
313 }
314
315 }
316 /*
317 -------------------------
318 InitRanks
319 -------------------------
320 */
321
InitRanks(int size)322 void CNode::InitRanks( int size )
323 {
324 //Clear it if it's already allocated
325 if ( m_ranks != NULL )
326 {
327 delete [] m_ranks;
328 m_ranks = NULL;
329 }
330
331 m_ranks = new int[size];
332
333 memset( m_ranks, -1, sizeof(int)*size );
334 }
335
336 /*
337 -------------------------
338 GetRank
339 -------------------------
340 */
341
GetRank(int ID)342 int CNode::GetRank( int ID )
343 {
344 assert( m_ranks );
345
346 return m_ranks[ ID ];
347 }
348
349
350 /*
351 -------------------------
352 Save
353 -------------------------
354 */
355
Save(int numNodes,fileHandle_t file)356 int CNode::Save( int numNodes, fileHandle_t file )
357 {
358 int i;
359
360 //Write out the header
361 unsigned long header = NODE_HEADER_ID;
362 gi.FS_Write( &header, sizeof( header ), file );
363
364 //Write out the basic information
365 for ( i = 0; i < 3; i++ )
366 gi.FS_Write( &m_position[i], sizeof( float ), file );
367
368 gi.FS_Write( &m_flags, sizeof( m_flags ), file );
369 gi.FS_Write( &m_ID, sizeof( m_ID ), file );
370 gi.FS_Write( &m_radius, sizeof( m_radius ), file );
371
372 //Write out the edge information
373 gi.FS_Write( &m_numEdges, sizeof( m_numEdges ), file );
374
375 edge_v::iterator ei;
376 STL_ITERATE( ei, m_edges )
377 {
378 gi.FS_Write( &(*ei), sizeof( edge_t ), file );
379 }
380
381 //Write out the node ranks
382 gi.FS_Write( &numNodes, sizeof( numNodes ), file );
383
384 for ( i = 0; i < numNodes; i++ )
385 {
386 gi.FS_Write( &m_ranks[i], sizeof( int ), file );
387 }
388
389 return true;
390 }
391
392 /*
393 -------------------------
394 Load
395 -------------------------
396 */
397
Load(int numNodes,fileHandle_t file)398 int CNode::Load( int numNodes, fileHandle_t file )
399 {
400 unsigned long header;
401 int i;
402 gi.FS_Read( &header, sizeof(header), file );
403
404 //Validate the header
405 if ( header != NODE_HEADER_ID )
406 return false;
407
408 //Get the basic information
409 for ( i = 0; i < 3; i++ )
410 gi.FS_Read( &m_position[i], sizeof( float ), file );
411
412 gi.FS_Read( &m_flags, sizeof( m_flags ), file );
413 gi.FS_Read( &m_ID, sizeof( m_ID ), file );
414 gi.FS_Read( &m_radius, sizeof( m_radius ), file );
415
416 //Get the edge information
417 gi.FS_Read( &m_numEdges, sizeof( m_numEdges ), file );
418
419 for ( i = 0; i < m_numEdges; i++ )
420 {
421 edge_t edge;
422
423 gi.FS_Read( &edge, sizeof( edge_t ), file );
424
425 STL_INSERT( m_edges, edge );
426 }
427
428 //Read the node ranks
429 int numRanks;
430
431 gi.FS_Read( &numRanks, sizeof( numRanks ), file );
432
433 //Allocate the memory
434 InitRanks( numRanks );
435
436 for ( i = 0; i < numRanks; i++ )
437 {
438 gi.FS_Read( &m_ranks[i], sizeof( int ), file );
439 }
440
441 return true;
442 }
443
444 /*
445 -------------------------
446 CNavigator
447 -------------------------
448 */
449
CNavigator(void)450 CNavigator::CNavigator( void )
451 {
452 }
453
~CNavigator(void)454 CNavigator::~CNavigator( void )
455 {
456 }
457
458 /*
459 -------------------------
460 FlagAllNodes
461 -------------------------
462 */
463
FlagAllNodes(int newFlag)464 void CNavigator::FlagAllNodes( int newFlag )
465 {
466 node_v::iterator ni;
467
468 STL_ITERATE( ni, m_nodes )
469 {
470 (*ni)->AddFlag( newFlag );
471 }
472 }
473
474 /*
475 -------------------------
476 GetChar
477 -------------------------
478 */
479
GetChar(fileHandle_t file)480 char CNavigator::GetChar( fileHandle_t file )
481 {
482 char value;
483
484 gi.FS_Read( &value, sizeof( value ), file );
485
486 return value;
487 }
488
489 /*
490 -------------------------
491 GetInt
492 -------------------------
493 */
494
GetInt(fileHandle_t file)495 int CNavigator::GetInt( fileHandle_t file )
496 {
497 int value;
498
499 gi.FS_Read( &value, sizeof( value ), file );
500
501 return value;
502 }
503
504 /*
505 -------------------------
506 GetFloat
507 -------------------------
508 */
509
GetFloat(fileHandle_t file)510 float CNavigator::GetFloat( fileHandle_t file )
511 {
512 float value;
513
514 gi.FS_Read( &value, sizeof( value ), file );
515
516 return value;
517 }
518
519 /*
520 -------------------------
521 GetLong
522 -------------------------
523 */
524
GetLong(fileHandle_t file)525 long CNavigator::GetLong( fileHandle_t file )
526 {
527 long value;
528
529 gi.FS_Read( &value, sizeof( value ), file );
530
531 return value;
532 }
533
534 /*
535 -------------------------
536 Init
537 -------------------------
538 */
539
Init(void)540 void CNavigator::Init( void )
541 {
542 Free();
543 }
544
545 /*
546 -------------------------
547 Free
548 -------------------------
549 */
550
Free(void)551 void CNavigator::Free( void )
552 {
553 node_v::iterator ni;
554
555 STL_ITERATE( ni, m_nodes )
556 {
557 delete (*ni);
558 }
559 }
560
561 /*
562 -------------------------
563 Load
564 -------------------------
565 */
566
Load(const char * filename,int checksum)567 bool CNavigator::Load( const char *filename, int checksum )
568 {
569 fileHandle_t file;
570
571 //Attempt to load the file
572 gi.FS_FOpenFile( va( "maps/%s.nav", filename ), &file, FS_READ );
573
574 //See if we succeeded
575 if ( file == NULL_FILE )
576 return false;
577
578 //Check the header id
579 long navID = GetLong( file );
580
581 if ( navID != NAV_HEADER_ID )
582 {
583 gi.FS_FCloseFile( file );
584 return false;
585 }
586
587 //Check the checksum to see if this file is out of date
588 int check = GetInt( file );
589
590 if ( check != checksum )
591 {
592 gi.FS_FCloseFile( file );
593 return false;
594 }
595
596 int numNodes = GetInt( file );
597
598 for ( int i = 0; i < numNodes; i++ )
599 {
600 CNode *node = CNode::Create();
601
602 if ( node->Load( numNodes, file ) == false )
603 {
604 gi.FS_FCloseFile( file );
605 return false;
606 }
607
608 STL_INSERT( m_nodes, node );
609 }
610
611 //read in the failed edges
612 gi.FS_Read( &failedEdges, sizeof( failedEdges ), file );
613 for ( int j = 0; j < MAX_FAILED_EDGES; j++ )
614 {
615 m_edgeLookupMap.insert(std::pair<int, int>(failedEdges[j].startID, j));
616 }
617
618
619 gi.FS_FCloseFile( file );
620
621 return true;
622 }
623
624 /*
625 -------------------------
626 Save
627 -------------------------
628 */
629
Save(const char * filename,int checksum)630 bool CNavigator::Save( const char *filename, int checksum )
631 {
632 fileHandle_t file;
633
634 //Attempt to load the file
635 gi.FS_FOpenFile( va( "maps/%s.nav", filename ), &file, FS_WRITE );
636
637 if ( file == NULL_FILE )
638 return false;
639
640 //Write out the header id
641 unsigned long id = NAV_HEADER_ID;
642
643 gi.FS_Write( &id, sizeof (id), file );
644
645 //Write out the checksum
646 gi.FS_Write( &checksum, sizeof( checksum ), file );
647
648 int numNodes = m_nodes.size();
649
650 //Write out the number of nodes to follow
651 gi.FS_Write( &numNodes, sizeof(numNodes), file );
652
653 //Write out all the nodes
654 node_v::iterator ni;
655
656 STL_ITERATE( ni, m_nodes )
657 {
658 (*ni)->Save( numNodes, file );
659 }
660
661 //write out failed edges
662 gi.FS_Write( &failedEdges, sizeof( failedEdges ), file );
663
664 gi.FS_FCloseFile( file );
665
666 return true;
667 }
668
669 /*
670 -------------------------
671 AddRawPoint
672 -------------------------
673 */
674
AddRawPoint(vec3_t point,int flags,int radius)675 int CNavigator::AddRawPoint( vec3_t point, int flags, int radius )
676 {
677 CNode *node = CNode::Create( point, flags, radius, m_nodes.size() );
678
679 if ( node == NULL )
680 {
681 Com_Error( ERR_DROP, "Error adding node!\n" );
682 return -1;
683 }
684
685 //TODO: Validate the position
686 //TODO: Correct stuck waypoints
687
688 STL_INSERT( m_nodes, node );
689
690 return node->GetID();
691 }
692
693 /*
694 -------------------------
695 GetEdgeCost
696 -------------------------
697 */
698
GetEdgeCost(CNode * first,CNode * second)699 int CNavigator::GetEdgeCost( CNode *first, CNode *second )
700 {
701 trace_t trace;
702 vec3_t start, end;
703 vec3_t mins, maxs;
704
705 //Setup the player size
706 VectorSet( mins, -8, -8, -8 );
707 VectorSet( maxs, 8, 8, 8 );
708
709 //Setup the points
710 first->GetPosition( start );
711 second->GetPosition( end );
712
713 gi.trace( &trace, start, mins, maxs, end, ENTITYNUM_NONE, MASK_SOLID, G2_NOCOLLIDE, 0 );
714
715 if ( trace.fraction < 1.0f || trace.allsolid || trace.startsolid )
716 return Q3_INFINITE; // return -1;
717
718 //Connection successful, return the cost
719 return Distance( start, end );
720 }
721
SetEdgeCost(int ID1,int ID2,int cost)722 void CNavigator::SetEdgeCost( int ID1, int ID2, int cost )
723 {
724 if( (ID1 == -1) || (ID2 == -1) )
725 {//not valid nodes, must have come from the ClearAllFailedEdges initization-type calls
726 return;
727 }
728
729 CNode *node1 = m_nodes[ID1];
730 CNode *node2 = m_nodes[ID2];
731
732 if ( cost == -1 )
733 {//they want us to calc it
734 //FIXME: can we just remember this instead of recalcing every time?
735 vec3_t pos1, pos2;
736
737 node1->GetPosition( pos1 );
738 node2->GetPosition( pos2 );
739 cost = Distance( pos1, pos2 );
740 }
741
742 //set it
743 node1->AddEdge( ID2, cost );
744 node2->AddEdge( ID1, cost );
745 }
746
747 /*
748 -------------------------
749 AddNodeEdges
750 -------------------------
751 */
752
AddNodeEdges(CNode * node,int addDist,edge_l & edgeList,bool * checkedNodes)753 void CNavigator::AddNodeEdges( CNode *node, int addDist, edge_l &edgeList, bool *checkedNodes )
754 {
755 //Add all edge
756 for ( int i = 0; i < node->GetNumEdges(); i++ )
757 {
758 //Make sure we don't add an old edge twice
759 if ( checkedNodes[ node->GetEdge( i ) ] == true )
760 continue;
761
762 //Get the node
763 CNode *nextNode = m_nodes[ node->GetEdge( i ) ];
764
765 //This node has now been checked
766 checkedNodes[ nextNode->GetID() ] = true;
767
768 //Add it to the list
769 STL_INSERT( edgeList, CEdge( nextNode->GetID(), node->GetID(), addDist + ( node->GetEdgeCost( i ) ) ) );
770 }
771 }
772
773 /*
774 -------------------------
775 CalculatePath
776 -------------------------
777 */
778
CalculatePath(CNode * node)779 void CNavigator::CalculatePath( CNode *node )
780 {
781 int curRank = 0;
782 int i;
783
784 CPriorityQueue *pathList = new CPriorityQueue();
785 unsigned char *checked;
786
787 //Init the completion table
788 checked = new unsigned char[ m_nodes.size() ];
789 memset( checked, 0, m_nodes.size() );
790
791 //Mark this node as checked
792 checked[ node->GetID() ] = true;
793 node->AddRank( node->GetID(), curRank++ );
794
795 //Add all initial nodes
796 for ( i = 0; i < node->GetNumEdges(); i++ )
797 {
798 CNode *nextNode = m_nodes[ node->GetEdge(i) ];
799 assert(nextNode);
800
801 checked[ nextNode->GetID() ] = true;
802
803 pathList->Push( new CEdge( nextNode->GetID(), nextNode->GetID(), node->GetEdgeCost(i) ) );
804 }
805
806 CEdge *test;
807
808 //Now flood fill all the others
809 while ( !pathList->Empty() )
810 {
811 test = pathList->Pop();
812
813 CNode *testNode = m_nodes[ (*test).m_first ];
814 assert( testNode );
815
816 node->AddRank( testNode->GetID(), curRank++ );
817
818 //Add in all the new edges
819 for ( i = 0; i < testNode->GetNumEdges(); i++ )
820 {
821 CNode *addNode = m_nodes[ testNode->GetEdge(i) ];
822 assert( addNode );
823
824 if ( checked[ addNode->GetID() ] )
825 continue;
826
827 int newDist = (*test).m_cost + testNode->GetEdgeCost(i);
828 pathList->Push( new CEdge( addNode->GetID(), (*test).m_second, newDist ) );
829
830 checked[ addNode->GetID() ] = true;
831 }
832 delete test;
833
834 }
835
836 node->RemoveFlag( NF_RECALC );
837
838 delete pathList;
839 delete [] checked;
840 }
841
842 /*
843 -------------------------
844 CalculatePaths
845 -------------------------
846 */
847 extern void CP_FindCombatPointWaypoints( void );
CalculatePaths(bool recalc)848 void CNavigator::CalculatePaths( bool recalc )
849 {
850 #ifndef FINAL_BUILD
851 int startTime = gi.Milliseconds();
852 #endif
853 #if _HARD_CONNECT
854 #else
855 #endif
856 int i;
857
858 for ( i = 0; i < (int)m_nodes.size(); i++ )
859 {
860 //Allocate the needed memory
861 m_nodes[i]->InitRanks( m_nodes.size() );
862 }
863
864 for ( i = 0; i < (int)m_nodes.size(); i++ )
865 {
866 CalculatePath( m_nodes[i] );
867 }
868
869 #ifndef FINAL_BUILD
870 if ( pathsCalculated )
871 {
872 gi.Printf( S_COLOR_CYAN"%s recalced paths in %d ms\n", (NPC!=NULL?NPC->targetname:"NULL"), gi.Milliseconds()-startTime );
873 }
874 #endif
875
876 if(!recalc) //Mike says doesn't need to happen on recalc
877 {
878 CP_FindCombatPointWaypoints();
879 }
880
881 pathsCalculated = qtrue;
882 }
883
884 /*
885 -------------------------
886 ShowNodes
887 -------------------------
888 */
889
ShowNodes(void)890 void CNavigator::ShowNodes( void )
891 {
892 node_v::iterator ni;
893
894 vec3_t position;
895 qboolean showRadius;
896 float dist,
897 radius;
898
899 STL_ITERATE( ni, m_nodes )
900 {
901 (*ni)->GetPosition( position );
902
903 showRadius = qfalse;
904 if( NAVDEBUG_showRadius )
905 {
906 dist = DistanceSquared( g_entities[0].currentOrigin, position );
907 radius = (*ni)->GetRadius();
908 // if player within node radius or 256, draw radius (sometimes the radius is really small, so we check for 256 to catch everything)
909 if( (dist <= radius*radius) || dist <= 65536 )
910 {
911 showRadius = qtrue;
912 }
913 }
914 else
915 {
916 dist = DistanceSquared( g_entities[0].currentOrigin, position );
917 }
918 if ( dist < 1048576 )
919 {
920 if ( gi.inPVS( g_entities[0].currentOrigin, position ) )
921 {
922 (*ni)->Draw(showRadius);
923 }
924 }
925 }
926 }
927
928 /*
929 -------------------------
930 ShowEdges
931 -------------------------
932 */
933
934 typedef std::map < int, bool > drawMap_m;
935
ShowEdges(void)936 void CNavigator::ShowEdges( void )
937 {
938 node_v::iterator ni;
939 vec3_t start, end;
940
941 drawMap_m *drawMap;
942
943 drawMap = new drawMap_m[ m_nodes.size() ];
944
945 STL_ITERATE( ni, m_nodes )
946 {
947 (*ni)->GetPosition( start );
948 if ( DistanceSquared( g_entities[0].currentOrigin, start ) >= 1048576 )
949 {
950 continue;
951 }
952
953 if ( !gi.inPVSIgnorePortals( g_entities[0].currentOrigin, start ) )
954 {
955 continue;
956 }
957
958 //Attempt to draw each connection
959 for ( int i = 0; i < (*ni)->GetNumEdges(); i++ )
960 {
961 int id = (*ni)->GetEdge( i );
962
963 if ( id == -1 )
964 continue;
965
966 //Already drawn?
967 if ( drawMap[(*ni)->GetID()].find( id ) != drawMap[(*ni)->GetID()].end() )
968 continue;
969
970 unsigned char flags = (*ni)->GetEdgeFlags( i );
971
972 CNode *node = m_nodes[id];
973
974 node->GetPosition( end );
975
976 //Set this as drawn
977 drawMap[id][(*ni)->GetID()] = true;
978
979 if ( DistanceSquared( g_entities[0].currentOrigin, end ) >= 1048576 )
980 {
981 continue;
982 }
983
984 if ( !gi.inPVSIgnorePortals( g_entities[0].currentOrigin, end ) )
985 continue;
986
987 if ( EdgeFailed( id, (*ni)->GetID() ) != -1 )//flags & EFLAG_FAILED )
988 CG_DrawEdge( start, end, EDGE_FAILED );
989 else if ( flags & EFLAG_BLOCKED )
990 CG_DrawEdge( start, end, EDGE_BLOCKED );
991 else
992 CG_DrawEdge( start, end, EDGE_NORMAL );
993 }
994 }
995
996 delete [] drawMap;
997 }
998
GetNodeRadius(int nodeID)999 int CNavigator::GetNodeRadius( int nodeID )
1000 {
1001 if ( m_nodes.size() == 0 )
1002 return 0;
1003 return m_nodes[nodeID]->GetRadius();
1004 }
1005
CheckBlockedEdges(void)1006 void CNavigator::CheckBlockedEdges( void )
1007 {
1008 CNode *start, *end;
1009 vec3_t p1, p2;
1010 int flags, first, second;
1011 trace_t trace;
1012 qboolean failed;
1013 int edgeNum;
1014 node_v::iterator ni;
1015
1016 //Go through all edges and test the ones that were blocked
1017 STL_ITERATE( ni, m_nodes )
1018 {
1019 //Attempt to draw each connection
1020 for ( edgeNum = 0; edgeNum < (*ni)->GetNumEdges(); edgeNum++ )
1021 {
1022 flags = (*ni)->GetEdgeFlags( edgeNum );
1023 if ( (flags&EFLAG_BLOCKED) )
1024 {
1025 first = (*ni)->GetID();
1026 second = (*ni)->GetEdge( edgeNum );
1027 start = m_nodes[first];
1028 end = m_nodes[second];
1029 failed = qfalse;
1030
1031 start->GetPosition( p1 );
1032 end->GetPosition( p2 );
1033
1034 //FIXME: can't we just store the trace.entityNum from the HardConnect trace? So we don't have to do another trace here...
1035 gi.trace( &trace, p1, wpMins, wpMaxs, p2, ENTITYNUM_NONE, MASK_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP, G2_NOCOLLIDE, 0 );
1036
1037 if ( trace.entityNum < ENTITYNUM_WORLD && (trace.fraction < 1.0f || trace.startsolid == qtrue || trace.allsolid == qtrue) )
1038 {//could be assumed, since failed before
1039 if ( G_EntIsDoor( trace.entityNum ) )
1040 {//door
1041 if ( !G_EntIsUnlockedDoor( trace.entityNum ) )
1042 {//locked door
1043 failed = qtrue;
1044 }
1045 }
1046 else
1047 {
1048 if ( G_EntIsBreakable( trace.entityNum ) )
1049 {//do same for breakable brushes/models/glass?
1050 failed = qtrue;
1051 }
1052 else if ( G_EntIsRemovableUsable( trace.entityNum ) )
1053 {
1054 failed = qtrue;
1055 }
1056 else if ( trace.allsolid || trace.startsolid )
1057 {//FIXME: the entitynum would be none here, so how do we know if this is stuck inside an ent or the world?
1058 }
1059 else
1060 {//FIXME: what about func_plats and scripted movers?
1061 }
1062 }
1063 }
1064
1065 if ( failed )
1066 {
1067 //could add the EFLAG_FAILED to the two edges, but we stopped doing that since it was pointless
1068 AddFailedEdge( ENTITYNUM_NONE, first, second );
1069 }
1070 }
1071 }
1072 }
1073 }
1074
1075 #if _HARD_CONNECT
1076
1077 /*
1078 -------------------------
1079 HardConnect
1080 -------------------------
1081 */
1082
HardConnect(int first,int second)1083 void CNavigator::HardConnect( int first, int second )
1084 {
1085 CNode *start, *end;
1086
1087 start = m_nodes[first];
1088 end = m_nodes[second];
1089
1090 vec3_t p1, p2;
1091
1092 start->GetPosition( p1 );
1093 end->GetPosition( p2 );
1094
1095 trace_t trace;
1096
1097 int flags = EFLAG_NONE;
1098
1099 gi.trace( &trace, p1, wpMins, wpMaxs, p2, ENTITYNUM_NONE, MASK_SOLID|CONTENTS_BOTCLIP|CONTENTS_MONSTERCLIP, G2_NOCOLLIDE, 0 );
1100
1101 int cost = Distance( p1, p2 );
1102
1103 if ( trace.fraction != 1.0f || trace.startsolid == qtrue || trace.allsolid == qtrue )
1104 {
1105 flags |= EFLAG_BLOCKED;
1106 }
1107
1108 start->AddEdge( second, cost, flags );
1109 end->AddEdge( first, cost, flags );
1110 }
1111
1112 #endif
1113
1114 /*
1115 -------------------------
1116 TestNodePath
1117 -------------------------
1118 */
1119
TestNodePath(gentity_t * ent,int okToHitEntNum,vec3_t position,qboolean includeEnts)1120 int CNavigator::TestNodePath( gentity_t *ent, int okToHitEntNum, vec3_t position, qboolean includeEnts )
1121 {
1122 int clipmask = ent->clipmask;
1123
1124 if ( !includeEnts )
1125 {
1126 clipmask &= ~CONTENTS_BODY;
1127 }
1128 //Check the path
1129 if ( NAV_ClearPathToPoint( ent, ent->mins, ent->maxs, position, clipmask, okToHitEntNum ) == false )
1130 return false;
1131
1132 return true;
1133 }
1134
1135 /*
1136 -------------------------
1137 TestNodeLOS
1138 -------------------------
1139 */
1140
TestNodeLOS(gentity_t * ent,vec3_t position)1141 int CNavigator::TestNodeLOS( gentity_t *ent, vec3_t position )
1142 {
1143 return NPC_ClearLOS( ent, position );
1144 }
1145
1146 /*
1147 -------------------------
1148 TestBestFirst
1149 -------------------------
1150 */
1151
TestBestFirst(gentity_t * ent,int lastID,int flags)1152 int CNavigator::TestBestFirst( gentity_t *ent, int lastID, int flags )
1153 {
1154 //Must be a valid one to begin with
1155 if ( lastID == NODE_NONE )
1156 return NODE_NONE;
1157
1158 if ( lastID >= (int)m_nodes.size() )
1159 return NODE_NONE;
1160
1161 //Get the info
1162 vec3_t nodePos;
1163 CNode *node = m_nodes[ lastID ];
1164 CNode *testNode;
1165 int numEdges = node->GetNumEdges();
1166 float dist;
1167
1168 node->GetPosition( nodePos );
1169
1170 //Setup our last node as our root, and search for a closer one according to its edges
1171 int bestNode = ( TestNodePath( ent, ENTITYNUM_NONE, nodePos, qtrue ) ) ? lastID : NODE_NONE;
1172 float bestDist = ( bestNode == NODE_NONE ) ? Q3_INFINITE : DistanceSquared( ent->currentOrigin, nodePos );
1173
1174 //Test all these edges first
1175 for ( int i = 0; i < numEdges; i++ )
1176 {
1177 //Get this node and its distance
1178 testNode = m_nodes[ node->GetEdge(i) ];
1179
1180 if ( NodeFailed( ent, testNode->GetID() ) )
1181 {
1182 continue;
1183 }
1184
1185 testNode->GetPosition( nodePos );
1186
1187 dist = DistanceSquared( ent->currentOrigin, nodePos );
1188
1189 //Test against current best
1190 if ( dist < bestDist )
1191 {
1192 //See if this node is valid
1193 if ( CheckedNode(testNode->GetID(),ent->s.number) == CHECKED_PASSED || TestNodePath( ent, ENTITYNUM_NONE, nodePos, qtrue ) )
1194 {
1195 bestDist = dist;
1196 bestNode = testNode->GetID();
1197 SetCheckedNode(testNode->GetID(),ent->s.number,CHECKED_PASSED);
1198 }
1199 else
1200 {
1201 SetCheckedNode(testNode->GetID(),ent->s.number,CHECKED_FAILED);
1202 }
1203 }
1204 }
1205
1206 return bestNode;
1207 }
1208
1209 /*
1210 -------------------------
1211 CollectNearestNodes
1212 -------------------------
1213 */
1214
1215 #define NODE_COLLECT_MAX 16 //Maximum # of nodes collected at any time
1216 #define NODE_COLLECT_RADIUS 512 //Default radius to search for nodes in
1217 #define NODE_COLLECT_RADIUS_SQR ( NODE_COLLECT_RADIUS * NODE_COLLECT_RADIUS )
1218
CollectNearestNodes(vec3_t origin,int radius,int maxCollect,nodeChain_l & nodeChain)1219 int CNavigator::CollectNearestNodes( vec3_t origin, int radius, int maxCollect, nodeChain_l &nodeChain )
1220 {
1221 node_v::iterator ni;
1222 float dist;
1223 vec3_t position;
1224 int collected = 0;
1225 bool added = false;
1226
1227 //Get a distance rating for each node in the system
1228 STL_ITERATE( ni, m_nodes )
1229 {
1230 //If we've got our quota, then stop looking
1231 //Get the distance to the node
1232 (*ni)->GetPosition( position );
1233 dist = DistanceSquared( position, origin );
1234
1235 //Must be within our radius range
1236 if ( dist > (float) ( radius * radius ) )
1237 continue;
1238
1239 nodeList_t nChain;
1240 nodeChain_l::iterator nci;
1241
1242 //Always add the first node
1243 if ( nodeChain.size() == 0 )
1244 {
1245 nChain.nodeID = (*ni)->GetID();
1246 nChain.distance = dist;
1247
1248 nodeChain.insert( nodeChain.begin(), nChain );
1249 continue;
1250 }
1251
1252 added = false;
1253
1254 //Compare it to what we already have
1255 STL_ITERATE( nci, nodeChain )
1256 {
1257 //If we're less, than this entry, then insert before it
1258 if ( dist < (*nci).distance )
1259 {
1260 nChain.nodeID = (*ni)->GetID();
1261 nChain.distance = dist;
1262
1263 nodeChain.insert( nci, nChain );
1264 collected = nodeChain.size();
1265 added = true;
1266
1267 //If we've hit our collection limit, throw off the oldest one
1268 if ( (int)nodeChain.size() > maxCollect )
1269 {
1270 nodeChain.pop_back();
1271 }
1272
1273 break;
1274 }
1275 }
1276
1277 //Otherwise, always pad out the collection if possible so we don't miss anything
1278 if ( ( added == false ) && ( (int)nodeChain.size() < maxCollect ) )
1279 {
1280 nChain.nodeID = (*ni)->GetID();
1281 nChain.distance = dist;
1282
1283 nodeChain.insert( nodeChain.end(), nChain );
1284 }
1285 }
1286
1287 return collected;
1288 }
1289
GetBestPathBetweenEnts(gentity_t * ent,gentity_t * goal,int flags)1290 int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int flags )
1291 {
1292 //Must have nodes
1293 if ( m_nodes.size() == 0 )
1294 return NODE_NONE;
1295
1296 #define MAX_Z_DELTA 18
1297
1298 nodeChain_l nodeChain;
1299 nodeChain_l::iterator nci;
1300 nodeChain_l nodeChain2;
1301 nodeChain_l::iterator nci2;
1302
1303 //Collect all nodes within a certain radius
1304 CollectNearestNodes( ent->currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain );
1305 CollectNearestNodes( goal->currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain2 );
1306
1307 vec3_t position;
1308 vec3_t position2;
1309 int radius;
1310 int cost, pathCost, bestCost = Q3_INFINITE;
1311 CNode *node, *node2;
1312 int nodeNum, nodeNum2;
1313 int nextNode = NODE_NONE, bestNode = NODE_NONE;
1314 int nodeFlags = 0;
1315 // bool recalc = false;
1316
1317 ent->waypoint = NODE_NONE;
1318 goal->waypoint = NODE_NONE;
1319
1320 //Look through all nodes
1321 STL_ITERATE( nci, nodeChain )
1322 {
1323 node = m_nodes[(*nci).nodeID];
1324 nodeNum = (*nci).nodeID;
1325
1326 node->GetPosition( position );
1327
1328 if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_FAILED )
1329 {//already checked this node against ent and it failed
1330 continue;
1331 }
1332 if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_PASSED )
1333 {//already checked this node against ent and it passed
1334 }
1335 else
1336 {//haven't checked this node against ent yet
1337 if ( NodeFailed( ent, nodeNum ) )
1338 {
1339 SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED );
1340 continue;
1341 }
1342 //okay, since we only have to do this once, let's check to see if this node is even usable (could help us short-circuit a whole loop of the dest nodes)
1343 radius = node->GetRadius();
1344
1345 //If we're not within the known clear radius of this node OR out of Z height range...
1346 if ( (signed)(*nci).distance >= (radius*radius) || ( fabs( position[2] - ent->currentOrigin[2] ) >= MAX_Z_DELTA ) )
1347 {
1348 //We're not *within* this node, so check clear path, etc.
1349
1350 //FIXME: any way to call G_FindClosestPointOnLineSegment and see if I can at least get to the waypoint's path
1351 if ( flags & NF_CLEAR_PATH )//|| flags & NF_CLEAR_LOS )
1352 {//need a clear path or LOS
1353 if ( !gi.inPVS( ent->currentOrigin, position ) )
1354 {//not even potentially clear
1355 SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED );
1356 continue;
1357 }
1358 }
1359
1360 //Do we need a clear path?
1361 if ( flags & NF_CLEAR_PATH )
1362 {
1363 if ( TestNodePath( ent, goal->s.number, position, qtrue ) == false )
1364 {
1365 SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED );
1366 continue;
1367 }
1368 }
1369 }//otherwise, inside the node so it must be clear (?)
1370 SetCheckedNode( nodeNum, ent->s.number, CHECKED_PASSED );
1371 }
1372
1373 if ( d_altRoutes->integer )
1374 {
1375 //calc the paths for this node if they're out of date
1376 nodeFlags = node->GetFlags();
1377 if ( (nodeFlags&NF_RECALC) )
1378 {
1379 //gi.Printf( S_COLOR_CYAN"%d recalcing paths from node %d\n", level.time, nodeNum );
1380 CalculatePath( node );
1381 }
1382 }
1383
1384 STL_ITERATE( nci2, nodeChain2 )
1385 {
1386 node2 = m_nodes[(*nci2).nodeID];
1387 nodeNum2 = (*nci2).nodeID;
1388 if ( d_altRoutes->integer )
1389 {
1390 //calc the paths for this node if they're out of date
1391 nodeFlags = node2->GetFlags();
1392 if ( (nodeFlags&NF_RECALC) )
1393 {
1394 //gi.Printf( S_COLOR_CYAN"%d recalcing paths from node %d\n", level.time, nodeNum2 );
1395 CalculatePath( node2 );
1396 }
1397 }
1398
1399 node2->GetPosition( position2 );
1400 //Okay, first get the entire path cost, including distance to first node from ents' positions
1401 cost = floor(Distance( ent->currentOrigin, position ) + Distance( goal->currentOrigin, position2 ));
1402
1403 if ( d_altRoutes->integer )
1404 {
1405 nextNode = GetBestNodeAltRoute( (*nci).nodeID, (*nci2).nodeID, &pathCost, bestNode );
1406 cost += pathCost;
1407 }
1408 else
1409 {
1410 cost += GetPathCost( (*nci).nodeID, (*nci2).nodeID );
1411 }
1412
1413 if ( cost >= bestCost )
1414 {
1415 continue;
1416 }
1417
1418 //okay, this is the shortest path we've found yet, check clear path, etc.
1419 if ( CheckedNode( nodeNum2, goal->s.number ) == CHECKED_FAILED )
1420 {//already checked this node against goal and it failed
1421 continue;
1422 }
1423 if ( CheckedNode( nodeNum2, goal->s.number ) == CHECKED_PASSED )
1424 {//already checked this node against goal and it passed
1425 }
1426 else
1427 {//haven't checked this node against goal yet
1428 if ( NodeFailed( goal, nodeNum2 ) )
1429 {
1430 SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED );
1431 continue;
1432 }
1433 radius = node2->GetRadius();
1434
1435 //If we're not within the known clear radius of this node OR out of Z height range...
1436 if ( (signed)(*nci2).distance >= (radius*radius) || ( fabs( position2[2] - goal->currentOrigin[2] ) >= MAX_Z_DELTA ) )
1437 {
1438 //We're not *within* this node, so check clear path, etc.
1439
1440 if ( flags & NF_CLEAR_PATH )//|| flags & NF_CLEAR_LOS )
1441 {//need a clear path or LOS
1442 if ( !gi.inPVS( goal->currentOrigin, position2 ) )
1443 {//not even potentially clear
1444 SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED );
1445 continue;
1446 }
1447 }
1448 //Do we need a clear path?
1449 if ( flags & NF_CLEAR_PATH )
1450 {
1451 if ( TestNodePath( goal, ent->s.number, position2, qfalse ) == false )//qtrue?
1452 {
1453 SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED );
1454 continue;
1455 }
1456 }
1457 }//otherwise, inside the node so it must be clear (?)
1458 SetCheckedNode( nodeNum2, goal->s.number, CHECKED_PASSED );
1459 }
1460
1461 bestCost = cost;
1462 bestNode = nextNode;
1463 ent->waypoint = (*nci).nodeID;
1464 goal->waypoint = (*nci2).nodeID;
1465 }
1466 }
1467
1468 if ( !d_altRoutes->integer )
1469 {//bestNode would not have been set by GetBestNodeAltRoute above, so get it here
1470 if ( ent->waypoint != NODE_NONE && goal->waypoint != NODE_NONE )
1471 {//have 2 valid waypoints which means a valid path
1472 bestNode = GetBestNodeAltRoute( ent->waypoint, goal->waypoint, &bestCost, NODE_NONE );
1473 }
1474 }
1475 return bestNode;
1476 }
1477
1478 /*
1479 -------------------------
1480 GetNearestWaypoint
1481 -------------------------
1482 */
1483
GetNearestNode(gentity_t * ent,int lastID,int flags,int targetID)1484 int CNavigator::GetNearestNode( gentity_t *ent, int lastID, int flags, int targetID )
1485 {
1486 int bestNode = NODE_NONE;
1487 //Must have nodes
1488 if ( m_nodes.size() == 0 )
1489 return NODE_NONE;
1490
1491 if ( targetID == NODE_NONE )
1492 {
1493 //Try and find an early match using our last node
1494 bestNode = TestBestFirst( ent, lastID, flags );
1495
1496 if ( bestNode != NODE_NONE )
1497 return bestNode;
1498 }//else can't rely on testing last, we want best to targetID
1499
1500 /////////////////////////////////////////////////
1501
1502 #define MAX_Z_DELTA 18
1503
1504 /////////////////////////////////////////////////
1505
1506 nodeChain_l nodeChain;
1507 nodeChain_l::iterator nci;
1508
1509 //Collect all nodes within a certain radius
1510 CollectNearestNodes( ent->currentOrigin, NODE_COLLECT_RADIUS, NODE_COLLECT_MAX, nodeChain );
1511
1512 vec3_t position;
1513 int radius;
1514 int dist, bestDist = Q3_INFINITE;
1515 CNode *node;
1516
1517 //Look through all nodes
1518 STL_ITERATE( nci, nodeChain )
1519 {
1520 node = m_nodes[(*nci).nodeID];
1521
1522 node->GetPosition( position );
1523
1524 radius = node->GetRadius();
1525
1526 if ( NodeFailed( ent, (*nci).nodeID ) )
1527 {
1528 continue;
1529 }
1530 //Are we within the known clear radius of this node?
1531 if ( (signed)(*nci).distance < (radius*radius) )
1532 {
1533 //Do a z-difference sanity check
1534 if ( fabs( position[2] - ent->currentOrigin[2] ) < MAX_Z_DELTA )
1535 {
1536 //Found one
1537 return (*nci).nodeID;
1538 }
1539 }
1540
1541 //We're not *within* this node, so...
1542 if ( CheckedNode((*nci).nodeID,ent->s.number) == CHECKED_FAILED )
1543 {
1544 continue;
1545 }
1546 else if ( CheckedNode((*nci).nodeID,ent->s.number) == CHECKED_FAILED )
1547 {
1548 continue;
1549 }
1550 else
1551 {
1552 //Do we need a clear path?
1553 if ( flags & NF_CLEAR_PATH )
1554 {
1555 if ( TestNodePath( ent, ENTITYNUM_NONE, position, qfalse ) == false )//qtrue?
1556 {
1557 SetCheckedNode((*nci).nodeID,ent->s.number,CHECKED_FAILED);
1558 continue;
1559 }
1560 }
1561
1562 //Do we need a clear line of sight?
1563 /*
1564 if ( flags & NF_CLEAR_LOS )
1565 {
1566 if ( TestNodeLOS( ent, position ) == false )
1567 {
1568 nodeChecked[(*nci).nodeID][ent->s.number] = CHECKED_FAILED;
1569 continue;
1570 }
1571 }
1572 */
1573 SetCheckedNode((*nci).nodeID,ent->s.number,CHECKED_PASSED);
1574 }
1575
1576 if ( targetID != WAYPOINT_NONE )
1577 {//we want to find the one with the shortest route here
1578 dist = GetPathCost( (*nci).nodeID, targetID );
1579 if ( dist < bestDist )
1580 {
1581 bestDist = dist;
1582 bestNode = (*nci).nodeID;
1583 }
1584 }
1585 else
1586 {//first one we find is fine
1587 bestNode = (*nci).nodeID;
1588 break;
1589 }
1590 }
1591
1592 //Found one, we're done
1593 return bestNode;
1594 }
1595
1596 /*
1597 -------------------------
1598 ShowPath
1599 -------------------------
1600 */
1601
ShowPath(int start,int end)1602 void CNavigator::ShowPath( int start, int end )
1603 {
1604 //Validate the start position
1605 if ( ( start < 0 ) || ( start >= (int)m_nodes.size() ) )
1606 return;
1607
1608 //Validate the end position
1609 if ( ( end < 0 ) || ( end >= (int)m_nodes.size() ) )
1610 return;
1611
1612 CNode *startNode = m_nodes[ start ];
1613 CNode *endNode = m_nodes[ end ];
1614
1615 CNode *moveNode = startNode;
1616 CNode *testNode = NULL;
1617
1618 int bestNode;
1619 vec3_t startPos, endPos;
1620
1621 int runAway = 0;
1622
1623 //Draw out our path
1624 while ( moveNode != endNode )
1625 {
1626 bestNode = GetBestNode( moveNode->GetID(), end );
1627
1628 //Some nodes may be fragmented
1629 if ( bestNode == -1 )
1630 {
1631 Com_Printf("No connection possible between node %d and %d\n", start, end );
1632 return;
1633 }
1634
1635 //This is our next node on the path
1636 testNode = m_nodes[ bestNode ];
1637
1638 //Get their origins
1639 moveNode->GetPosition( startPos );
1640 testNode->GetPosition( endPos );
1641
1642 //Draw the edge
1643 CG_DrawEdge( startPos, endPos, EDGE_PATH );
1644
1645 //Take a new best node
1646 moveNode = testNode;
1647
1648 if ( runAway++ > 64 )
1649 {
1650 Com_Printf("Potential Run-away path!\n");
1651 return;
1652 }
1653 }
1654 }
1655
1656 static std::map<int,byte> CheckedNodes;
ClearCheckedNodes(void)1657 void CNavigator::ClearCheckedNodes( void )
1658 {
1659 CheckedNodes.clear();
1660 }
1661
CheckedNode(int wayPoint,int ent)1662 byte CNavigator::CheckedNode(int wayPoint,int ent)
1663 {
1664 assert(wayPoint>=0&&wayPoint<MAX_STORED_WAYPOINTS);
1665 assert(ent>=0&&ent<MAX_GENTITIES);
1666 std::map<int,byte>::iterator f=CheckedNodes.find(wayPoint*MAX_GENTITIES+ent);
1667 if (f!=CheckedNodes.end())
1668 {
1669 return (*f).second;
1670 }
1671 return CHECKED_NO;
1672 }
1673
SetCheckedNode(int wayPoint,int ent,byte value)1674 void CNavigator::SetCheckedNode(int wayPoint,int ent,byte value)
1675 {
1676 assert(wayPoint>=0&&wayPoint<MAX_STORED_WAYPOINTS);
1677 assert(ent>=0&&ent<MAX_GENTITIES);
1678 assert(value==CHECKED_FAILED||value==CHECKED_PASSED);
1679 CheckedNodes[wayPoint*MAX_GENTITIES+ent]=value;
1680 }
1681
1682 #define CHECK_FAILED_EDGE_INTERVAL 1000
1683 #define CHECK_FAILED_EDGE_INTITIAL 5000//10000
1684
CheckFailedNodes(gentity_t * ent)1685 void CNavigator::CheckFailedNodes( gentity_t *ent )
1686 {
1687 vec3_t nodePos;
1688 int j;
1689
1690 //Must have nodes
1691 if ( m_nodes.size() == 0 )
1692 return;
1693
1694 if ( ent->failedWaypointCheckTime && ent->failedWaypointCheckTime < level.time )
1695 {
1696 int failed = 0;
1697 //do this only once every 1 second
1698 for ( j = 0; j < MAX_FAILED_NODES; j++ )
1699 {
1700 if ( ent->failedWaypoints[j] != 0 )
1701 {
1702 failed++;
1703 //-1 because 0 is a valid node but also the default, so we add one when we add one
1704 m_nodes[ent->failedWaypoints[j]-1]->GetPosition( nodePos );
1705 if ( !NAV_ClearPathToPoint( ent, ent->mins, ent->maxs, nodePos, (CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP), ENTITYNUM_NONE ) )
1706 {//no path clear of architecture, so clear this since we can't check against entities
1707 ent->failedWaypoints[j] = 0;
1708 failed--;
1709 }
1710 //have clear architectural path, now check against ents only
1711 else if ( NAV_ClearPathToPoint( ent, ent->mins, ent->maxs, nodePos, CONTENTS_BODY, ENTITYNUM_NONE ) )
1712 {//clear of ents, too, so all clear, clear this one out
1713 ent->failedWaypoints[j] = 0;
1714 failed--;
1715 }
1716 }
1717 }
1718 if ( !failed )
1719 {
1720 ent->failedWaypointCheckTime = 0;
1721 }
1722 else
1723 {
1724 ent->failedWaypointCheckTime = level.time + CHECK_FAILED_EDGE_INTERVAL + Q_irand( 0, 1000 );
1725 }
1726 }
1727 }
1728
AddFailedNode(gentity_t * ent,int nodeID)1729 void CNavigator::AddFailedNode( gentity_t *ent, int nodeID )
1730 {
1731 int j;
1732 for ( j = 0; j < MAX_FAILED_NODES; j++ )
1733 {
1734 if ( ent->failedWaypoints[j] == 0 )
1735 {
1736 ent->failedWaypoints[j] = nodeID+1;//+1 because 0 is the default value and that's a valid node, so we take the +1 out when we check the node above
1737 if ( !ent->failedWaypointCheckTime )
1738 {
1739 ent->failedWaypointCheckTime = level.time + CHECK_FAILED_EDGE_INTITIAL;
1740 }
1741 return;
1742 }
1743 if ( ent->failedWaypoints[j] == nodeID+1 )
1744 {//already have this one marked as failed
1745 return;
1746 }
1747 }
1748 if ( j == MAX_FAILED_NODES )//check not needed, but...
1749 {//ran out of failed nodes, get rid of first one, shift rest up
1750 for ( j = 0; j < MAX_FAILED_NODES-1; j++ )
1751 {
1752 ent->failedWaypoints[j] = ent->failedWaypoints[j+1];
1753 }
1754 }
1755 ent->failedWaypoints[MAX_FAILED_NODES-1] = nodeID+1;
1756 if ( !ent->failedWaypointCheckTime )
1757 {
1758 ent->failedWaypointCheckTime = level.time + CHECK_FAILED_EDGE_INTITIAL;
1759 }
1760 }
1761
NodeFailed(gentity_t * ent,int nodeID)1762 qboolean CNavigator::NodeFailed( gentity_t *ent, int nodeID )
1763 {
1764 for ( int j = 0; j < MAX_FAILED_NODES; j++ )
1765 {
1766 if ( (ent->failedWaypoints[j]-1) == nodeID )
1767 {
1768 return qtrue;
1769 }
1770 }
1771 return qfalse;
1772 }
1773
NodesAreNeighbors(int startID,int endID)1774 qboolean CNavigator::NodesAreNeighbors( int startID, int endID )
1775 {//See if these 2 are neighbors
1776 if ( startID == endID )
1777 {
1778 return qfalse;
1779 }
1780
1781 CNode *start = m_nodes[startID];
1782 int nextID = -1;
1783 //NOTE: we only check start because we assume all connections are 2-way
1784 for ( int i = 0; i < start->GetNumEdges(); i++ )
1785 {
1786 nextID = start->GetEdge(i);
1787 if ( nextID == endID )
1788 {
1789 return qtrue;
1790 }
1791 }
1792 //not neighbors
1793 return qfalse;
1794 }
1795
ClearFailedEdge(failedEdge_t * failedEdge)1796 void CNavigator::ClearFailedEdge( failedEdge_t *failedEdge )
1797 {
1798 if ( !failedEdge )
1799 {
1800 return;
1801 }
1802
1803 //clear the edge failed flags
1804 /*
1805 CNode *node = m_nodes[failedEdge->startID];
1806 int edgeNum = node->GetEdgeNumToNode( failedEdge->endID );
1807 int flags;
1808 if ( edgeNum != -1 )
1809 {
1810 flags = node->GetEdgeFlags( edgeNum )&~EFLAG_FAILED;
1811 node->SetEdgeFlags( edgeNum, flags );
1812 }
1813 node = m_nodes[failedEdge->endID];
1814 edgeNum = node->GetEdgeNumToNode( failedEdge->startID );
1815 if ( edgeNum != -1 )
1816 {
1817 flags = node->GetEdgeFlags( edgeNum )&~EFLAG_FAILED;
1818 node->SetEdgeFlags( edgeNum, flags );
1819 }
1820 */
1821 //clear failedEdge info
1822 SetEdgeCost( failedEdge->startID, failedEdge->endID, -1 );
1823 failedEdge->startID = failedEdge->endID = WAYPOINT_NONE;
1824 failedEdge->entID = ENTITYNUM_NONE;
1825 failedEdge->checkTime = 0;
1826 }
1827
ClearAllFailedEdges(void)1828 void CNavigator::ClearAllFailedEdges( void )
1829 {
1830 memset( &failedEdges, WAYPOINT_NONE, sizeof( failedEdges ) );
1831 for ( int j = 0; j < MAX_FAILED_EDGES; j++ )
1832 {
1833 ClearFailedEdge( &failedEdges[j] );
1834 }
1835 }
1836
EdgeFailed(int startID,int endID)1837 int CNavigator::EdgeFailed( int startID, int endID )
1838 {
1839 //OPTIMIZED WAY (bjg 01/02)
1840 //find in lookup map
1841 std::pair <EdgeMultimapIt, EdgeMultimapIt> findValue;
1842 findValue = m_edgeLookupMap.equal_range(startID);
1843 while ( findValue.first != findValue.second )
1844 {
1845 if( failedEdges[findValue.first->second].endID == endID)
1846 {
1847 return findValue.first->second;
1848 }
1849 ++findValue.first;
1850 }
1851 findValue = m_edgeLookupMap.equal_range(endID);
1852 while ( findValue.first != findValue.second )
1853 {
1854 if( failedEdges[findValue.first->second].endID == startID)
1855 {
1856 return findValue.first->second;
1857 }
1858 ++findValue.first;
1859 }
1860
1861 return -1;
1862
1863 //Old way (linear search)
1864 /*
1865 for ( int j = 0; j < MAX_FAILED_EDGES; j++ )
1866 {
1867 if ( failedEdges[j].startID == startID )
1868 {
1869 if ( failedEdges[j].endID == endID )
1870 {
1871 return j;
1872 }
1873 }
1874 else if ( failedEdges[j].startID == endID )
1875 {
1876 if ( failedEdges[j].endID == startID )
1877 {
1878 return j;
1879 }
1880 }
1881 }
1882 return -1;
1883 */
1884 }
1885
AddFailedEdge(int entID,int startID,int endID)1886 void CNavigator::AddFailedEdge( int entID, int startID, int endID )
1887 {
1888 int j;//, nextID;
1889
1890 //Must have nodes
1891 if ( m_nodes.size() == 0 )
1892 return;
1893
1894 if ( d_patched->integer )
1895 {//use patch-style navigation
1896 if ( startID == endID )
1897 {//not an edge!
1898 return;
1899 }
1900 }
1901
1902 //Validate the ent number
1903 if ( ( entID < 0 ) || ( entID > ENTITYNUM_NONE ) )
1904 {
1905 #ifndef FINAL_BUILD
1906 gi.Printf( S_COLOR_RED"NAV ERROR: envalid ent %d\n", entID );
1907 assert(0&&"invalid entID");
1908 #endif
1909 return;
1910 }
1911
1912 //Validate the start position
1913 if ( ( startID < 0 ) || ( startID >= (int)m_nodes.size() ) )
1914 {
1915 #ifndef FINAL_BUILD
1916 gi.Printf( S_COLOR_RED"NAV ERROR: tried to fail invalid waypoint %d\n", startID );
1917 assert(0&&"invalid failed edge");
1918 #endif
1919 return;
1920 }
1921
1922 //Validate the end position
1923 if ( ( endID < 0 ) || ( endID >= (int)m_nodes.size() ) )
1924 {
1925 #ifndef FINAL_BUILD
1926 gi.Printf( S_COLOR_RED"NAV ERROR: tried to fail invalid waypoint %d\n", endID );
1927 assert(0&&"invalid failed edge");
1928 #endif
1929 return;
1930 }
1931
1932 //First see if we already have this one
1933 if ( (j = EdgeFailed( startID, endID )) != -1 )
1934 {
1935 //just remember this guy instead
1936 failedEdges[j].entID = entID;
1937 return;
1938 }
1939
1940 //Okay, new one, find an empty slot
1941 for ( j = 0; j < MAX_FAILED_EDGES; j++ )
1942 {
1943 if ( failedEdges[j].startID == WAYPOINT_NONE )
1944 {
1945 failedEdges[j].startID = startID;
1946 failedEdges[j].endID = endID;
1947 //Check one second from now to see if it's clear
1948 failedEdges[j].checkTime = level.time + CHECK_FAILED_EDGE_INTERVAL + Q_irand( 0, 1000 );
1949
1950 m_edgeLookupMap.insert(std::pair<int, int>(startID, j));
1951
1952 /*
1953 //DISABLED this for now, makes people stand around too long when
1954 // collision avoidance just wasn't at it's best but path is clear
1955 CNode *start = m_nodes[startID];
1956 CNode *end = m_nodes[endID];
1957
1958 for ( int i = 0; i < start->GetNumEdges(); i++ )
1959 {
1960 nextID = start->GetEdge(i);
1961
1962 if ( EdgeFailed( startID, nextID ) != -1 )
1963 {
1964 //This edge blocked, check next
1965 continue;
1966 }
1967
1968 if ( nextID == endID || end->GetRank( nextID ) >= 0 )
1969 {//neighbor of or route to end
1970 //There's an alternate route, so don't check this one for 10 seconds
1971 failedEdges[j].checkTime = level.time + CHECK_FAILED_EDGE_INTITIAL;
1972 break;
1973 }
1974 }
1975 */
1976
1977 //Remember who needed it
1978 failedEdges[j].entID = entID;
1979
1980 //set the edge failed flags
1981 /*
1982 CNode *node = m_nodes[startID];
1983 int edgeNum = node->GetEdgeNumToNode( endID );
1984 int flags;
1985 if ( edgeNum != -1 )
1986 {
1987 flags = node->GetEdgeFlags( edgeNum )|EFLAG_FAILED;
1988 node->SetEdgeFlags( edgeNum, flags );
1989 }
1990 node = m_nodes[endID];
1991 edgeNum = node->GetEdgeNumToNode( startID );
1992 if ( edgeNum != -1 )
1993 {
1994 flags = node->GetEdgeFlags( edgeNum )|EFLAG_FAILED;
1995 node->SetEdgeFlags( edgeNum, flags );
1996 }
1997 */
1998
1999 //stuff the index to this one in our lookup map
2000
2001 //now recalc all the paths!
2002 if ( pathsCalculated )
2003 {
2004 //reconnect the nodes and mark every node's flag NF_RECALC
2005 //gi.Printf( S_COLOR_CYAN"%d marking all nodes for recalc\n", level.time );
2006 SetEdgeCost( startID, endID, Q3_INFINITE );
2007 FlagAllNodes( NF_RECALC );
2008 }
2009 return;
2010 }
2011 }
2012
2013 #ifndef FINAL_BUILD
2014 gi.Printf( S_COLOR_RED"NAV ERROR: too many blocked waypoint connections (%d)!!!\n", j );
2015 #endif
2016 }
2017
CheckFailedEdge(failedEdge_t * failedEdge)2018 qboolean CNavigator::CheckFailedEdge( failedEdge_t *failedEdge )
2019 {
2020 if ( !failedEdge )
2021 {
2022 return qfalse;
2023 }
2024
2025 //Every 1 second, see if our failed edges are clear
2026 if ( failedEdge->checkTime < level.time )
2027 {
2028 if ( failedEdge->startID != WAYPOINT_NONE )
2029 {
2030 vec3_t start, end, mins, maxs;
2031 int ignore, clipmask;
2032 gentity_t *ent = (failedEdge->entID<ENTITYNUM_WORLD)?&g_entities[failedEdge->entID]:NULL;
2033 int hitEntNum;
2034
2035 if ( !ent || !ent->inuse || !ent->client || ent->health <= 0 )
2036 {
2037 VectorSet( mins, DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2+STEPSIZE );
2038 VectorSet( maxs, DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2 );
2039 ignore = ENTITYNUM_NONE;
2040 clipmask = MASK_NPCSOLID;
2041 }
2042 else
2043 {
2044 VectorCopy( ent->mins, mins );
2045 mins[2] += STEPSIZE;
2046 VectorCopy( ent->maxs, maxs );
2047 ignore = failedEdge->entID;
2048 clipmask = ent->clipmask;
2049 }
2050
2051 if ( maxs[2] < mins[2] )
2052 {//don't invert bounding box
2053 maxs[2] = mins[2];
2054 }
2055
2056 m_nodes[failedEdge->startID]->GetPosition( start );
2057 m_nodes[failedEdge->endID]->GetPosition( end );
2058
2059 //See if it's NAV_ClearPath...
2060 #if 0
2061 hitEntNum = NAVNEW_ClearPathBetweenPoints( start, end, mins, maxs, ignore, clipmask|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP );//NOTE: should we really always include monsterclip (physically blocks NPCs) and botclip (do not enter)?
2062 #else
2063 trace_t trace;
2064
2065 //Test if they're even conceivably close to one another
2066 if ( !gi.inPVSIgnorePortals( start, end ) )
2067 {
2068 return qfalse;
2069 }
2070
2071 gi.trace( &trace, start, mins, maxs, end, ignore, clipmask|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP, G2_NOCOLLIDE, 0 );//NOTE: should we really always include monsterclip (physically blocks NPCs) and botclip (do not enter)?
2072
2073 if( trace.startsolid == qtrue || trace.allsolid == qtrue )
2074 {
2075 return qfalse;
2076 }
2077 hitEntNum = trace.entityNum;
2078 #endif
2079 //if we did hit something, see if it's just an auto-door and allow it
2080 if ( hitEntNum != ENTITYNUM_NONE && G_EntIsUnlockedDoor( hitEntNum ) )
2081 {
2082 hitEntNum = ENTITYNUM_NONE;
2083 }
2084 else if ( hitEntNum == failedEdge->entID )
2085 {//don't hit the person who initially marked the edge failed
2086 hitEntNum = ENTITYNUM_NONE;
2087 }
2088 if ( hitEntNum == ENTITYNUM_NONE )
2089 {
2090 //If so, clear it
2091 ClearFailedEdge( failedEdge );
2092 return qtrue;
2093 }
2094 else
2095 {
2096 //Check again in one second
2097 failedEdge->checkTime = level.time + CHECK_FAILED_EDGE_INTERVAL + Q_irand( 0, 1000 );
2098 }
2099 }
2100 }
2101 return qfalse;
2102 }
2103
CheckAllFailedEdges(void)2104 void CNavigator::CheckAllFailedEdges( void )
2105 {
2106 failedEdge_t *failedEdge;
2107 qboolean clearedAny = qfalse;
2108
2109 //Must have nodes
2110 if ( m_nodes.size() == 0 )
2111 return;
2112
2113 for ( int j = 0; j < MAX_FAILED_EDGES; j++ )
2114 {
2115 failedEdge = &failedEdges[j];
2116
2117 clearedAny = CheckFailedEdge( failedEdge )?qtrue:clearedAny;
2118 }
2119 if ( clearedAny )
2120 {//need to recalc the paths
2121 if ( pathsCalculated )
2122 {
2123 //reconnect the nodes and mark every node's flag NF_RECALC
2124 //gi.Printf( S_COLOR_CYAN"%d marking all nodes for recalc\n", level.time );
2125 FlagAllNodes( NF_RECALC );
2126 }
2127 }
2128 }
2129
RouteBlocked(int startID,int testEdgeID,int endID,int rejectRank)2130 qboolean CNavigator::RouteBlocked( int startID, int testEdgeID, int endID, int rejectRank )
2131 {
2132 int nextID, edgeID, lastID, bestNextID = NODE_NONE;
2133 int bestRank = rejectRank;
2134 int testRank;
2135 qboolean allEdgesFailed;
2136 CNode *end;
2137 CNode *next;
2138
2139
2140 if ( EdgeFailed( startID, testEdgeID ) != -1 )
2141 {
2142 return qtrue;
2143 }
2144
2145 if ( testEdgeID == endID )
2146 {//Neighbors, checked out, all clear
2147 return qfalse;
2148 }
2149
2150 //Okay, first edge is clear, now check rest of route!
2151 end = m_nodes[ endID ];
2152 nextID = testEdgeID;
2153 lastID = startID;
2154
2155 while( 1 )
2156 {
2157 next = m_nodes[ nextID ];
2158 allEdgesFailed = qtrue;
2159
2160 for ( int i = 0; i < next->GetNumEdges(); i++ )
2161 {
2162 edgeID = next->GetEdge(i);
2163
2164 if ( edgeID == lastID )
2165 {//Don't backtrack
2166 continue;
2167 }
2168
2169 if ( edgeID == startID )
2170 {//Don't loop around
2171 continue;
2172 }
2173
2174 if ( EdgeFailed( nextID, edgeID ) != -1 )
2175 {
2176 //This edge blocked, check next
2177 continue;
2178 }
2179
2180 if ( edgeID == endID )
2181 {//We got there all clear!
2182 return qfalse;
2183 }
2184
2185 //Still going...
2186 testRank = end->GetRank( edgeID );
2187
2188 if ( testRank < 0 )
2189 {//No route this way
2190 continue;
2191 }
2192
2193 //Is the rank good enough?
2194 if ( testRank < bestRank )
2195 {
2196 bestNextID = edgeID;
2197 bestRank = testRank;
2198 allEdgesFailed = qfalse;
2199 }
2200 }
2201
2202 if ( allEdgesFailed )
2203 {
2204 //This route has no clear way of getting to end
2205 return qtrue;
2206 }
2207 else
2208 {
2209 lastID = nextID;
2210 nextID = bestNextID;
2211 }
2212 }
2213 }
2214
2215 /*
2216 -------------------------
2217 GetBestNodeAltRoute
2218 -------------------------
2219 */
2220
GetBestNodeAltRoute(int startID,int endID,int * pathCost,int rejectID)2221 int CNavigator::GetBestNodeAltRoute( int startID, int endID, int *pathCost, int rejectID )
2222 {
2223 //Must have nodes
2224 if ( m_nodes.size() == 0 )
2225 return WAYPOINT_NONE;
2226
2227 //Validate the start position
2228 if ( ( startID < 0 ) || ( startID >= (int)m_nodes.size() ) )
2229 return WAYPOINT_NONE;
2230
2231 //Validate the end position
2232 if ( ( endID < 0 ) || ( endID >= (int)m_nodes.size() ) )
2233 return WAYPOINT_NONE;
2234
2235 //Is it the same node?
2236 if ( startID == endID )
2237 {
2238 if ( !d_altRoutes->integer || EdgeFailed( startID, endID ) == -1 )
2239 {
2240 return startID;
2241 }
2242 else
2243 {
2244 return WAYPOINT_NONE;
2245 }
2246 }
2247
2248 CNode *start = m_nodes[ startID ];
2249
2250 int bestNode = -1;
2251 int bestRank = Q3_INFINITE;
2252 int testRank, rejectRank = Q3_INFINITE;
2253 int bestCost = Q3_INFINITE;
2254
2255 *pathCost = 0;
2256
2257 //Find the minimum rank of the edge(s) we want to reject as paths
2258 if ( rejectID != WAYPOINT_NONE )
2259 {
2260 for ( int i = 0; i < start->GetNumEdges(); i++ )
2261 {
2262 if ( start->GetEdge(i) == rejectID )
2263 {
2264 rejectRank = GetPathCost( startID, endID );//end->GetRank( start->GetEdge(i) );
2265 break;
2266 }
2267 }
2268 }
2269
2270 for ( int i = 0; i < start->GetNumEdges(); i++ )
2271 {
2272 int edgeID = start->GetEdge(i);
2273
2274 testRank = GetPathCost( edgeID, endID );//end->GetRank( edgeID );
2275
2276 //Make sure it's not worse than our reject rank
2277 if ( testRank >= rejectRank )
2278 continue;
2279
2280 //Found one
2281 if ( edgeID == endID )
2282 {
2283 if ( !d_altRoutes->integer || !RouteBlocked( startID, edgeID, endID, rejectRank ) )
2284 {
2285 *pathCost += start->GetEdgeCost( i );
2286 return edgeID;
2287 }
2288 else
2289 {//this is blocked, can't consider it
2290 continue;
2291 }
2292 }
2293
2294 //No possible connection
2295 if ( testRank == NODE_NONE )
2296 {
2297 *pathCost = Q3_INFINITE;
2298 return NODE_NONE;
2299 }
2300
2301 //Found a better one
2302 if ( testRank < bestRank )
2303 {
2304 //FIXME: make sure all the edges down from startID through edgeID to endID
2305 // does NOT include a failedEdge...
2306 if ( !d_altRoutes->integer || !RouteBlocked( startID, edgeID, endID, rejectRank ) )
2307 {
2308 bestNode = edgeID;
2309 bestRank = testRank;
2310 bestCost = start->GetEdgeCost(i)+testRank;
2311 }
2312 }
2313 }
2314
2315 *pathCost = bestCost;
2316
2317 return bestNode;
2318 }
2319 /*
2320 -------------------------
2321 GetBestNodeAltRoute
2322 overloaded so you don't have to pass a pathCost int pointer in
2323 -------------------------
2324 */
2325
GetBestNodeAltRoute(int startID,int endID,int rejectID)2326 int CNavigator::GetBestNodeAltRoute( int startID, int endID, int rejectID )
2327 {
2328 int junk;
2329 return GetBestNodeAltRoute( startID, endID, &junk, rejectID );
2330 }
2331 /*
2332 -------------------------
2333 GetBestNode
2334 -------------------------
2335 */
2336
GetBestNode(int startID,int endID,int rejectID)2337 int CNavigator::GetBestNode( int startID, int endID, int rejectID )
2338 {
2339 //Validate the start position
2340 if ( ( startID < 0 ) || ( startID >= (int)m_nodes.size() ) )
2341 return WAYPOINT_NONE;
2342
2343 //Validate the end position
2344 if ( ( endID < 0 ) || ( endID >= (int)m_nodes.size() ) )
2345 return WAYPOINT_NONE;
2346
2347 if ( startID == endID )
2348 return startID;
2349
2350 CNode *start = m_nodes[ startID ];
2351 CNode *end = m_nodes[ endID ];
2352
2353 int bestNode = -1;
2354 int bestRank = Q3_INFINITE;
2355 int testRank, rejectRank = 0;
2356
2357 if ( rejectID != WAYPOINT_NONE )
2358 {
2359 for ( int i = 0; i < start->GetNumEdges(); i++ )
2360 {
2361 if ( start->GetEdge(i) == rejectID )
2362 {
2363 rejectRank = end->GetRank( start->GetEdge(i) );
2364 break;
2365 }
2366 }
2367 }
2368
2369 for ( int i = 0; i < start->GetNumEdges(); i++ )
2370 {
2371 int edgeID = start->GetEdge(i);
2372
2373 //Found one
2374 if ( edgeID == endID )
2375 return edgeID;
2376
2377 testRank = end->GetRank( edgeID );
2378
2379 //Found one
2380 if ( testRank <= rejectRank )
2381 continue;
2382
2383 //No possible connection
2384 if ( testRank == NODE_NONE )
2385 return NODE_NONE;
2386
2387 //Found a better one
2388 if ( testRank < bestRank )
2389 {
2390 bestNode = edgeID;
2391 bestRank = testRank;
2392 }
2393 }
2394
2395 return bestNode;
2396 }
2397
2398 /*
2399 -------------------------
2400 GetNodePosition
2401 -------------------------
2402 */
2403
GetNodePosition(int nodeID,vec3_t out)2404 int CNavigator::GetNodePosition( int nodeID, vec3_t out )
2405 {
2406 //Validate the number
2407 if ( ( nodeID < 0 ) || ( nodeID >= (int)m_nodes.size() ) )
2408 return false;
2409
2410 CNode *node = m_nodes[ nodeID ];
2411
2412 node->GetPosition( out );
2413
2414 return true;
2415 }
2416
2417 /*
2418 -------------------------
2419 GetNodeNumEdges
2420 -------------------------
2421 */
2422
GetNodeNumEdges(int nodeID)2423 int CNavigator::GetNodeNumEdges( int nodeID )
2424 {
2425 if ( ( nodeID < 0 ) || ( nodeID >= (int)m_nodes.size() ) )
2426 return -1;
2427
2428 CNode *node = m_nodes[ nodeID ];
2429
2430 assert( node );
2431
2432 return node->GetNumEdges();
2433 }
2434
2435 /*
2436 -------------------------
2437 GetNodeEdge
2438 -------------------------
2439 */
2440
GetNodeEdge(int nodeID,int edge)2441 int CNavigator::GetNodeEdge( int nodeID, int edge )
2442 {
2443 if ( ( nodeID < 0 ) || ( nodeID >= (int)m_nodes.size() ) )
2444 return -1;
2445
2446 CNode *node = m_nodes[ nodeID ];
2447
2448 assert( node );
2449
2450 return node->GetEdge( edge );
2451 }
2452
2453 /*
2454 -------------------------
2455 Connected
2456 -------------------------
2457 */
2458
Connected(int startID,int endID)2459 bool CNavigator::Connected( int startID, int endID )
2460 {
2461 //Validate the start position
2462 if ( ( startID < 0 ) || ( startID >= (int)m_nodes.size() ) )
2463 return false;
2464
2465 //Validate the end position
2466 if ( ( endID < 0 ) || ( endID >= (int)m_nodes.size() ) )
2467 return false;
2468
2469 if ( startID == endID )
2470 return true;
2471
2472 CNode *start = m_nodes[ startID ];
2473 CNode *end = m_nodes[ endID ];
2474
2475 for ( int i = 0; i < start->GetNumEdges(); i++ )
2476 {
2477 int edgeID = start->GetEdge(i);
2478
2479 //Found one
2480 if ( edgeID == endID )
2481 return true;
2482
2483 if ( ( end->GetRank( edgeID ) ) != NODE_NONE )
2484 return true;
2485 }
2486
2487 return false;
2488 }
2489
2490 /*
2491 -------------------------
2492 GetPathCost
2493 -------------------------
2494 */
2495
GetPathCost(int startID,int endID)2496 unsigned int CNavigator::GetPathCost( int startID, int endID )
2497 {
2498 //Validate the start position
2499 if ( ( startID < 0 ) || ( startID >= (int)m_nodes.size() ) )
2500 return Q3_INFINITE; // return 0;
2501
2502 //Validate the end position
2503 if ( ( endID < 0 ) || ( endID >= (int)m_nodes.size() ) )
2504 return Q3_INFINITE; // return 0;
2505
2506 CNode *startNode = m_nodes[ startID ];
2507
2508 if ( !startNode->GetNumEdges() )
2509 {//WTF? Solitary waypoint! Bad designer!
2510 return Q3_INFINITE; // return 0;
2511 }
2512
2513 CNode *endNode = m_nodes[ endID ];
2514
2515 CNode *moveNode = startNode;
2516
2517 int bestNode;
2518 int pathCost = 0;
2519 int bestCost;
2520
2521 int bestRank;
2522 int testRank;
2523
2524 //Draw out our path
2525 while ( moveNode != endNode )
2526 {
2527 bestRank = WORLD_SIZE;
2528 bestNode = -1;
2529 bestCost = 0;
2530
2531 for ( int i = 0; i < moveNode->GetNumEdges(); i++ )
2532 {
2533 int edgeID = moveNode->GetEdge(i);
2534
2535 //Done
2536 if ( edgeID == endID )
2537 {
2538 return pathCost + moveNode->GetEdgeCost( i );
2539 }
2540
2541 testRank = endNode->GetRank( edgeID );
2542
2543 //No possible connection
2544 if ( testRank == NODE_NONE )
2545 {
2546 return Q3_INFINITE; // return 0;
2547 }
2548
2549 //Found a better one
2550 if ( testRank < bestRank )
2551 {
2552 bestNode = edgeID;
2553 bestRank = testRank;
2554 bestCost = moveNode->GetEdgeCost( i );
2555 }
2556 }
2557
2558 pathCost += bestCost;
2559
2560 //Take a new best node
2561 moveNode = m_nodes[ bestNode ];
2562 }
2563
2564 return pathCost;
2565 }
2566
2567 /*
2568 -------------------------
2569 GetEdgeCost
2570 -------------------------
2571 */
2572
GetEdgeCost(int startID,int endID)2573 unsigned int CNavigator::GetEdgeCost( int startID, int endID )
2574 {
2575 //Validate the start position
2576 if ( ( startID < 0 ) || ( startID >= (int)m_nodes.size() ) )
2577 return Q3_INFINITE; // return 0;
2578
2579 //Validate the end position
2580 if ( ( endID < 0 ) || ( endID >= (int)m_nodes.size() ) )
2581 return Q3_INFINITE; // return 0;
2582
2583 CNode *start = m_nodes[startID];
2584 CNode *end = m_nodes[endID];
2585
2586 return GetEdgeCost( start, end );
2587 }
2588
2589 /*
2590 -------------------------
2591 GetProjectedNode
2592 -------------------------
2593 */
2594
GetProjectedNode(vec3_t origin,int nodeID)2595 int CNavigator::GetProjectedNode( vec3_t origin, int nodeID )
2596 {
2597 //Validate the start position
2598 if ( ( nodeID < 0 ) || ( nodeID >= (int)m_nodes.size() ) )
2599 return NODE_NONE;
2600
2601 CNode *node = m_nodes[nodeID];
2602 CNode *tempNode;
2603
2604 float bestDot = 0.0f;
2605 int bestNode = NODE_NONE;
2606
2607 vec3_t targetDir, basePos, tempDir, tempPos;
2608 float dot;
2609
2610 //Setup our target direction
2611 node->GetPosition( basePos );
2612
2613 VectorSubtract( origin, basePos, targetDir );
2614 VectorNormalize( targetDir );
2615
2616 //Go through all the edges
2617 for ( int i = 0; i < node->GetNumEdges(); i++ )
2618 {
2619 tempNode = m_nodes[node->GetEdge(i)];
2620 tempNode->GetPosition( tempPos );
2621
2622 VectorSubtract( tempPos, basePos, tempDir );
2623 VectorNormalize( tempDir ); //FIXME: Retain the length here if you want it
2624
2625 dot = DotProduct( targetDir, tempDir );
2626
2627 if ( dot < 0.0f )
2628 continue;
2629
2630 if ( dot > bestDot )
2631 {
2632 bestDot = dot;
2633 bestNode = tempNode->GetID();
2634 }
2635 }
2636
2637 return bestNode;
2638 }
2639
2640 // This is the PriorityQueue stuff for lists of connections
2641 // better than linear (1/21/02 BJG)
2642 //////////////////////////////////////////////////////////////////
2643 // Helper pop_mHeap algorithm class
2644 //////////////////////////////////////////////////////////////////
2645 class NodeTotalGreater
2646 {
2647 public:
operator ()(CEdge * first,CEdge * second) const2648 bool operator()( CEdge * first, CEdge * second ) const {
2649 return( first->m_cost > second->m_cost );
2650 }
2651 };
2652
2653
2654 //////////////////////////////////////////////////////////////////
2655 // Destructor - Deallocate any remaining pointers in the queue
2656 //////////////////////////////////////////////////////////////////
~CPriorityQueue()2657 CPriorityQueue::~CPriorityQueue()
2658 {
2659 while (!Empty())
2660 {
2661 delete Pop();
2662 }
2663 }
2664
2665 //////////////////////////////////////////////////////////////////
2666 // Standard Iterative Search
2667 //////////////////////////////////////////////////////////////////
Find(int npNum)2668 CEdge* CPriorityQueue::Find(int npNum)
2669 {
2670 for(std::vector<CEdge*>::iterator HeapIter=mHeap.begin(); HeapIter!=mHeap.end(); HeapIter++)
2671 {
2672 if ((*HeapIter)->m_first == npNum)
2673 {
2674 return *HeapIter;
2675 }
2676 }
2677 return 0;
2678 }
2679
2680 //////////////////////////////////////////////////////////////////
2681 // Remove Node And Resort
2682 //////////////////////////////////////////////////////////////////
Pop()2683 CEdge* CPriorityQueue::Pop()
2684 {
2685 CEdge *edge = mHeap.front();
2686
2687 //pop_mHeap will move the node at the front to the position N
2688 //and then sort the mHeap to make positions 1 through N-1 correct
2689 //(STL makes no assumptions about your data and doesn't want to change
2690 //the size of the container.)
2691 std::pop_heap(mHeap.begin(), mHeap.end(), NodeTotalGreater() );
2692
2693 //pop_back() will actually remove the last element from the mHeap
2694 //now the mHeap is sorted for positions 1 through N
2695 mHeap.pop_back();
2696
2697 return( edge );
2698 }
2699
2700 //////////////////////////////////////////////////////////////////
2701 // Add New Node And Resort
2702 //////////////////////////////////////////////////////////////////
Push(CEdge * theEdge)2703 void CPriorityQueue::Push(CEdge* theEdge )
2704 {
2705 //Pushes the node onto the back of the mHeap
2706 mHeap.push_back( theEdge );
2707
2708 //Sorts the new element into the mHeap
2709 std::push_heap( mHeap.begin(), mHeap.end(), NodeTotalGreater() );
2710 }
2711
2712 //////////////////////////////////////////////////////////////////
2713 // Find The Node In Question And Resort mHeap Around It
2714 //////////////////////////////////////////////////////////////////
Update(CEdge * edge)2715 void CPriorityQueue::Update( CEdge* edge )
2716 {
2717 for(std::vector<CEdge*>::iterator i=mHeap.begin(); i!=mHeap.end(); i++)
2718 {
2719 if( (*i)->m_first == edge->m_first )
2720 { //Found node - resort from this position in the mHeap
2721 //(its total value was changed before this function was called)
2722 std::push_heap( mHeap.begin(), i+1, NodeTotalGreater() );
2723 return;
2724 }
2725 }
2726 }
2727
2728 //////////////////////////////////////////////////////////////////
2729 // Just a wrapper for stl empty function.
2730 //////////////////////////////////////////////////////////////////
Empty()2731 bool CPriorityQueue::Empty()
2732 {
2733 return( mHeap.empty() );
2734 };
2735
2736