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