/*
**	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/Tools/LevelEdit/NodeMgr.cpp                  $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 3/07/02 1:54p                                               $*
 *                                                                                             *
 *                    $Revision:: 42                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "stdafx.h"
#include "nodemgr.h"
#include "node.h"
#include "editorchunkids.h"
#include "preset.h"
#include "wwdebug.h"
#include "chunkio.h"
#include "persistfactory.h"
#include "sceneeditor.h"
#include "leveleditview.h"
#include "instancespage.h"
#include "gameobjmanager.h"
#include "terrainnode.h"
#include "leveledit.h"
#include "regkeys.h"
#include "presetmgr.h"
#include "presetremapdialog.h"
#include "heightfieldmgr.h"


///////////////////////////////////////////////////////////////////////
// Singleton instance
///////////////////////////////////////////////////////////////////////
NodeMgrClass _TheNodeMgr;


///////////////////////////////////////////////////////////////////////
//	Constants
///////////////////////////////////////////////////////////////////////
enum
{
	CHUNKID_VARIABLES			= 0x00000100,
	CHUNKID_NODES
};


enum
{
	XXX_VARID_NEXT_NODE_ID		= 0x01,
};


enum
{
	FIRST_OBJECT_NODE_ID		= 100000,
	FIRST_STATIC_NODE_ID		= 1100000000,
	FIRST_LIGHT_NODE_ID		= 1200000000,
	FIRST_MISC_NODE_ID		= 1300000000,
};


///////////////////////////////////////////////////////////////////////
// Static member initialization
///////////////////////////////////////////////////////////////////////
NodeClass * NodeMgrClass::_NodeListHead	= NULL;

uint32 NodeMgrClass::_NextObjectNodeID		= FIRST_OBJECT_NODE_ID;
uint32 NodeMgrClass::_NextStaticNodeID		= FIRST_STATIC_NODE_ID;
uint32 NodeMgrClass::_NextLightNodeID		= FIRST_LIGHT_NODE_ID;
uint32 NodeMgrClass::_NextMiscNodeID		= FIRST_MISC_NODE_ID;


//////////////////////////////////////////////////////////////////////////////////////////
//
//	~NodeMgrClass
//
//////////////////////////////////////////////////////////////////////////////////////////
NodeMgrClass::~NodeMgrClass (void)
{
	return ;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
//	Chunk_ID
//
//////////////////////////////////////////////////////////////////////////////////////////
uint32
NodeMgrClass::Chunk_ID (void) const
{
	return CHUNKID_NODEMGR;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
//	Setup_Node_Identity
//
//////////////////////////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Setup_Node_Identity (NodeClass &node)
{
	PresetClass *preset	= node.Get_Preset ();
	int id					= node.Get_ID ();

	//
	//	Give the node an ID if it doesn't already
	// have one.
	//
	if (id == 0) {
		id = Get_Node_ID (node.Get_Type ());
		node.Set_ID (id);
	}	

	//
	//	Give the node a name
	//
	if (preset !=  NULL) {
		CString name;
		name.Format ("%s.%d", preset->Get_Name (), id);
		node.Set_Name (name);
	} else {
		
		RenderObjClass *model = node.Peek_Render_Obj ();
		if (model != NULL) {
			CString name;
			name.Format ("%s.%d", model->Get_Name (), id);
			node.Set_Name (name);		
		} else {
			CString name;
			name.Format ("Unknown.%d", id);
			node.Set_Name (name);		
		}
	}

	return ;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
//	Create_Node
//
//////////////////////////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Create_Node (PresetClass *preset, uint32 id)
{
	NodeClass *node = preset->Create ();
	if (node != NULL) {

		//
		//	Give the new node a unique id and name
		//
		node->Set_ID (id);
		Setup_Node_Identity (*node);

		//
		//	Give the new node a chance to initialize its data
		//
		node->Initialize ();

		//
		//	Keep track of this node
		//
		Add_Node (node);
	}

	return node;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
//	Add_Node
//
//////////////////////////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Add_Node (NodeClass *node)
{
	WWASSERT (node->m_NextNode == 0);
	WWASSERT (node->m_PrevNode == 0);
	
	//
	//	Add the node to the the list and lock
	// a refcount on the node
	//
	if (	node->m_NextNode == NULL &&
			node->m_PrevNode == NULL &&
			node != _NodeListHead)
	{
		Link_Node (node);
		SAFE_ADD_REF (node);

		//
		//	Update the UI
		//
		InstancesPageClass *instances_form = ::Get_Instances_Form ();
		if (instances_form != NULL) {
			instances_form->Add_Node (node);
		}
	}

	return ;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
//	Remove_Node
//
//////////////////////////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Remove_Node (NodeClass *node)
{
	WWASSERT (node != 0);

	if (	node->m_NextNode != NULL ||
			node->m_PrevNode != NULL ||
			node == _NodeListHead)
	{
		//
		//	Remove the node from our list and
		//	free our hold on the node
		//	
		Unlink_Node (node);		

		//
		//	Update the UI
		//
		InstancesPageClass *instances_form = ::Get_Instances_Form ();
		if (instances_form != NULL) {
			instances_form->Remove_Node (node);
		}

		MEMBER_RELEASE (node);
	}
	
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//	Link_Node
//
////////////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Link_Node (NodeClass *node)
{
	WWASSERT (node->m_NextNode == 0);
	WWASSERT (node->m_PrevNode == 0);

	// Add this preset in front of the current head of the list
	node->m_NextNode = _NodeListHead;
	
	// If the list wasn't empty, link the next definiton back to this node
	if (node->m_NextNode != 0) {
		node->m_NextNode->m_PrevNode = node;
	}

	// Point the head of the list at this node now
	_NodeListHead = node;
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//	Unlink_Node
//
////////////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Unlink_Node (NodeClass *node)
{
	WWASSERT(node != 0);

	// Handle the node's prev pointer:
	if (node->m_PrevNode == 0) {

		// this node is the head
		WWASSERT (_NodeListHead == node);
		_NodeListHead = node->m_NextNode;
	
	} else {

		// link it's prev with it's next
		node->m_PrevNode->m_NextNode = node->m_NextNode;

	}

	// Handle the node's next pointer if its not at the end of the list:
	if (node->m_NextNode != 0) {		
		node->m_NextNode->m_PrevNode = node->m_PrevNode;
	}

	// The node is now un-linked
	node->m_NextNode = 0;
	node->m_PrevNode = 0;
	return ;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
//	Free_Nodes
//
//////////////////////////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Free_Nodes (void)
{
	NodeClass *curr_node = NULL;
	NodeClass *next_node = NULL;
	InstancesPageClass *instances_form = ::Get_Instances_Form ();

	//
	//	Unlink the nodes
	//
	DynamicVectorClass<NodeClass *> node_list;
	for (curr_node = _NodeListHead; curr_node != NULL; curr_node = next_node) {
		next_node = curr_node->m_NextNode;
		curr_node->m_PrevNode = NULL;
		curr_node->m_NextNode = NULL;
		node_list.Add (curr_node);
	}

	//
	//	Free the nodes
	//
	_NodeListHead = NULL;
	for (int index = 0; index < node_list.Count (); index ++) {
		MEMBER_RELEASE (node_list[index]);
	}	
	
	//
	//	Reset the node ID ranges
	//
	uint32 foo = 0;
	Get_ID_Range (NODE_TYPE_OBJECT,		&_NextObjectNodeID,	&foo);
	Get_ID_Range (NODE_TYPE_TERRAIN,		&_NextStaticNodeID,	&foo);
	Get_ID_Range (NODE_TYPE_LIGHT,		&_NextLightNodeID,	&foo);
	Get_ID_Range (NODE_TYPE_VIS_POINT,	&_NextMiscNodeID,		&foo);

	//
	//	Update the UI
	//
	if (instances_form != NULL) {
		instances_form->Reset_List ();
	}

	NetworkObjectMgrClass::Delete_Pending ();
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Find_Node
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Find_Node (uint32 id)
{
	NodeClass *req_node = NULL;

	for (	NodeClass *node = _NodeListHead;
			(req_node == NULL) && (node != NULL);
			node = node->m_NextNode)
	{
		//
		//	Is this the node we are looking for?
		//
		if (node->Get_ID () == id) {
			req_node = node;
		}
	}

	return req_node;
}


///////////////////////////////////////////////////////////////////////
//
//	Find_Node
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Find_Node (const char *name)
{
	NodeClass *req_node = NULL;

	for (	NodeClass *node = _NodeListHead;
			(req_node == NULL) && (node != NULL);
			node = node->m_NextNode)
	{
		//
		//	Is this the node we are looking for?
		//
		if (::lstrcmpi (node->Get_Name (), name) == 0) {
			req_node = node;
		}
	}

	return req_node;
}


///////////////////////////////////////////////////////////////////////
//
//	Get_Next
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Get_Next (NodeClass *node)
{
	return node->m_NextNode;
}


///////////////////////////////////////////////////////////////////////
//
//	Get_First
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Get_First (NODE_TYPE type)
{
	NodeClass *req_node = NULL;

	//
	//	Loop through all the nodes until we've found the
	// first one that matches the criteria
	//
	for (	NodeClass *node = _NodeListHead;
			(req_node == NULL) && (node != NULL);
			node = node->m_NextNode)
	{
		if (node->Get_Type () == type) {
			req_node = node;
		}
	}

	return req_node;
}


///////////////////////////////////////////////////////////////////////
//
//	Get_Next
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Get_Next (NodeClass *current, NODE_TYPE type)
{
	NodeClass *req_node = NULL;

	//
	//	Loop through all the nodes until we've found the
	// first one that matches the criteria
	//
	while ((req_node == NULL) && ((current = current->m_NextNode) != NULL))
	{
		if (current->Get_Type () == type) {
			req_node = current;
		}
	}

	return req_node;
}



///////////////////////////////////////////////////////////////////////
//
//	Get_First
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Get_First (PresetClass *preset)
{
	NodeClass *req_node = NULL;

	//
	//	Loop through all the nodes until we've found the
	// first one that matches the criteria
	//
	for (	NodeClass *node = _NodeListHead;
			(req_node == NULL) && (node != NULL);
			node = node->m_NextNode)
	{
		if (node->Get_Preset () == preset) {
			req_node = node;
		}
	}

	return req_node;
}


///////////////////////////////////////////////////////////////////////
//
//	Find_First
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Find_First (uint32 class_id)
{
	NodeClass *req_node = NULL;

	//
	//	Loop through all the nodes until we've found the
	// first one that matches the criteria
	//
	for (	NodeClass *node = _NodeListHead;
			(req_node == NULL) && (node != NULL);
			node = node->m_NextNode)
	{
		//
		//	Get the preset this node was created from
		//
		PresetClass *preset = node->Get_Preset ();
		if (preset != NULL) {

			//
			//	Is this preset belong to the requested class?
			//
			DefinitionClass *definition = preset->Get_Definition ();
			if (definition != NULL && definition->Get_Class_ID () == class_id) {
				req_node = node;
			}
		}
	}

	return req_node;
}


///////////////////////////////////////////////////////////////////////
//
//	Find_Next
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Find_Next (NodeClass *current, uint32 class_id)
{
	NodeClass *req_node = NULL;

	//
	//	Loop through all the nodes until we've found the
	// first one that matches the criteria
	//
	while ((req_node == NULL) && ((current = current->m_NextNode) != NULL))
	{
		//
		//	Get the preset this node was created from
		//
		PresetClass *preset = current->Get_Preset ();
		if (preset != NULL) {

			//
			//	Is this preset belong to the requested class?
			//
			DefinitionClass *definition = preset->Get_Definition ();
			if (definition != NULL && definition->Get_Class_ID () == class_id) {
				req_node = current;
			}
		}
	}

	return req_node;
}


///////////////////////////////////////////////////////////////////////
//
//	Get_Next
//
///////////////////////////////////////////////////////////////////////
NodeClass *
NodeMgrClass::Get_Next (NodeClass *current, PresetClass *preset)
{
	NodeClass *req_node = NULL;

	//
	//	Loop through all the nodes until we've found the
	// first one that matches the criteria
	//
	while ((req_node == NULL) && ((current = current->m_NextNode) != NULL))
	{
		if (current->Get_Preset () == preset) {
			req_node = current;
		}
	}

	return req_node;
}


///////////////////////////////////////////////////////////////////////
//
//	Contains_Data
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Contains_Data (void) const
{
	return (_NodeListHead != NULL);
}


///////////////////////////////////////////////////////////////////////
//
//	Save
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Save (ChunkSaveClass &csave)
{
	bool retval = true;

	//
	//	Save the variables to their own chunk
	//
	csave.Begin_Chunk (CHUNKID_VARIABLES);	
		//WRITE_MICRO_CHUNK (csave, VARID_NEXT_NODE_ID, _NextNodeID);
	csave.End_Chunk ();


	//
	//	Save the nodes to their own chunk
	//
	csave.Begin_Chunk (CHUNKID_NODES);
	retval &= Save_Nodes (csave);
	csave.End_Chunk ();
	return retval;
}


///////////////////////////////////////////////////////////////////////
//
//	Load
//
//	Note:  It is the outside caller's responsibility to make sure
// all nodes have been removed from the manager before a load.  This
// is done so we can import/export nodes without trashing the existing
// node set.
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Load (ChunkLoadClass &cload)
{
	bool retval = true;	
	while (cload.Open_Chunk ()) {
		switch (cload.Cur_Chunk_ID ()) {
			
			//
			//	Load all the nodes from this chunk
			//
			case CHUNKID_NODES:
				retval &= Load_Nodes (cload);
				break;

			case CHUNKID_VARIABLES:
				retval &= Load_Variables (cload);
				break;
		}

		cload.Close_Chunk ();
	}

	SaveLoadSystemClass::Register_Post_Load_Callback (this);
	return retval;
}


///////////////////////////////////////////////////////////////////////
//
//	Save_Node_List
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Save_Node_List (ChunkSaveClass &csave, NODE_LIST &node_list)
{	
	//
	//	Loop over all the nodes in the list
	//
	for (int index = 0; index < node_list.Count (); index ++) {
		NodeClass *node = node_list[index];
		if (node != NULL) {
			
			//
			//	Save this node to its own chunk
			//
			csave.Begin_Chunk (node->Get_Factory ().Chunk_ID ());
			node->Get_Factory ().Save (csave, node);
			csave.End_Chunk ();
		}
	}

	return true;
}


///////////////////////////////////////////////////////////////////////
//
//	Load_Node_List
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Load_Node_List (ChunkLoadClass &cload, NODE_LIST &node_list)
{	
	while (cload.Open_Chunk ()) {

		//
		//	Load this node from the chunk (if possible)
		//
		PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
		if (factory != NULL) {
			NodeClass *node = (NodeClass *)factory->Load (cload);
			if (node != NULL) {
				
				//
				// Does this node's preset still exist?
				//
				if (node->Get_Preset () != NULL || node->Get_Type () == NODE_TYPE_WAYPOINT) {
					
					//
					//	Initialize the node
					//
					node->Initialize ();
					if (node->Needs_Save () == false) {
						node->Lock (true);
					}

					//
					//	Add this node to the list
					//
					node->Add_Ref ();
					node_list.Add (node);

				} else {
					WWDEBUG_SAY (("No preset for linked node %s.\r\n", node->Get_Name ()));
				}

				MEMBER_RELEASE (node);
			}
		}

		cload.Close_Chunk ();
	}

	return true;
}


///////////////////////////////////////////////////////////////////////
//
//	Save_Nodes
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Save_Nodes (ChunkSaveClass &csave)
{	
	for (	NodeClass *node = _NodeListHead;
			node != NULL;
			node = node->m_NextNode)
	{
		if (node->Needs_Save ()) {
			
			//
			//	Save this node to its own chunk
			//
			csave.Begin_Chunk (node->Get_Factory ().Chunk_ID ());
			node->Get_Factory ().Save (csave, node);
			csave.End_Chunk ();
		}
	}

	return true;
}


///////////////////////////////////////////////////////////////////////
//
//	Load_Nodes
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Load_Nodes (ChunkLoadClass &cload)
{
	bool retval = true;

	DynamicVectorClass<NodeClass *> removed_node_list;

	while (cload.Open_Chunk ()) {

		//
		//	Load this node from the chunk (if possible)
		//
		PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
		if (factory != NULL) {
			NodeClass *node = (NodeClass *)factory->Load (cload);
			if (node != NULL) {
				
				// Does this node's preset still exist?
				if (node->Get_Preset () != NULL || node->Get_Type () == NODE_TYPE_WAYPOINT) {
					
					//
					//	Initialize the node
					//
					node->Initialize ();
					if (node->Needs_Save () == false) {
						node->Lock (true);
					}

					//
					//	Put the node back into the world
					//
					Add_Node (node);

				} else {
					node->Add_Ref ();
					removed_node_list.Add (node);

					WWDEBUG_SAY (("No preset for %s, removing from level.\r\n", node->Get_Name ()));
				}

				MEMBER_RELEASE (node);
			}
		}

		cload.Close_Chunk ();
	}

	//
	//	Display a dialog to the user allowing them to remap removed nodes
	// to other presets...
	//
	if (removed_node_list.Count () > 0) {
		PresetRemapDialogClass dialog (::AfxGetMainWnd ());
		dialog.Set_Node_list (removed_node_list);
		dialog.DoModal ();
	}

	return retval;
}


///////////////////////////////////////////////////////////////////////
//
//	On_Post_Load
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::On_Post_Load (void)
{
	SceneEditorClass *scene = ::Get_Scene_Editor ();

	//
	//	Loop over all the nodes
	//
	for (	NodeClass *node = _NodeListHead;
			node != NULL;
			node = node->m_NextNode)
	{
		//
		//	Add them to the scene...
		//
		node->Add_To_Scene ();
		scene->Update_File_Mgr (node);
	}
	
	Reset_New_ID ();
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Load_Variables
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Load_Variables (ChunkLoadClass &cload)
{
	bool retval = true;

	while (cload.Open_Micro_Chunk ()) {
		/*switch (cload.Cur_Micro_Chunk_ID ()) {

		}*/

		cload.Close_Micro_Chunk ();
	}
	
	return retval;
}


///////////////////////////////////////////////////////////////////////
//
//	Reload_Nodes
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Reload_Nodes (void)
{
	::Get_Main_View ()->Allow_Repaint (false);

	//
	//	First, make sure each node has the correct preset pointer
	//
	DynamicVectorClass<NodeClass *> node_list;
	Build_Full_Node_List (node_list);
	for (int index = 0; index < node_list.Count (); index ++) {
		NodeClass *node = node_list[index];
		if (node != NULL) {

			//
			//	Lookup the preset and re-assign it to this node
			//
			uint32 preset_id		= node->Get_Preset_ID ();
			PresetClass *preset	= PresetMgrClass::Find_Preset (preset_id);
			node->Set_Preset (preset);
		}
	}

	//
	//	Ask each node to recreate itself
	//
	for (	NodeClass *node = _NodeListHead; node != NULL; node = node->m_NextNode) {
		node->Reload ();
	}

	::Get_Scene_Editor ()->Validate_Vis ();
	::Get_Main_View ()->Allow_Repaint (true);
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Reload_Nodes
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Reload_Nodes (PresetClass *preset)
{
	::Get_Main_View ()->Allow_Repaint (false);

	//
	//	Loop over all the nodes
	//
	for (	NodeClass *node = _NodeListHead;
			node != NULL;
			node = node->m_NextNode)
	{
		//
		//	Simply ask the node to re-create itself
		//
		if (node->Get_Preset () == preset) {
			node->Reload ();
		}
	}
	
	::Get_Scene_Editor ()->Validate_Vis ();
	::Get_Main_View ()->Allow_Repaint (true);
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Put_Objects_Back
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Put_Objects_Back (const NODE_LIST &obj_list)
{
	//
	//	Insert the nodes back into the system
	//
	for (int index = 0; index < obj_list.Count (); index ++) {
		NodeClass *node = obj_list[index];
		NodeMgrClass::Add_Node (node);
		node->Release_Ref ();
	}
	
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Remove_Dynamic_Objects
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Remove_Dynamic_Objects (NODE_LIST &dynamic_obj_list)
{
	//
	//	Build a list of nodes to that are not dynamic
	//
	for (	NodeClass *node = NodeMgrClass::Get_First ();
			node != NULL;
			node = NodeMgrClass::Get_Next (node))
	{
		if (node->Is_Static () == false) {
			dynamic_obj_list.Add (node);
		}
	}

	//
	//	Remove these nodes from the level
	//
	for (int index = 0; index < dynamic_obj_list.Count (); index ++) {
		NodeClass *node = dynamic_obj_list[index];
		if (node != NULL) {
			node->Add_Ref ();
			NodeMgrClass::Remove_Node (node);
		}
	}
	
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Remove_Static_Objects
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Remove_Static_Objects (NODE_LIST &static_obj_list)
{
	//
	//	Build a list of nodes to that are not dynamic
	//
	for (	NodeClass *node = NodeMgrClass::Get_First ();
			node != NULL;
			node = NodeMgrClass::Get_Next (node))
	{
		if (node->Is_Static ()) {
			static_obj_list.Add (node);
		}
	}

	//
	//	Remove these nodes from the level
	//
	for (int index = 0; index < static_obj_list.Count (); index ++) {
		NodeClass *node = static_obj_list[index];
		if (node != NULL) {
			node->Add_Ref ();
			NodeMgrClass::Remove_Node (node);
		}
	}
	
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Get_Max_Used_ID
//
///////////////////////////////////////////////////////////////////////
uint32
NodeMgrClass::Get_Max_Used_ID (void)
{
	//
	//	Build the full list of nodes
	//
	DynamicVectorClass<NodeClass *> full_node_list;
	Build_Full_Node_List (full_node_list);

	uint32 max_used = 0;

	//
	//	Determine what the largest used ID is
	//
	for (int index = 0; index < full_node_list.Count (); index ++) {
		NodeClass *node = full_node_list[index];
		max_used = max (node->Get_ID (), max_used);
	}

	return max_used;
}


///////////////////////////////////////////////////////////////////////
//
//	Verify_Unique_ID - Verify that given ID is unique.
//
///////////////////////////////////////////////////////////////////////
bool
NodeMgrClass::Verify_Unique_ID(uint32 id)
{
	NodeClass* node = Get_First();

	while (node != NULL) {
		if (node->Get_ID() == id) {
			return false;
		}

		node = Get_Next(node);
	}

	return true;
}


///////////////////////////////////////////////////////////////////////
//
//	Make_Static_Anim_Phys_Collideable
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Make_Static_Anim_Phys_Collideable (bool onoff)
{
	//
	//	Loop over all the terrains in the level
	//
	for (	NodeClass *node = NodeMgrClass::Get_First (NODE_TYPE_TERRAIN);
			node != NULL;
			node = NodeMgrClass::Get_Next (node, NODE_TYPE_TERRAIN))
	{
		TerrainNodeClass *terrain	= (TerrainNodeClass *)node;
		int count						= terrain->Get_Sub_Node_Count ();
		
		//
		//	Loop over all the sections of this terrain object
		//
		for (int index = 0; index < count; index ++) {
			NodeClass *sub_node = terrain->Get_Sub_Node (index);
			if (sub_node != NULL) {				

				//
				//	Check to make sure this is a static anim phys object
				//				
				PhysClass *phys_obj = sub_node->Peek_Physics_Obj ();
				if (phys_obj != NULL && phys_obj->As_StaticAnimPhysClass () != NULL) {
					sub_node->Hide (onoff == false);
					
					//
					//	Turn off collision on or off this object
					//
					if (onoff == false) {
						phys_obj->Inc_Ignore_Counter ();
					} else if (phys_obj->Is_Ignore_Me ()) {
						phys_obj->Dec_Ignore_Counter ();
					}
				}			
			}
		}
	}

	//
	//	Loop over all the tiles in the level
	//
	for (	node = NodeMgrClass::Get_First (NODE_TYPE_TILE);
			node != NULL;
			node = NodeMgrClass::Get_Next (node, NODE_TYPE_TILE))
	{
		//
		//	Check to make sure this is a static anim phys object
		//
		PhysClass *phys_obj = node->Peek_Physics_Obj ();
		if (phys_obj != NULL && phys_obj->As_StaticAnimPhysClass () != NULL) {
			node->Hide (onoff);
			
			//
			//	Turn off collision on or off this object
			//
			if (onoff == false) {
				phys_obj->Inc_Ignore_Counter ();
			} else if (phys_obj->Is_Ignore_Me ()) {
				phys_obj->Dec_Ignore_Counter ();
			}
		}
	}
	
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Build_ID_Collision_List
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Build_ID_Collision_List (NODE_LIST &node_list)
{
	CWaitCursor wait_cursor;

	//
	//	Build a flat list of nodes
	//
	DynamicVectorClass<NodeClass *> full_node_list;
	Build_Full_Node_List (full_node_list);

	//
	//	Loop over all the nodes, checking every one against the other
	//
	for (int index1 = 0; index1 < full_node_list.Count (); index1 ++) {
		for (int index2 = 0; index2 < full_node_list.Count (); index2 ++) {

			NodeClass *node1 = full_node_list[index1];
			NodeClass *node2 = full_node_list[index2];

			//
			//	Do these nodes share the same ID?
			//
			if (node1 != node2 && node1->Get_ID () == node2->Get_ID ()) {
				if (node_list.ID (node1) == -1) {
					node_list.Add (node1);
				}
			}
			
		}
	}

	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Build_Unimportant_ID_Collision_List
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Build_Unimportant_ID_Collision_List (NODE_LIST &node_list)
{
	CWaitCursor wait_cursor;

	//
	//	Build a flat list of nodes
	//
	DynamicVectorClass<NodeClass *> full_node_list;
	Build_Full_Node_List (full_node_list);

	//
	//	Loop over all the nodes, checking every one against the other
	//
	for (int index1 = 0; index1 < full_node_list.Count (); index1 ++) {
		for (int index2 = 0; index2 < full_node_list.Count (); index2 ++) {

			NodeClass *node1 = full_node_list[index1];
			NodeClass *node2 = full_node_list[index2];

			//
			//	Do these nodes share the same ID?
			//
			if (node1 != node2 && node1->Get_ID () == node2->Get_ID ()) {
				if (node_list.ID (node1) == -1) {

					//
					//	Check to make sure this isn't an important node type
					//
					if (	node1->As_ObjectNodeClass () == NULL &&
							node1->Get_Type () != NODE_TYPE_SPAWNER &&
							node1->Get_Type () != NODE_TYPE_TILE &&
							node1->Get_Type () != NODE_TYPE_WAYPATH &&
							node1->Get_Type () != NODE_TYPE_WAYPOINT &&
							node1->Get_Type () != NODE_TYPE_SOUND)
					{
						node_list.Add (node1);
					}
				}
			}
			
		}
	}

	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Reset_New_ID
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Reset_New_ID (void)
{
	//
	//	Reset each of the id ranges
	//
	_NextObjectNodeID = Find_Max_Used_ID (NODE_TYPE_OBJECT) + 1;
	_NextStaticNodeID = Find_Max_Used_ID (NODE_TYPE_TERRAIN) + 1;
	_NextLightNodeID	= Find_Max_Used_ID (NODE_TYPE_LIGHT) + 1;
	_NextMiscNodeID	= Find_Max_Used_ID (NODE_TYPE_VIS_POINT) + 1;

	//
	//	Update each heightfield with a guaranteed unique ID
	//
	HeightfieldMgrClass::Assign_Unique_IDs ();
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Find_Max_Used_ID
//
///////////////////////////////////////////////////////////////////////
uint32
NodeMgrClass::Find_Max_Used_ID (NODE_TYPE type)
{
	//
	//	Determine what our minimum ID is
	//
	uint32 min_id = 0;
	uint32 max_id = 0;
	Get_ID_Range (type, &min_id, &max_id);

	//
	//	First, build a complete list of all the nodes
	//
	DynamicVectorClass<NodeClass *> node_list;
	Build_Full_Node_List (node_list);

	//
	//	Loop over all the nodes in the level and find the largest ID
	// in our range
	//
	uint32 largest_id = min_id;
	for (int index = 0; index < node_list.Count (); index ++) {
		NodeClass *node = node_list[index];
		if (node != NULL) {

			//
			//	Is this the largest ID in our range?
			//
			uint32 curr_id = node->Get_ID ();
			if (curr_id >= min_id && curr_id < max_id) {
				largest_id = max (largest_id, curr_id);
			}
		}
	}

	return largest_id;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
//	Get_Node_ID
//
//////////////////////////////////////////////////////////////////////////////////////////
uint32
NodeMgrClass::Get_Node_ID (NODE_TYPE type)
{
	uint32 new_id = 0;

	switch (type)
	{
		case NODE_TYPE_TERRAIN:
		case NODE_TYPE_TERRAIN_SECTION:
		case NODE_TYPE_TILE:
		case NODE_TYPE_BUILDING:
			new_id = _NextStaticNodeID ++;
			break;

		case NODE_TYPE_DAMAGE_ZONE:
		case NODE_TYPE_ZONE:
			new_id = _NextObjectNodeID ++;
			break;

		case NODE_TYPE_WAYPATH:
		case NODE_TYPE_WAYPOINT:
			new_id = _NextObjectNodeID ++;
			break;
		
		case NODE_TYPE_OBJECT:		
			new_id = _NextObjectNodeID ++;
			break;

		
		case NODE_TYPE_COVER_SPOT:
		case NODE_TYPE_COVER_ATTACK_POINT:
			new_id = _NextObjectNodeID ++;
			break;
		
		case NODE_TYPE_SPAWNER:
			new_id = _NextObjectNodeID ++;
			break;

		case NODE_TYPE_SOUND:
		case NODE_TYPE_TRANSITION:
		case NODE_TYPE_TRANSITION_CHARACTER:
		case NODE_TYPE_DUMMY_OBJECT:
			new_id = _NextObjectNodeID ++;
			break;

		case NODE_TYPE_LIGHT:
			new_id = _NextLightNodeID ++;
			break;

		case NODE_TYPE_UNKNOWN:		
		case NODE_TYPE_VIS:		
		case NODE_TYPE_VIS_POINT:
		case NODE_TYPE_PATHFIND_START:
		default:
			new_id = _NextMiscNodeID ++;
			break;
	}

	return new_id;
}


///////////////////////////////////////////////////////////////////////
//
//	Get_ID_Range
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Get_ID_Range (NODE_TYPE type, uint32 *min_id, uint32 *max_id)
{
	switch (type)
	{
		case NODE_TYPE_TERRAIN:
		case NODE_TYPE_TERRAIN_SECTION:
		case NODE_TYPE_TILE:		
		case NODE_TYPE_BUILDING:
			(*min_id) = theApp.GetProfileInt (CONFIG_KEY, NODE_ID_START_VALUE, FIRST_OBJECT_NODE_ID);
			(*min_id) += 50000;
			(*max_id) = (*min_id) + 50000;
			break;

		case NODE_TYPE_DAMAGE_ZONE:
		case NODE_TYPE_ZONE:
		case NODE_TYPE_WAYPATH:
		case NODE_TYPE_WAYPOINT:
		case NODE_TYPE_OBJECT:		
		case NODE_TYPE_COVER_SPOT:
		case NODE_TYPE_COVER_ATTACK_POINT:
		case NODE_TYPE_SPAWNER:
		case NODE_TYPE_SOUND:
		case NODE_TYPE_TRANSITION:
		case NODE_TYPE_TRANSITION_CHARACTER:
		case NODE_TYPE_DUMMY_OBJECT:
			(*min_id) = theApp.GetProfileInt (CONFIG_KEY, NODE_ID_START_VALUE, FIRST_OBJECT_NODE_ID);
			(*max_id) = (*min_id) + 50000;
			break;

		case NODE_TYPE_LIGHT:
			(*min_id) = FIRST_LIGHT_NODE_ID;
			(*max_id) = (*min_id) + 100000;
			break;

		case NODE_TYPE_UNKNOWN:		
		case NODE_TYPE_VIS:		
		case NODE_TYPE_VIS_POINT:
		case NODE_TYPE_PATHFIND_START:		
			(*min_id) = FIRST_MISC_NODE_ID;
			(*max_id) = (*min_id) + 100000;
			break;

		default:
			(*min_id) = FIRST_MISC_NODE_ID;
			break;
	}

	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Build_Full_Node_List
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Build_Full_Node_List (DynamicVectorClass<NodeClass *> &node_list)
{
	//
	//	Add all nodes and sub-nodes to this list
	//
	for (NodeClass *node = ::Get_Node_Mgr ().Get_First ();
		  node != NULL;
		  node = ::Get_Node_Mgr ().Get_Next (node))
	{
		Add_Nodes_To_List (node_list, node);
	}
	
	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Add_Nodes_To_List
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Add_Nodes_To_List
(
	DynamicVectorClass<NodeClass *> &node_list,
	NodeClass *node
)
{
	node_list.Add (node);

	//
	// If the node is an aggregate (like a terrain), we are adding the 
	// sub-nodes directly into the list
	//
	int sub_count = node->Get_Sub_Node_Count ();
	if (sub_count > 0) {
		for (int index = 0; index < sub_count; index ++) {
			NodeClass *sub_node = node->Get_Sub_Node (index);
			
			//
			//	Recurse into this node
			//
			Add_Nodes_To_List (node_list, sub_node);
		}
	}

	return ;
}


///////////////////////////////////////////////////////////////////////
//
//	Create_All_Embedded_Nodes
//
///////////////////////////////////////////////////////////////////////
void
NodeMgrClass::Create_All_Embedded_Nodes (void)
{
	//
	//	Generate a list of nodes
	//
	DynamicVectorClass<NodeClass *> node_list;
	Build_Full_Node_List (node_list);

	//
	//	Loop over all the nodes
	//
	for (int index = 0; index < node_list.Count (); index ++) {
		if (node_list[index]->Is_Proxied () == false) {
			PresetClass *preset = node_list[index]->Get_Preset ();
			if (preset != NULL) {
				
				//
				//	Create the nodes that are embedded inside this preset
				//
				preset->Create_Linked_Nodes (node_list[index]);
			}
		}
	}

	return ;
}
