/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : LevelEdit                                                    *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/wwphys/Pathfind.cpp                          $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 10/15/01 7:44p                                              $*
 *                                                                                             *
 *                    $Revision:: 35                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "pathfind.h"
#include "pathfindportal.h"
#include "path.h"
#include "pscene.h"
#include "boxrobj.h"
#include "decophys.h"
#include "mesh.h"
#include "assetmgr.h"
#include "chunkio.h"
#include "wwphysids.h"
#include "pathdebugplotter.h"
#include "persistfactory.h"
#include "waypath.h"
#include "waypoint.h"
#include "wwmemlog.h"
#include "heightdb.h"
#include "colmathaabox.h"


///////////////////////////////////////////////////////////////////////////
//	Local prototypes
///////////////////////////////////////////////////////////////////////////
int __cdecl fnCompareWaypathPortalsCallback (const void *elem1, const void *elem2);
bool Find_Intersection_Point (const AABoxClass &box, const Vector3 &p0, const Vector3 &p1, float *percent, Vector3 *intersection_point);
void Add_New_Portal_To_List (DynamicVectorClass<PathfindWaypathPortalClass *> &portal_list, PathfindSectorClass *dest_sector1, PathfindSectorClass *dest_sector2, int waypath_id, int waypoint_index, const Vector3 &portal_pos, float percent);


///////////////////////////////////////////////////////////////////////////
//	Constants
///////////////////////////////////////////////////////////////////////////
const int MAX_TEMP_PORTALS				= 20;


///////////////////////////////////////////////////////////////////////////
//	Static data initialization
///////////////////////////////////////////////////////////////////////////
static int _NextTempPortalID		= PathfindClass::TEMP_PORTAL_ID_START;


///////////////////////////////////////////////////////////////////////////
//	Save/Load stuff
///////////////////////////////////////////////////////////////////////////
enum
{
	CHUNKID_DATABASE							= 0x01060635,
	XXX_CHUNKID_SECTOR,
	CHUNKID_PORTAL,
	CHUNKID_SECTOR_CULLING_SYSTEM,
	CHUNKID_SECTOR,
	CHUNKID_SECTOR_CULL_TREE,
	CHUNKID_SECTOR_LINKAGE,
	CHUNKID_SECTOR_OBJECT,
	CHUNKID_HEIGHTDB,
	CHUNKID_ACTION_PORTAL,
	CHUNKID_WAYPATH_PORTAL,
	CHUNKID_PATHFIND_SECTOR_OBJECT
};


///////////////////////////////////////////////////////////////////////////
//	Static member initialization
///////////////////////////////////////////////////////////////////////////
PathfindClass *PathfindClass::_Pathfinder	= NULL;
int PathfindClass::_MemoryFootprint			= 0;


///////////////////////////////////////////////////////////////////////////
//
//	PathfindClass
//
///////////////////////////////////////////////////////////////////////////
PathfindClass::PathfindClass (void)
	:	m_SectorsDisplayed (false),
		m_PortalsDisplayed (false),
		m_Plotter (NULL),
		m_SectorList(1000),		// 1000's of these
		m_PortalList(5000),		// 10.000's of these
		m_SectorDisplayList(1000),	// 1000's of these (debug only)
		m_WaypathList(30)		// 5-30 of these?
{
	WWASSERT (_Pathfinder == NULL);
	_Pathfinder = this;

	// Set good estimates of growth steps
	m_SectorList.Set_Growth_Step(500);
	m_PortalList.Set_Growth_Step(2500);
	m_SectorDisplayList.Set_Growth_Step(500);
	m_WaypathList.Set_Growth_Step(10);

	m_Plotter = new PathDebugPlotterClass;

	//
	//	Initialize the height database
	//
	HeightDBClass::Initialize ();
	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	~PathfindClass
//
///////////////////////////////////////////////////////////////////////////
PathfindClass::~PathfindClass (void)
{		
	Reset_Sectors ();
	Reset_Portals ();
	Reset_Waypaths ();
	_Pathfinder = NULL;

	REF_PTR_RELEASE (m_Plotter);

	//
	//	Free the height database
	//
	HeightDBClass::Shutdown ();
	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Add_Sector
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Add_Sector (PathfindSectorClass *sector, bool add_to_tree)
{
	WWASSERT (sector != NULL);
	if (sector != NULL) {
		sector->Add_Ref ();
		
		//
		//	Add this sector to the culling system (if needs be)
		//
		if (add_to_tree) {
			m_SectorTree.Add_Object (sector);
		}

		//
		//	Add this sector to our linear list (for housekeeping)
		//
		m_SectorList.Add (sector);

		//
		//	For debugging purposes, add this object to our
		// memory footprint counter.
		//
		_MemoryFootprint += sizeof (PathfindSectorClass);
	}

	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Add_Portal
//
///////////////////////////////////////////////////////////////////////////
int
PathfindClass::Add_Portal (PathfindPortalClass *portal)
{
	//
	//	Add this portal to the housekeeping list
	//
	portal->Add_Ref ();
	m_PortalList.Add (portal);

	//
	//	Assign the portal a unique ID
	//
	int portal_id = m_PortalList.Count () - 1;
	portal->Set_ID (portal_id);

	//
	//	Add the size of this portal to the pool.
	//
	_MemoryFootprint += sizeof (PathfindPortalClass);

	return portal_id;
}


///////////////////////////////////////////////////////////////////////////
//
//	Add_Waypath_Portal
//
///////////////////////////////////////////////////////////////////////////
int
PathfindClass::Add_Waypath_Portal (PathfindWaypathPortalClass *portal)
{
	//
	//	Add this portal to the housekeeping list
	//
	portal->Add_Ref ();
	m_WaypathPortalList.Add (portal);
	
	//
	//	Assign the portal a unique ID
	//
	int portal_id = (m_WaypathPortalList.Count () - 1) + WAYPATH_PORTAL_ID_START;
	portal->Set_ID (portal_id);

	//
	//	Add the size of this portal to the pool.
	//
	_MemoryFootprint += sizeof (PathfindWaypathPortalClass);

	return portal_id;
}


///////////////////////////////////////////////////////////////////////////
//
//	Add_Temporary_Portal
//
///////////////////////////////////////////////////////////////////////////
int
PathfindClass::Add_Temporary_Portal
(
	PathfindSectorClass *sector_from,
	PathfindSectorClass *sector_to,
	const Vector3 &		start_pos,
	const Vector3 &		dest_pos
)
{
	int retval = 0;

	if (sector_from != NULL && sector_to != NULL) {
		
		//
		//	Check to see if there is already a portal between the two sectors
		//
		bool found = false;
		for (int index = 0; index < sector_from->Get_Portal_Count (); index ++) {
			PathfindPortalClass *portal = sector_from->Peek_Portal (index);
			PathfindSectorClass *sector = portal->Peek_Dest_Sector (sector_from);

			//
			//	Is this portal's destination sector the one we are looking for?
			//
			if (sector == sector_to) {
				found = true;
				break;
			}
		}

		//
		//	If there isn't already a portal between the sectors, then add one...
		//
		if (found == false) {

			AABoxClass portal_box (start_pos, Vector3 (0.25F, 0.25F, 1.0F));

			PathfindActionPortalClass *new_portal = new PathfindActionPortalClass;
			new_portal->Set_Bounding_Box (portal_box);
			new_portal->Add_Dest_Sector (sector_to);
			new_portal->Set_Entrance_Sector (sector_from);
			new_portal->Set_Action_Type (PathClass::ACTION_LEAP);
			new_portal->Set_Destination (dest_pos);
			new_portal->Set_ID (_NextTempPortalID ++);
				
			//
			//	Add this portal to the housekeeping list
			//
			m_TemporaryPortalList.Add (new_portal);
			sector_from->Add_Portal (new_portal->Get_ID ());

			//
			//	Add the size of this portal to the pool.
			//
			_MemoryFootprint += sizeof (PathfindActionPortalClass);
			retval = new_portal->Get_ID ();

			//
			//	Have we reached the maximum number of temporary portals?
			//
			if (m_TemporaryPortalList.Count () > MAX_TEMP_PORTALS) {
				
				//
				//	Remove the first temp portal in the list
				//
				PathfindActionPortalClass *old_portal = (PathfindActionPortalClass *)m_TemporaryPortalList[0];
				if (old_portal != NULL) {
					
					//
					//	Remove the portal from which ever sectors reference it
					//
					PathfindSectorClass *sector = old_portal->Get_Entrance_Sector ();
					if (sector != NULL) {
						sector->Remove_Portal (old_portal->Get_ID ());
					}

					//
					//	Remove the portal from our list and free our hold on it
					//
					m_TemporaryPortalList.Delete (0);
					old_portal->Release_Ref ();
				}
			}
		}
	}

	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Save
//
///////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Save (ChunkSaveClass &csave)
{
	WWMEMLOG(MEM_PATHFIND);

	csave.Begin_Chunk (CHUNKID_DATABASE);
	
		//
		//	Save the sectors and portals
		//
		bool retval =	Save_Culling_System (csave);
		retval &=		Save_Portals (csave);
		retval &=		Save_Waypaths (csave);

		//
		//	Save the height database for flying vehicles
		//
		csave.Begin_Chunk (CHUNKID_HEIGHTDB);
			HeightDBClass::Save (csave);
		csave.End_Chunk ();

	csave.End_Chunk ();

	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Save_Portals
//
///////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Save_Portals (ChunkSaveClass &csave)
{
	bool retval = true;

	int count = m_PortalList.Count ();
	for (int index = 0; index < count && retval; index ++) {		
		PathfindPortalClass *portal = m_PortalList[index];

		//
		//	Determine what type of chunk to save this portal in
		//
		if (portal->As_PathfindActionPortalClass () != NULL) {
			csave.Begin_Chunk (CHUNKID_ACTION_PORTAL);
		} else {
			csave.Begin_Chunk (CHUNKID_PORTAL);
		}

		//
		//	Write this portal out ot its own chunk
		//
			retval &= portal->Save (csave);
		csave.End_Chunk ();
	}

	//
	//	Now save the waypath portals
	//
	count = m_WaypathPortalList.Count ();
	for (index = 0; index < count && retval; index ++) {		
		PathfindPortalClass *portal = m_WaypathPortalList[index];

		//
		//	Write this portal out ot its own chunk
		//
		csave.Begin_Chunk (CHUNKID_WAYPATH_PORTAL);
			retval &= portal->Save (csave);
		csave.End_Chunk ();
	}

	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Load
//
///////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Load (ChunkLoadClass &cload)
{
	WWMEMLOG(MEM_PATHFIND);

	Reset_Sectors ();
	Reset_Portals ();
	Reset_Waypaths ();

	bool retval = true;

	//
	//	Make sure we are reading data from the right file...
	//
	if (	cload.Open_Chunk () != true ||
			cload.Cur_Chunk_ID () != CHUNKID_DATABASE)
	{
		retval = false;
	}

	//
	//	Read all the chunks...
	//
	while (retval && cload.Open_Chunk ()) {		
		switch (cload.Cur_Chunk_ID ()) {
			
			case CHUNKID_HEIGHTDB:
				retval &= HeightDBClass::Load (cload);
				break;

			case CHUNKID_SECTOR_CULLING_SYSTEM:
				retval &= Load_Culling_System (cload);
				break;

			case CHUNKID_PORTAL:
			{
				PathfindPortalClass *portal = new PathfindPortalClass;
				retval &= Load_Portal (cload, portal);
			}
			break;

			case CHUNKID_ACTION_PORTAL:
			{
				PathfindActionPortalClass *portal = new PathfindActionPortalClass;
				retval &= Load_Portal (cload, portal);
			}
			break;

			case CHUNKID_WAYPATH_PORTAL:
			{
				PathfindWaypathPortalClass *portal = new PathfindWaypathPortalClass;
				retval &= Load_Portal (cload, portal);
			}
			break;
			
			default:
			{
				//
				//	Load the object from the chunk (if possible)
				//
				PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
				if (factory != NULL) {
					PersistClass *object = factory->Load (cload);
					
					//
					//	Were we successful?
					//
					if (object != NULL) {
						
						//
						//	If this is a waypath, then register it with the system
						//
						if (cload.Cur_Chunk_ID () == PHYSICS_CHUNKID_WAYPATH) {
							Add_Waypath ((WaypathClass *)object);
							((WaypathClass *)object)->Release_Ref ();
						}

					} else {
						WWDEBUG_SAY (("Unknown chunk ID 0x%X\r\n", cload.Cur_Chunk_ID ()));
						retval = false;
					}
				}
			}
			break;
		}

		cload.Close_Chunk ();
	}

	cload.Close_Chunk ();
	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Save_Sector
//
///////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Save_Sector
(
	ChunkSaveClass &			csave,
	PathfindSectorClass *	sector
)
{
	bool retval = true;

	csave.Begin_Chunk (CHUNKID_SECTOR);
		
		//
		//	Write this sector out to its own chunk
		//
		bool save_linkage = true;
		if (sector->As_PathfindWaypathSectorClass () != NULL) {
			csave.Begin_Chunk (CHUNKID_PATHFIND_SECTOR_OBJECT);
			save_linkage = false;
		} else {
			csave.Begin_Chunk (CHUNKID_SECTOR_OBJECT);
		}
		
			retval &= sector->Save (csave);
		csave.End_Chunk ();

		//
		//	Now write the sector's AABTreeCullSystem linkage to a chunk
		//
		if (save_linkage) {
			csave.Begin_Chunk (CHUNKID_SECTOR_LINKAGE);
				m_SectorTree.Save_Object_Linkage (csave, sector);
			csave.End_Chunk ();
		}

	csave.End_Chunk ();

	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Load_Sector
//
///////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Load_Sector (ChunkLoadClass &cload)
{
	bool retval							= true;
	PathfindSectorClass *sector	= NULL;

	//
	//	Read all the chunks...
	//
	while (cload.Open_Chunk ()) {		
		switch (cload.Cur_Chunk_ID ()) {
			
			case CHUNKID_SECTOR_OBJECT:
				sector = new PathfindSectorClass;
				retval &= sector->Load (cload);
				break;

			case CHUNKID_PATHFIND_SECTOR_OBJECT:
				sector = new PathfindWaypathSectorClass;
				retval &= sector->Load (cload);

				//
				//	Add this sector to our housekeeping list
				//
				Add_Sector (sector, false);
				break;

			case CHUNKID_SECTOR_LINKAGE:
				WWASSERT (sector != NULL);
				if (sector != NULL) {
					
					//
					//	Read the linkage information from the chunk
					//					
					m_SectorTree.Load_Object_Linkage (cload, sector);

					//
					//	Add this sector to our housekeeping list
					//
					Add_Sector (sector, false);

				}				
				break;

			default:
				WWDEBUG_SAY (("Unknown chunk ID 0x%X\r\n", cload.Cur_Chunk_ID ()));
				retval = false;
				break;
		}

		cload.Close_Chunk ();
	}

	REF_PTR_RELEASE (sector);
	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Load_Portal
//
///////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Load_Portal (ChunkLoadClass &cload, PathfindPortalClass *portal)
{
	//
	//	Load the portal's data from its chunk
	// and add it to our global portal list.
	//
	bool retval	= portal->Load (cload);
	if (retval) {

		//
		//	Now add this portal to its list
		//
		if (portal->As_PathfindWaypathPortalClass () == NULL) {
			m_PortalList.Add (portal);
		} else {
			m_WaypathPortalList.Add (portal->As_PathfindWaypathPortalClass ());
		}

		//
		//	Add the size of this portal to the pool.
		//
		_MemoryFootprint += sizeof (PathfindPortalClass);
	}

	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Get_Sector_Index
//
///////////////////////////////////////////////////////////////////////////
int
PathfindClass::Get_Sector_Index (PathfindSectorClass *sector)
{
	int index = m_SectorList.Count ();

	bool keep_going = true;
	while (keep_going && index --) {
		keep_going = (m_SectorList[index] != sector);
	}

	return index;
}


static inline bool Clip_Point (Vector3 *point, const AABoxClass &box)
{
	Vector3 temp_point = *point;

	//
	//	Clip the temporary point
	//
	temp_point.X = max (temp_point.X, box.Center.X - box.Extent.X);
	temp_point.Y = max (temp_point.Y, box.Center.Y - box.Extent.Y);
	temp_point.Z = max (temp_point.Z, box.Center.Z - box.Extent.Z);
	temp_point.X = min (temp_point.X, box.Center.X + box.Extent.X);
	temp_point.Y = min (temp_point.Y, box.Center.Y + box.Extent.Y);
	temp_point.Z = min (temp_point.Z, box.Center.Z + box.Extent.Z);

	//
	//	Did the clip change the point?
	//
	bool retval = (point->X != temp_point.X);
	retval		|= (point->Y != temp_point.Y);
	retval		|= (point->Z != temp_point.Z);
	
	//
	//	Pass the new point back to the caller
	//
	(*point) = temp_point;
	
	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Collect_Sectors
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Collect_Sectors
(
	DynamicVectorClass<PathfindSectorClass *> &	list,
	const AABoxClass &									box,
	PathfindSectorClass *								exclude_sector
)
{
	//
	//	Collect all the sectors this box intersects
	//
	m_SectorTree.Reset_Collection ();
	m_SectorTree.Collect_Objects (box);

	//
	//	Return the list of sectors to the caller
	//
	PathfindSectorClass *sector = NULL;
	for (	sector = m_SectorTree.Get_First_Collected_Object ();
			sector != NULL;
			sector = m_SectorTree.Get_Next_Collected_Object (sector))
	{		
		if (sector != exclude_sector) {

			//
			//	Ignore sectors that are 0 size
			//
			const AABoxClass &box = sector->Get_Bounding_Box ();
			if (box.Extent.X != 0 && box.Extent.Y != 0 && box.Extent.Z != 0) {
				list.Add (sector);
			}
		}
	}	
		
	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Find_Sector
//
///////////////////////////////////////////////////////////////////////////
PathfindSectorClass *
PathfindClass::Find_Sector
(
	const Vector3 &		position,
	float						sector_fudge,
	PathfindSectorClass *exclude_sector
)
{
	//
	//	Collect all the sectors this position intersects (hopefully
	// will be only one).
	//
	SphereClass sphere (position, sector_fudge);

	m_SectorTree.Reset_Collection ();
	m_SectorTree.Collect_Objects (sphere);

	float closest = 99999.9F;

	//
	//	Find an acceptable sector to return
	//
	PathfindSectorClass *closest_sector = NULL;
	PathfindSectorClass *sector = NULL;
	for (	sector = m_SectorTree.Get_First_Collected_Object ();
			sector != NULL;
			sector = m_SectorTree.Get_Next_Collected_Object (sector))
	{				
		if (sector != exclude_sector) {

			//
			//	Ignore sectors that are small
			//
			const AABoxClass &box = sector->Get_Bounding_Box ();
			if (box.Extent.X > 0.325F && box.Extent.Y > 0.325F && box.Extent.Z > WWMATH_EPSILON) {

				//
				//	Clip the player's position to this sector
				//
				Vector3 clipped_pos = position;
				::Clip_Point (&clipped_pos, box);

				//
				//	Check to see if this clipped position is the closest yet
				//
				float dist = (clipped_pos - position).Length ();
				if (dist < closest) {
					closest			= dist;
					closest_sector	= sector;				
				}
			}
		}
	}	
		
	//
	//	Return the first (and hopefully only) sector
	//
	return closest_sector;
}


///////////////////////////////////////////////////////////////////////////
//
//	Reset_Sectors
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Reset_Sectors (void)
{
	Display_Sectors (false);
	Display_Portals (false);

	//
	//	Release our hold on each of the sectors
	//
	for (int index = 0; index < m_SectorList.Count (); index ++) {
		PathfindSectorClass *sector = m_SectorList[index];
		
		//
		//	Remove this sector from the culling system (if necessary)
		//
		if (sector->As_PathfindWaypathSectorClass () == NULL) {
			m_SectorTree.Remove_Object (sector);
		}

		REF_PTR_RELEASE (sector);
	}

	m_SectorList.Delete_All ();

	_MemoryFootprint = 0;
	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Reset_Portals
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Reset_Portals (void)
{
	Display_Portals (false);

	//
	//	Release our hold on each of the portals
	//
	for (int index = 0; index < m_PortalList.Count (); index ++) {
		PathfindPortalClass *portal = m_PortalList[index];
		REF_PTR_RELEASE (portal);
	}

	//
	//	Release our hold on each of the temporary portals
	//
	for (index = 0; index < m_TemporaryPortalList.Count (); index ++) {
		PathfindPortalClass *portal = m_TemporaryPortalList[index];
		REF_PTR_RELEASE (portal);
	}

	//
	//	Release our hold on each of the waypath portals
	//
	for (index = 0; index < m_WaypathPortalList.Count (); index ++) {
		PathfindPortalClass *portal = m_WaypathPortalList[index];
		REF_PTR_RELEASE (portal);
	}

	//
	//	Reset the lists
	//
	m_PortalList.Delete_All ();
	m_TemporaryPortalList.Delete_All ();
	m_WaypathPortalList.Delete_All ();

	//
	//	Reset the starting IDs
	//
	_NextTempPortalID = TEMP_PORTAL_ID_START;
	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Display_Sectors
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Display_Sectors (bool onoff)
{
	//
	//	Start fresh
	//
	m_SectorDisplayWidgets.Reset_Debug_Widget_List ();

	if (onoff) {
		
		//
		//	Add boxes to our debug widget system to represent the sectors
		//
		for (int index = 0; index < m_SectorList.Count (); index ++) {
			PathfindSectorClass *sector		= m_SectorList[index];
			const AABoxClass &bounding_box	= sector->Get_Bounding_Box ();

			//
			//	Add a randomly colored box to the widget system
			//
			float red	= 0;
			float green = 0.5F + float(rand () % 128) / 256.0F;
			float blue	= 0.5F + float(rand () % 128) / 256.0F;
			m_SectorDisplayWidgets.Add_Debug_AABox (bounding_box, Vector3 (red, green, blue));
		}
	}

	m_SectorsDisplayed = onoff;
	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Display_Portals
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Display_Portals (bool onoff)
{
	//
	//	Start fresh
	//
	m_PortalDisplayWidgets.Reset_Debug_Widget_List ();

		//
		//	Add boxes to our debug widget system to represent the sectors
		//
	if (onoff) {

		for (int index = 0; index < m_PortalList.Count (); index ++) {
			PathfindPortalClass *portal = m_PortalList[index];

			AABoxClass bounding_box;
			portal->Get_Bounding_Box (bounding_box);

			//
			//	Choose a color for this box
			//
			Vector3 color (1, 1, 1);
			if (portal->Is_Two_Way_Portal () == false) {
				color.Set  (1, 0, 0);
			}

			//
			//	Add a box to the system
			//
			m_PortalDisplayWidgets.Add_Debug_AABox (bounding_box, color);
		}		
	}

	m_PortalsDisplayed = onoff;
	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Add_Waypath
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Add_Waypath (WaypathClass *waypath)
{
	if (waypath != NULL) {
		waypath->Add_Ref ();
		m_WaypathList.Add (waypath);
	}

	return ;
}


///////////////////////////////////////////////////////////////////////////
//
//	Remove_Waypath
//
///////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Remove_Waypath (WaypathClass *waypath)
{
	bool retval = false;

	if (waypath != NULL) {
		
		int index = m_WaypathList.Count ();
		while (index -- && (retval == false)) {
			WaypathClass *curr_waypath = m_WaypathList[index];
			
			//
			//	If this is the waypath we are looking for
			// then remove it from the list and release our
			// hold on it.
			//
			if (curr_waypath == waypath) {
				REF_PTR_RELEASE (waypath);
				m_WaypathList.Delete (index);
				retval = true;
			}
		}		
	}

	return retval;
}


///////////////////////////////////////////////////////////////////////////
//
//	Find_Waypath
//
///////////////////////////////////////////////////////////////////////////
WaypathClass *
PathfindClass::Find_Waypath (int id) const
{
	WaypathClass *waypath = NULL;

	//
	//	Loop over all the paths in our list until we've
	// found the one are looking for.
	//
	int index = m_WaypathList.Count ();
	while (index -- && (waypath == NULL)) {
		WaypathClass *curr_waypath = m_WaypathList[index];
		
		//
		//	Is this the waypath we are looking for?
		//
		if (curr_waypath->Get_ID () == id) {
			waypath = curr_waypath;
		}
	}		

	return waypath;
}

///////////////////////////////////////////////////////////////////////////
//
//	Count_Waypaths_Starting_In_Box 
//
///////////////////////////////////////////////////////////////////////////
int PathfindClass::Count_Waypaths_Starting_In_Box (const AABoxClass & box)
{
	//
	//	Loop over all the paths in our list counting the ones whose 
	// start points are contained inside the given box.
	//
	int count = 0;
	for (int index=0; index<m_WaypathList.Count(); index++) {
		WaypathClass *curr_waypath = m_WaypathList[index];
		if (CollisionMath::Overlap_Test(box,curr_waypath->Get_Point(0)->Get_Position()) == CollisionMath::INSIDE) {
			count++;
		}
	}
	return count;
}

///////////////////////////////////////////////////////////////////////////
//
//	Get_Waypath_Starting_In_Box 
//
///////////////////////////////////////////////////////////////////////////
WaypathClass * PathfindClass::Get_Waypath_Starting_In_Box (const AABoxClass & box,int i)
{
	//
	//	Loop over all the paths in our list counting down until
	// we get to the i'th one that starts in the given box.
	//
	WaypathClass * path = NULL;
	int count = i;
	for (int index=0; index<m_WaypathList.Count(); index++) {
		WaypathClass *curr_waypath = m_WaypathList[index];
		
		if (CollisionMath::Overlap_Test(box,curr_waypath->Get_Point(0)->Get_Position()) == CollisionMath::INSIDE) {
			if (count == 0) {
				path = curr_waypath;
				break;
			} else {
				count--;
			}
		}
	}
	
	if (path != NULL) {
		path->Add_Ref();
	}
	return path;
}

///////////////////////////////////////////////////////////////////////////
//
//	Reset_Waypaths
//
///////////////////////////////////////////////////////////////////////////
void
PathfindClass::Reset_Waypaths (void)
{
	//
	//	Release our hold on each of the waypaths
	//
	int index = m_WaypathList.Count ();
	while (index --) {
		WaypathClass *waypath = m_WaypathList[index];
		REF_PTR_RELEASE (waypath);	
	}		

	//
	//	Remove all the waypaths from our list
	//
	m_WaypathList.Delete_All ();
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Save_Waypaths
//
//////////////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Save_Waypaths (ChunkSaveClass &csave)
{
	//
	//	Loop over all the waypaths
	//
	int index = m_WaypathList.Count ();
	while (index --) {
		WaypathClass *waypath = m_WaypathList[index];

		//
		//	Save this waypath object
		//
		csave.Begin_Chunk (waypath->Get_Factory ().Chunk_ID ());
			waypath->Get_Factory ().Save (csave, waypath);
		csave.End_Chunk ();

		//
		//	Now save all the waypoints that this waypath contains
		//
		int waypoint_index = waypath->Get_Point_Count ();
		while (waypoint_index --) {
			WaypointClass *waypoint = waypath->Get_Point (waypoint_index);

			//
			//	Save this waypoint object
			//
			csave.Begin_Chunk (waypoint->Get_Factory ().Chunk_ID ());
				waypoint->Get_Factory ().Save (csave, waypoint);
			csave.End_Chunk ();
		}
	}		

	return true;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Save_Culling_System
//
//////////////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Save_Culling_System (ChunkSaveClass &csave)
{
	bool retval = true;

	csave.Begin_Chunk (CHUNKID_SECTOR_CULLING_SYSTEM);

		//
		//	Write the AABTreeCullSystem we use for sector lookups
		// to the chunk
		//
		csave.Begin_Chunk (CHUNKID_SECTOR_CULL_TREE);
			m_SectorTree.Save (csave);
		csave.End_Chunk ();

		//
		//	Now write the contents of the tree (the sector list)
		// to the chunk
		//
		int count = m_SectorList.Count ();
		for (int index = 0; index < count && retval; index ++) {
			Save_Sector (csave, m_SectorList[index]);
		}

	csave.End_Chunk ();

	return retval;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Load_Culling_System
//
//////////////////////////////////////////////////////////////////////////////////
bool
PathfindClass::Load_Culling_System (ChunkLoadClass &cload)
{
	bool retval = true;

	//
	//	Read all the chunks...
	//
	while (cload.Open_Chunk ()) {		
		switch (cload.Cur_Chunk_ID ()) {
			
			case CHUNKID_SECTOR_CULL_TREE:
				m_SectorTree.Load (cload);
				break;

			case CHUNKID_SECTOR:
				retval &= Load_Sector (cload);
				break;

			default:
				WWDEBUG_SAY (("Unknown chunk ID 0x%X\r\n", cload.Cur_Chunk_ID ()));
				retval = false;
				break;
		}

		cload.Close_Chunk ();
	}

	return retval;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Re_Partition_Sector_Tree
//
//////////////////////////////////////////////////////////////////////////////////
void
PathfindClass::Re_Partition_Sector_Tree (void)
{
	m_SectorTree.Re_Partition ();
	return ;
}


/////////////////////////////////////////////////////////////////////////
//
// Peek_Portal
//
/////////////////////////////////////////////////////////////////////////
PathfindPortalClass *
PathfindClass::Peek_Portal (int portal_index)
{
	PathfindPortalClass *portal = NULL;
	
	if (portal_index >= TEMP_PORTAL_ID_START) {

		//
		//	Try to find the portal in our temporary portal list
		//
		for (int index = 0; index < m_TemporaryPortalList.Count (); index ++) {
			
			//
			//	Is this the portal we are looking for?
			//
			if (portal_index == (int)m_TemporaryPortalList[index]->Get_ID ()) {
				portal = m_TemporaryPortalList[index];
				break;
			}
		}

	} else if (portal_index >= WAYPATH_PORTAL_ID_START) {				
		
		//
		//	Look for the portal in our waypath portal list
		//
		portal = m_WaypathPortalList[portal_index - WAYPATH_PORTAL_ID_START];

	} else if (portal_index >= 0) {
		portal = m_PortalList[portal_index];
	}

	return portal;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Get_Height_Value
//
//////////////////////////////////////////////////////////////////////////////////
float
PathfindClass::Get_Height_Value (const Vector3 &pos)
{	
	return HeightDBClass::Get_Height (pos);
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Find_Random_Spot
//
//////////////////////////////////////////////////////////////////////////////////
static DynamicVectorClass<AABoxClass> box_list;

bool
PathfindClass::Find_Random_Spot
(
	const Vector3 &center,
	float				max_dist,
	Vector3 *		dest
)
{	
	bool retval = false;

	//
	//	Lookup the starting sector we'll search from
	//
	PathfindSectorClass *start_sector = Find_Sector (center, 0.1F, NULL);
	if (start_sector != NULL) {
		box_list.Reset_Active();

		//
		//	Start a list of valid 'regions' with the bounding box of the
		// current sector
		//
		box_list.Add (start_sector->Get_Bounding_Box ());

		//
		//	Square the distances for quick compares
		//
		float max_dist2 = max_dist * max_dist;

		//
		//	Loop over all connecting sectors and add any of them that meet
		// the distance requirements to our list
		//
		for (int index = 0; index < start_sector->Get_Portal_Count (); index ++) {
			PathfindPortalClass *portal		= start_sector->Peek_Portal (index);		
			PathfindSectorClass *dest_sector	= portal->Peek_Dest_Sector (start_sector);
			if (dest_sector != NULL) {
				const AABoxClass &bounding_box = dest_sector->Get_Bounding_Box ();
				
				//
				//	Find a "close" point to the box from the starting position
				//
				Vector3 closest_pt = center;
				Clip_Point (&closest_pt, bounding_box);
				
				//
				//	Is this box within the maximum acceptable distance?
				//
				float dist_to_box2 = (center - closest_pt).Length2 ();
				if (dist_to_box2 < max_dist2) {
					box_list.Add (bounding_box);
				}
			}
		}

		//
		//	Pick a random box from the list
		//
		int count					= box_list.Count ();
		int box_index				= (rand () % count);
		const AABoxClass &box	= box_list[box_index];

		//
		//	Find a "close" point to the box from the starting position
		//
		Vector3 closest_pt = center;
		Clip_Point (&closest_pt, box);

		//
		//	Now add a random vector to this point that will stay
		// inside the requirements
		//
		float curr_dist				= (closest_pt - center).Length ();
		float max_allowable_dist	= (max_dist - curr_dist);

		(*dest) = closest_pt;
		dest->X += WWMath::Random_Float (-max_allowable_dist, max_allowable_dist);
		dest->Y += WWMath::Random_Float (-max_allowable_dist, max_allowable_dist);

		//
		//	Now, simply make sure the point stays inside the pathfind sector
		//
		Clip_Point (dest, box);
		retval = true;

	} else if (m_SectorList.Count () == 0) {

		//
		//	If we don't have any pathfind data, then simply choose a random point
		//
		(*dest) = center;
		dest->X += WWMath::Random_Float (-max_dist, max_dist);
		dest->Y += WWMath::Random_Float (-max_dist, max_dist);
		retval = true;
	}

	return retval;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Free_Waypath_Sectors_And_Portals
//
//////////////////////////////////////////////////////////////////////////////////
void
PathfindClass::Free_Waypath_Sectors_And_Portals (void)
{
	//
	//	Release our hold on each of the waypath portals
	//
	for (int index = 0; index < m_WaypathPortalList.Count (); index ++) {
		PathfindPortalClass *portal = m_WaypathPortalList[index];

		//
		//	Remove this portal from the sectors that reference it
		//
		uint16 dest_sector1_id = portal->Get_Dest_Sector1 ();
		uint16 dest_sector2_id = portal->Get_Dest_Sector2 ();
		PathfindSectorClass *dest_sector1 = Peek_Sector (dest_sector1_id);
		PathfindSectorClass *dest_sector2 = Peek_Sector (dest_sector2_id);

		if (dest_sector1 != NULL) {
			dest_sector1->Remove_Portal (portal->Get_ID ());
		}

		if (dest_sector2 != NULL) {
			dest_sector2->Remove_Portal (portal->Get_ID ());
		}

		REF_PTR_RELEASE (portal);
	}

	//
	//	Loop over all the sectors in our list
	//
	for (index = 0; index < m_SectorList.Count (); index ++) {
		PathfindSectorClass *sector = m_SectorList[index];
		
		//
		//	Is this sector a waypath sector?
		//
		if (sector->As_PathfindWaypathSectorClass () != NULL) {

			//
			//	Free the sector and remove it from the list
			//
			REF_PTR_RELEASE (sector);
			m_SectorList.Delete (index);
			index --;
		}
	}

	//
	//	Reset the portal list
	//
	m_WaypathPortalList.Delete_All ();	
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Generate_Waypath_Sectors_And_Portals
//
//////////////////////////////////////////////////////////////////////////////////
void
PathfindClass::Generate_Waypath_Sectors_And_Portals (void)
{
	//
	//	Start fresh
	//
	Free_Waypath_Sectors_And_Portals ();

	//
	//	Loop over all the waypaths in the system
	//
	for (int index = 0; index < m_WaypathList.Count (); index ++) {		
		WaypathClass *waypath = m_WaypathList[index];

		//
		//	Only process waypaths that can be used during a path solve
		//
		if (waypath->Get_Flag (WaypathClass::FLAG_INNATE_PATHFIND)) {
			Generate_Waypath_Sector_And_Portals (waypath);
		}
	}

	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Generate_Waypath_Sector_And_Portals
//
//////////////////////////////////////////////////////////////////////////////////
void
PathfindClass::Generate_Waypath_Sector_And_Portals (WaypathClass *waypath)
{
	DynamicVectorClass<PathfindWaypathPortalClass *> portal_list;

	//
	//	Get information about this waypath
	//		
	int waypoint_count	= waypath->Get_Point_Count ();
	int waypath_id			= waypath->Get_ID ();

	//
	//	Create a new "waypath sector" that will hold all the enter and
	// exit portals for us
	//
	PathfindWaypathSectorClass *new_sector = new PathfindWaypathSectorClass;
	new_sector->Set_Waypath_ID (waypath_id);
	new_sector->Set_Bounding_Box (AABoxClass (Vector3 (0, 0, 0), Vector3 (0, 0, 0)));
	
	//
	//	Register this new sector with the pathfind system
	//
	Add_Sector (new_sector, false);		
	
	//
	//	Add a portal around each waypoint in the path
	//
	for (int index = 0; index < waypoint_count; index ++) {
		WaypointClass *waypoint			= waypath->Get_Point (index);
		const Vector3 &waypoint_pos	= waypoint->Get_Position ();

		//
		//	Check to see if there is a pathfind sector at the waypoint position...
		// if there is, then we should create a new portal at this position.
		//
		PathfindSectorClass *sector_at_pos = Find_Sector (waypoint_pos, 0.125F);
		if (sector_at_pos != NULL) {

			//
			//	Add a new portal to the list at this position
			//
			::Add_New_Portal_To_List (portal_list, new_sector, sector_at_pos, waypath_id,
					index, waypoint_pos, 0);
		}
	}

	//
	//	Now add a portal everywhere the waypath intersects a pathfind sector
	//
	Add_Intersection_Portals_To_List (portal_list, waypath, new_sector);

	if (portal_list.Count () > 0) {
		
		//
		//	Now sort the portals based on occurance within the path
		//
		::qsort (&portal_list[0], portal_list.Count (),
			sizeof (PathfindWaypathPortalClass *), fnCompareWaypathPortalsCallback);

		//
		//	Register the portals with the system and add them to their sectors
		//
		for (int portal_index = 0; portal_index < portal_list.Count (); portal_index ++) {
			PathfindWaypathPortalClass *curr_portal = portal_list[portal_index];
			
			//
			//	Register the portal
			//
			int portal_id = Add_Waypath_Portal (curr_portal);

			//
			//	Lookup the sectors this portal will connect
			//
			uint16 dest_sector1_id = curr_portal->Get_Dest_Sector1 ();
			uint16 dest_sector2_id = curr_portal->Get_Dest_Sector2 ();
			PathfindSectorClass *dest_sector1 = Peek_Sector (dest_sector1_id);
			PathfindSectorClass *dest_sector2 = Peek_Sector (dest_sector2_id);

			//
			//	Add this portal to the sectors
			//
			if (dest_sector1 != NULL) {
				dest_sector1->Add_Portal (portal_id);
			}
		
			if (dest_sector2 != NULL) {
				dest_sector2->Add_Portal (portal_id);
			}

			//
			//	Release our hold on this portal
			//
			REF_PTR_RELEASE (curr_portal);
		}
	}
	
	//
	//	Now release our hold on the sector we just created
	//
	REF_PTR_RELEASE (new_sector);
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Add_Intersection_Portals_To_List
//
//////////////////////////////////////////////////////////////////////////////////
void
PathfindClass::Add_Intersection_Portals_To_List
(
	DynamicVectorClass<PathfindWaypathPortalClass *> &	portal_list,
	WaypathClass *													waypath,
	PathfindWaypathSectorClass *								dest_sector
)
{
	int waypoint_count	= waypath->Get_Point_Count ();
	int waypath_id			= waypath->Get_ID ();

	//
	//	For this waypath, find all the pathfind sectors it intersects
	//
	for (int waypoint_index = 0; waypoint_index < waypoint_count - 1; waypoint_index ++) {
		WaypointClass *waypoint1 = waypath->Get_Point (waypoint_index);
		WaypointClass *waypoint2 = waypath->Get_Point (waypoint_index + 1);

		const Vector3 &pos1 = waypoint1->Get_Position ();
		const Vector3 &pos2 = waypoint2->Get_Position ();

		//
		//	Build a bounding box around the two points
		//
		Vector3 extent = (pos2 - pos1) * 0.5F;
		Vector3 center = pos1 + extent;
		extent.X = max (WWMath::Fabs (extent.X), 1.0F);
		extent.Y = max (WWMath::Fabs (extent.Y), 1.0F);
		extent.Z = max (WWMath::Fabs (extent.Z), 1.0F);
		AABoxClass bounding_box (center, extent);
		
		//
		//	Find all the pathfind sectors in this bounding box
		//
		m_SectorTree.Reset_Collection ();
		m_SectorTree.Collect_Objects (bounding_box);

		//
		//	Loop over all the sectors that this line segment could possibly intersect
		//			
		PathfindSectorClass *sector = NULL;			
		for (	sector = m_SectorTree.Peek_First_Collected_Object ();
				sector != NULL;
				sector = m_SectorTree.Peek_Next_Collected_Object (sector))
		{
			const AABoxClass &sector_box = sector->Get_Bounding_Box ();

			//
			//	Find where this line segment intersects the sector (if at all)
			//
			float percent = 0;
			Vector3 portal_pos;
			if (::Find_Intersection_Point (sector_box, pos1, pos2, &percent, &portal_pos)) {

				//
				//	Add a new portal to the list at this position
				//
				::Add_New_Portal_To_List (portal_list, dest_sector, sector, waypath_id,
						waypoint_index, portal_pos, percent);
			}
		}
	}					

	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Add_New_Portal_To_List
//
//////////////////////////////////////////////////////////////////////////////////
void
Add_New_Portal_To_List
(
	DynamicVectorClass<PathfindWaypathPortalClass *> &	portal_list,	
	PathfindSectorClass *										dest_sector1,
	PathfindSectorClass *										dest_sector2,
	int																waypath_id,
	int																waypoint_index,
	const Vector3 &												portal_pos,
	float																percent
)
{
	const Vector3 portal_extent (0.5F, 0.5F, 1.0F);

	//
	//	Create a position along this waypath for the waypoint
	//
	WaypathPositionClass waypath_pos;
	waypath_pos.Set_Waypath_ID (waypath_id);
	waypath_pos.Set_Waypoint_Index (waypoint_index);
	waypath_pos.Set_Percent (percent);

	//
	//	Allcoate and configure a new portal
	//
	PathfindWaypathPortalClass *new_portal = new PathfindWaypathPortalClass;
	new_portal->Set_Waypath_Pos (waypath_pos);				
	new_portal->Set_Bounding_Box (AABoxClass (portal_pos, portal_extent));
	
	if (dest_sector1 != NULL) {
		new_portal->Add_Dest_Sector (dest_sector1);
	}

	if (dest_sector2 != NULL) {
		new_portal->Add_Dest_Sector (dest_sector2);
	}

	//
	//	Add this portal to the list
	//
	portal_list.Add (new_portal);
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Find_Intersection_Point
//
//////////////////////////////////////////////////////////////////////////////////
bool
Find_Intersection_Point
(
	const AABoxClass &	box,
	const Vector3 &		p0,
	const Vector3 &		p1,
	float *					percent,
	Vector3 *				intersection_point
)
{
	bool retval = false;

	//
	//	Find the locations of each of the 6 "planes"
	// we will be testing against
	//
	float x1 = box.Center.X - box.Extent.X;
	float x2 = box.Center.X + box.Extent.X;
	float y1 = box.Center.Y - box.Extent.Y;
	float y2 = box.Center.Y + box.Extent.Y;
	float z1 = box.Center.Z - box.Extent.Z;
	float z2 = box.Center.Z + box.Extent.Z;

	float t_values[6] = { -1, -1, -1, -1, -1, -1 };

	Vector3 delta = p1 - p0;

	//
	//	Find the "t" values where the line intersects the
	// 2 "Y-Z" planes
	//
	if (delta.X != 0) {
		t_values[0] = (x1 - p0.X) / delta.X;
		t_values[1] = (x2 - p0.X) / delta.X;
	}

	//
	//	Find the "t" values where the line intersects the
	// 2 "X-Z" planes
	//
	if (delta.Y != 0) {
		t_values[2] = (y1 - p0.Y) / delta.Y;
		t_values[3] = (y2 - p0.Y) / delta.Y;
	}

	//
	//	Find the "t" values where the line intersects the
	// 2 "X-Y" planes
	//
	if (delta.Z != 0) {
		t_values[4] = (z1 - p0.Z) / delta.Z;
		t_values[5] = (z2 - p0.Z) / delta.Z;
	}

	//
	//	Loop over all the "t" values we've calculated
	//
	(*percent) = 2.0F;
	for (int index = 0; index < 6; index ++) {		

		//
		// Is this "t" value the smallest in-range value we've
		// found yet?
		//
		if (	t_values[index] >= 0 &&
				t_values[index] <= 1.0F &&
				t_values[index] < (*percent))
		{
			//
			//	Find the point which exists at this "t" value along the line segment
			//
			Vector3 point = p0 + delta * t_values[index];

			//
			//	If this point isn't outside the box, then we'll consider it good enough
			//
			if (	(WWMath::Fabs (point.X - box.Center.X) <= (box.Extent.X + WWMATH_EPSILON)) &&
					(WWMath::Fabs (point.Y - box.Center.Y) <= (box.Extent.Y + WWMATH_EPSILON)) &&
					(WWMath::Fabs (point.Z - box.Center.Z) <= (box.Extent.Z + WWMATH_EPSILON)))
			{
				(*percent)					= t_values[index];
				(*intersection_point)	= point;
				retval						= true;
			}
		}
	}

	return retval;
}


////////////////////////////////////////////////////////////////
//
//	fnCompareWaypathPortalsCallback
//
////////////////////////////////////////////////////////////////
int __cdecl
fnCompareWaypathPortalsCallback
(
	const void *elem1,
	const void *elem2
)
{
   WWASSERT (elem1 != NULL);
   WWASSERT (elem2 != NULL);
   PathfindWaypathPortalClass *portal1 = *((PathfindWaypathPortalClass **)elem1);
   PathfindWaypathPortalClass *portal2 = *((PathfindWaypathPortalClass **)elem2);	

	const WaypathPositionClass &pos1 = portal1->Get_Waypath_Pos ();
	const WaypathPositionClass &pos2 = portal2->Get_Waypath_Pos ();

	//
	//	Sort based on the waypath segment the portals lie one
	//
	int result = 0;
	if (pos1.Get_Waypoint_Index () < pos2.Get_Waypoint_Index ()) {
		result = -1;
	} else if (pos1.Get_Waypoint_Index () > pos2.Get_Waypoint_Index ()) {
		result = 1;
	} else {
		
		//
		//	If the portals lie on the same segment, then sort by their
		// position along the segment
		//
		if (pos1.Get_Percent () < pos2.Get_Percent ()) {
			result = -1;
		} else if (pos1.Get_Percent () > pos2.Get_Percent ()) {
			result = 1;
		}
	}

   return result;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Find_Portals
//
//////////////////////////////////////////////////////////////////////////////////
void
PathfindClass::Find_Portals
(
	const Vector3 &										p0,
	const Vector3 &										p1,
	DynamicVectorClass<PathfindPortalClass *> &	list,
	bool														action_portals_only
)
{
	//
	//	Loop over all the portals
	//
	for (int index = 0; index < m_PortalList.Count (); index ++) {		
		PathfindPortalClass *portal					= m_PortalList[index];
		PathfindActionPortalClass *action_portal	= portal->As_PathfindActionPortalClass ();
		if (action_portals_only == false || (action_portal != NULL && portal->Get_Action_Type () != PathClass::ACTION_NONE)) {
			
			//
			//	Get the bounding box for the portal
			//
			AABoxClass box;
			portal->Get_Bounding_Box (box);

			//
			//	Does the line pass through this box?
			//
			float percent1 = 0;
			Vector3 intersection_pt (0, 0, 0);
			if (::Find_Intersection_Point (box, p0, p1, &percent1, &intersection_pt)) {
				bool is_valid = true;
				
				//
				//	If this is an action portal request, we need to ensure that the
				// line segment passes through to the exit portal
				//
				if (action_portals_only) {
					is_valid								= false;
					PathfindSectorClass *sector	= action_portal->Peek_Dest_Sector (NULL);
					if (sector != NULL) {

						//
						//	Get a pointer to the exit portal
						//
						PathfindPortalClass *exit_portal = action_portal->Get_Exit_Portal ();
						if (exit_portal != NULL) {
							
							//
							//	Does the line segment pass through to the exit portal?
							//						
							AABoxClass exit_box;
							exit_portal->Get_Bounding_Box (exit_box);
							if (::Find_Intersection_Point (exit_box, p0, p1,
										&percent1, &intersection_pt))
							{
								float dist1 = (box.Center - p0).Length2 ();
								float dist2 = (exit_box.Center - p0).Length2 ();							
								if (dist1 < dist2) {
									is_valid = true;
								}
							}
						}
					}
				}
				
				//
				//	Add the portal to the list
				//
				if (is_valid) {
					list.Add (portal);
				}
			}
		}
	}

	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Render_Debug_Widgets
//
//////////////////////////////////////////////////////////////////////////////////
void
PathfindClass::Render_Debug_Widgets (RenderInfoClass &rinfo)
{
	m_SectorDisplayWidgets.Render_Debug_Widgets (rinfo);
	m_PortalDisplayWidgets.Render_Debug_Widgets (rinfo);
	return ;
}


