/*
**	Command & Conquer Generals Zero Hour(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) 2001-2003 Electronic Arts Inc.																				//
//																																						//
////////////////////////////////////////////////////////////////////////////////

// FILE: BridgeBehavior.cpp ///////////////////////////////////////////////////////////////////////
// Author: Colin Day, July 2002
// Desc:   Behavior module for bridges
///////////////////////////////////////////////////////////////////////////////////////////////////

// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"	// This must go first in EVERY cpp file int the GameEngine

#include "Common/Radar.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/InGameUI.h"
#include "GameClient/FXList.h"
#include "GameClient/Line2D.h"
#include "GameClient/TerrainRoads.h"
#include "GameLogic/AI.h"
#include "GameLogic/AIPathfind.h"
#include "GameLogic/Object.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/ObjectCreationList.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/Module/BridgeBehavior.h"
#include "GameLogic/Module/BridgeScaffoldBehavior.h"
#include "GameLogic/Module/PhysicsUpdate.h"
#include "GameLogic/PartitionManager.h"
#include "GameLogic/TerrainLogic.h"

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BridgeBehaviorModuleData::BridgeBehaviorModuleData( void )
{

	m_lateralScaffoldSpeed = 1.0f;
	m_verticalScaffoldSpeed = 1.0f;

}  // end BridgeBehaviorModuleData

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BridgeBehaviorModuleData::~BridgeBehaviorModuleData( void )
{

	// clear the fx list
	m_fx.clear();

	// clear the ocl list
	m_ocl.clear();

}  // end ~BridgeBehaviorModuleData

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
/*static*/ void BridgeBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
{
  BehaviorModuleData::buildFieldParse( p );

	static const FieldParse dataFieldParse[] = 
	{
		{ "LateralScaffoldSpeed",			INI::parseVelocityReal, NULL, offsetof( BridgeBehaviorModuleData, m_lateralScaffoldSpeed ) },
		{ "VerticalScaffoldSpeed",		INI::parseVelocityReal, NULL, offsetof( BridgeBehaviorModuleData, m_verticalScaffoldSpeed ) },
		{ "BridgeDieFX",		parseFX,		NULL,			offsetof( BridgeBehaviorModuleData, m_fx ) },
		{ "BridgeDieOCL",		parseOCL,		NULL,			offsetof( BridgeBehaviorModuleData, m_ocl ) },
		{ 0, 0, 0, 0 }
	};

  p.add( dataFieldParse );

}  // end buildFieldParse

// ------------------------------------------------------------------------------------------------
/** Parse time and location info in the form of:
	* Delay:#### <Bone:BoneName> */
// ------------------------------------------------------------------------------------------------
static void parseTimeAndLocationInfo( INI *ini, void *instance, 
																			TimeAndLocationInfo *timeAndLocationInfo )
{

	// delay label
	const char *token = ini->getNextToken( ini->getSepsColon() );
	if( stricmp( token, "Delay" ) != 0 )
	{

		DEBUG_CRASH(( "Expected 'Delay' token, found '%s'\n", token ));
		throw INI_INVALID_DATA;

	}  // end if

	// delay value
	ini->parseDurationUnsignedInt( ini, instance, &timeAndLocationInfo->delay, NULL );

	// get optional bone label
	token = ini->getNextTokenOrNull( ini->getSepsColon() );
	if( token )
	{

		// token must be a label for bone location
		if( stricmp( token, "Bone" ) != 0 )
		{

			DEBUG_CRASH(( "Expected 'Bone' token, found '%s'\n", token ));
			throw INI_INVALID_DATA;

		}  // end if

		// read bone name and store
		timeAndLocationInfo->boneName = ini->getNextAsciiString();

	}  // end if

}  // end parseTimeAndLocationInfo

//-------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
/*static*/ void BridgeBehaviorModuleData::parseFX( INI *ini, 
											 														 void *instance, 
																									 void *store, 
																									 const void *userData )
{
	const char *token;

	// create item we will read into and push on list
	BridgeFXInfo item;
	item.fx = NULL;

	// get list to store at
	BridgeFXList *bridgeFXList = (BridgeFXList *)store;

	// fx list label
	token = ini->getNextToken( ini->getSepsColon() );
	if( stricmp( token, "FX" ) != 0 )
	{

		DEBUG_CRASH(( "Expected 'FX' token, found '%s'\n", token ));
		throw INI_INVALID_DATA;

	}  // end if

	// fx list name and store as pointer
	FXList *fx;
	ini->parseFXList( ini, instance, &fx, NULL );

	// store fx list to item
	item.fx = fx;

	// parse the timing and location info
	parseTimeAndLocationInfo( ini, instance, &item.timeAndLocationInfo );

	// put on list
	bridgeFXList->push_back( item );

}  // end parseFX

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
/*static*/ void BridgeBehaviorModuleData::parseOCL( INI *ini, 
																										void *instance,
																										void *store, 
																										const void *userData )
{
	const char *token;

	// create item we will read into and push on list
	BridgeOCLInfo item;
	item.ocl = NULL;

	// get list to store at
	BridgeOCLList *bridgeOCLList = (BridgeOCLList *)store;

	// fx list label
	token = ini->getNextToken( ini->getSepsColon() );
	if( stricmp( token, "OCL" ) != 0 )
	{

		DEBUG_CRASH(( "Expected 'OCL' token, found '%s'\n", token ));
		throw INI_INVALID_DATA;

	}  // end if

	// fx list name and store as pointer
	ObjectCreationList *ocl;
	ini->parseObjectCreationList( ini, instance, &ocl, NULL );

	// store ocl list to item
	item.ocl = ocl;

	// parse the timing and location info
	parseTimeAndLocationInfo( ini, instance, &item.timeAndLocationInfo );

	// put on list
	bridgeOCLList->push_back( item );

}  // end parseOCL

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BridgeBehavior::BridgeBehavior( Thing *thing, const ModuleData *moduleData )
							: UpdateModule( thing, moduleData )
{
	Int i;
	m_scaffoldObjectIDList.clear();
	m_scaffoldPresent = FALSE;

	for( i = 0; i < BRIDGE_MAX_TOWERS; ++i )
		m_towerID[ i ] = INVALID_ID;

	m_fxResolved = FALSE;
	for( Int bodyState = BODY_PRISTINE; bodyState < BODYDAMAGETYPE_COUNT; ++bodyState )
	{

		// initialize the fx and ocl lists
		for( i = 0; i < MAX_BRIDGE_BODY_FX; ++i )
		{

			m_damageToOCL[ bodyState ][ i ] = NULL;
			m_damageToFX[ bodyState ][ i ] = NULL;
			m_repairToOCL[ bodyState ][ i ] = NULL;
			m_repairToFX[ bodyState ][ i ] = NULL;

		}  // end for i

		// these don't need initialization, they have constructors
		// m_damageToSound[ bodyState ] = ???
		// m_repairToSound[ bodyState ] = ???

	}  // end for, bodyState

	m_deathFrame = 0;

}  // end BridgeBehavior

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
BridgeBehavior::~BridgeBehavior( void )
{

	//
	// destroy our four towers if they are still present in the world (they wouldn't be if we
	// are resetting the engine, and those towers were earlier in the destruction list than
	// we were)
	//
	for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
	{
		Object *tower;

		// get the tower and destroy
		tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) );
		if( tower )
			TheGameLogic->destroyObject( tower );

	}  // end for i

}  // end ~BridgeBehavior

// ------------------------------------------------------------------------------------------------
/** Get bridge behavior interface */
// ------------------------------------------------------------------------------------------------
/*static */BridgeBehaviorInterface *BridgeBehavior::getBridgeBehaviorInterfaceFromObject( Object *obj )
{
	
	// sanity
	if( obj == NULL )
		return NULL;

	BehaviorModule **bmi;
	BridgeBehaviorInterface *bbi = NULL;
	for( bmi = obj->getBehaviorModules(); *bmi; ++bmi )
	{

		bbi = (*bmi)->getBridgeBehaviorInterface();
		if( bbi )
			return bbi;

	}  // end for, bmi

	// interface not found
	return NULL;

}  // end getBridgeBehaviorInterfaceFromObject

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::onDelete( void )
{

	// clear the list of scaffold objects
	m_scaffoldObjectIDList.clear();

}  // end onDelete

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::resolveFX( void )
{
	Object *us = getObject();
	Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() );

	// sanity
	if( bridge == NULL )
		return;

	// get the bridge template name
	AsciiString bridgeTemplateName = bridge->getBridgeTemplateName();

	// find the bridge template
	TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName );

	// sanity
	if( bridgeTemplate == NULL )
		return;

	AsciiString name;	
	for( Int bodyState = BODY_PRISTINE; bodyState < BODYDAMAGETYPE_COUNT; ++bodyState )
	{

		// initialize the fx and ocl lists
		for( Int i = 0; i < MAX_BRIDGE_BODY_FX; ++i )
		{

			name = bridgeTemplate->getDamageToOCLString( (BodyDamageType)bodyState, i );
			m_damageToOCL[ bodyState ][ i ] = TheObjectCreationListStore->findObjectCreationList( name.str() );
			if( name.isEmpty() == FALSE && m_damageToOCL[ bodyState ][ i ] == NULL )
				DEBUG_CRASH(( "OCL list '%s' not found\n", name.str() ));

			name = bridgeTemplate->getDamageToFXString( (BodyDamageType)bodyState, i );
			m_damageToFX[ bodyState ][ i ] = TheFXListStore->findFXList( name.str() );
			if( name.isEmpty() == FALSE && m_damageToFX[ bodyState ][ i ] == NULL )
				DEBUG_CRASH(( "FX list '%s' not found\n", name.str() ));

			name = bridgeTemplate->getRepairedToOCLString( (BodyDamageType)bodyState, i );
			m_repairToOCL[ bodyState ][ i ] = TheObjectCreationListStore->findObjectCreationList( name.str() );
			if( name.isEmpty() == FALSE && m_repairToOCL[ bodyState ][ i ] == NULL )
				DEBUG_CRASH(( "OCL list '%s' not found\n", name.str() ));

			name = bridgeTemplate->getRepairedToFXString( (BodyDamageType)bodyState, i );
			m_repairToFX[ bodyState ][ i ] = TheFXListStore->findFXList( name.str() );;
			if( name.isEmpty() == FALSE && m_repairToFX[ bodyState ][ i ] == NULL )
				DEBUG_CRASH(( "FX list '%s' not found\n", name.str() ));

		}  // end for i

		// audio sounds
		name = bridgeTemplate->getDamageToSoundString( (BodyDamageType)bodyState );
		m_damageToSound[ bodyState ].setEventName( name );
		m_damageToSound[ bodyState ].setObjectID( us->getID() );

		name = bridgeTemplate->getRepairedToSoundString( (BodyDamageType)bodyState );
		m_repairToSound[ bodyState ].setEventName( name );
		m_repairToSound[ bodyState ].setObjectID( us->getID() );

	}  // end for, bodyState

	// fx are now "resolved"
	m_fxResolved = TRUE;

}  // end resolveFX

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::setTower( BridgeTowerType towerType, Object *tower )
{

	// sanity
	if( towerType < 0 || towerType >= BRIDGE_MAX_TOWERS )
	{

		DEBUG_CRASH(( "BridgeBehavior::setTower - Invalid tower type index '%d'\n", towerType ));
		return;

	}  // end if

	// store it
	if( tower )
		m_towerID[ towerType ] = tower->getID();
	else
		m_towerID[ towerType ] = INVALID_ID;

}  // end setTower

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
ObjectID BridgeBehavior::getTowerID( BridgeTowerType towerType )
{

	// sanity
	if( towerType < 0 || towerType >= BRIDGE_MAX_TOWERS )
	{

		DEBUG_CRASH(( "BridgeBehavior::setTower - Invalid tower type index '%d'\n", towerType ));
		return INVALID_ID;

	}  // end if

	// return the stored ID
	return m_towerID[ towerType ];

}  // end getTowerID

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::onDamage( DamageInfo *damageInfo )
{

	//
	// get our body info so we now how much damage percent is being done to us ... we need this
	// so that we can propagate the same damage percentage amont the towers and the bridge
	//
	BodyModuleInterface *body = getObject()->getBodyModule();
	Real damagePercentage = damageInfo->in.m_amount / body->getMaxHealth();

	//
	// if the damage didn't come from a bridge tower, then we must propagate this damage
	// to all our towers
	//
	Object *source = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
	if( source == NULL || source->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE )
	{
		Object *tower;

		for( Int i = 0; i < BRIDGE_MAX_TOWERS; i++ )
		{

			tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) );
			if( tower )
			{
				BodyModuleInterface *towerBody = tower->getBodyModule();
				DamageInfo towerDamage;

				towerDamage.in.m_amount = damagePercentage * towerBody->getMaxHealth();
				towerDamage.in.m_sourceID = getObject()->getID();  // we're now the source
				towerDamage.in.m_damageType = damageInfo->in.m_damageType;
				towerDamage.in.m_deathType = damageInfo->in.m_deathType;
				tower->attemptDamage( &towerDamage );

			}  // end if

		}  // end for i

	}  // end if

}  // end onDamage

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::onHealing( DamageInfo *damageInfo )
{

	//
	// get our body info so we now how much healing percent is being done to us ... we need this
	// so that we can propagate the same healing percentage amont the towers and the bridge
	//
	BodyModuleInterface *body = getObject()->getBodyModule();
	Real healingPercentage = damageInfo->in.m_amount / body->getMaxHealth();

	//
	// if the healing didn't come from a bridge tower, then we must propagate this healing
	// to all our towers
	//
	Object *source = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
	if( source == NULL || source->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE )
	{
		Object *tower;

		for( Int i = 0; i < BRIDGE_MAX_TOWERS; i++ )
		{

			tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) );
			if( tower )
			{
				BodyModuleInterface *towerBody = tower->getBodyModule();
				tower->attemptHealing(healingPercentage * towerBody->getMaxHealth(), getObject());

			}  // end if

		}  // end for i

	}  // end if

}  // end onHealing

// ------------------------------------------------------------------------------------------------
/** Pick a random surface spot on the bridge surface */
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::getRandomSurfacePosition( TerrainRoadType *bridgeTemplate, 
																							 const BridgeInfo *bridgeInfo, 
																							 Coord3D *pos )
{

	// sanity
	if( bridgeInfo == NULL || pos == NULL )
		return;

	//
	// pick the spot by finding vectors along the edge of the bridge region, scaling
	// them and then adding them together
	//
	Real scale;

	Coord3D v1;
	v1.x = bridgeInfo->toLeft.x - bridgeInfo->fromLeft.x;
	v1.y = bridgeInfo->toLeft.y - bridgeInfo->fromLeft.y;
	v1.z = bridgeInfo->toLeft.z - bridgeInfo->fromLeft.z;
	scale = GameLogicRandomValueReal( 0.0f, 1.0f );
	v1.x *= scale;
	v1.y *= scale;
	v1.z *= scale;

	Coord3D v2;
	v2.x = bridgeInfo->fromRight.x - bridgeInfo->fromLeft.x;
	v2.y = bridgeInfo->fromRight.y - bridgeInfo->fromLeft.y;
	v2.z = bridgeInfo->fromRight.z - bridgeInfo->fromLeft.z;
	scale = GameLogicRandomValueReal( 0.0f, 1.0f );
	v2.x *= scale;
	v2.y *= scale;
	v2.z *= scale;

	// set the position
	pos->x = v1.x + v2.x + bridgeInfo->fromLeft.x;
	pos->y = v1.y + v2.y + bridgeInfo->fromLeft.y;
	pos->z = v1.z + v2.z + bridgeInfo->fromLeft.z;

	//
	// we now have a position picked, the last thing to do is add in an additional 
	// Z component so that effects can be created in a "cube" area on and above the bridge
	//
	pos->z += GameLogicRandomValueReal( 0.0f, bridgeTemplate->getTransitionEffectsHeight() );

}  // end getRandomSurfacePosition

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::doAreaEffects( TerrainRoadType *bridgeTemplate,
																		Bridge *bridge, 
																		const ObjectCreationList *ocl, 
																		const FXList *fx )
{
	
	// sanity
	if( bridge == NULL )
		return;

	// if no effects, don't bother
	if( ocl == NULL && fx == NULL )
		return;

	// get bridge info
	const BridgeInfo *bridgeInfo = bridge->peekBridgeInfo();

	// play effects in the bridge area
	const Int maxEffects = bridgeTemplate->getNumFXPerType();
	Coord3D pos;
	for( Int i = 0; i < maxEffects; ++i )
	{

		// pick spot in the bridge area and do FX
		if( fx )
		{

			getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos );
			FXList::doFXPos( fx, &pos );

		}  // end if

		// pick spot in the bridge area and do OCL
		if( ocl )
		{

			getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos );
			ObjectCreationList::create( ocl, getObject(), &pos, NULL, INVALID_ANGLE );

		}  // end if

	}  // end for i

}  // end doAreaEffects

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::onBodyDamageStateChange( const DamageInfo* damageInfo, 
																							BodyDamageType oldState, 
																							BodyDamageType newState )
{

	//
	// check for coming back from the dead, if our new state is not the rubble state, we can't
	// possibly be dead
	//
	if( newState != BODY_RUBBLE )
		m_deathFrame = 0;

	// first resolve any fx stuff if we need to 
	if( m_fxResolved == FALSE )
		resolveFX();

	// sanity
	if( m_fxResolved == FALSE )
		return;

	// sanity
	DEBUG_ASSERTCRASH( oldState != newState, ("BridgeBehavior::onBodyDamageStateChange - oldState and newState should be different if this is getting called\n") );

	Object *us = getObject();
	Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() );

	// sanity
	if( bridge == NULL )
	{

		DEBUG_CRASH(( "BridgeBehavior - Unable to find bridge\n" ));
		return;

	}  // end if

	// get the bridge template name
	AsciiString bridgeTemplateName = bridge->getBridgeTemplateName();

	// find the bridge template
	TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName );

	// sanity
	DEBUG_ASSERTCRASH( bridgeTemplate, ("BridgeBehavior: Unable to find bridge template '%s' in bridge object '%s'\n",	
																		 bridgeTemplateName,
																		 us->getTemplate()->getName().str()) );

	//
	// given the old state and the new state, did we get worse (damaged) or did
	// we get better (repaired)?
	//
	Bool gotRepaired = IS_CONDITION_WORSE( oldState, newState );

	// get the effect data
	AsciiString soundString;
	AsciiString oclString[ MAX_BRIDGE_BODY_FX ];
	AsciiString fxString[ MAX_BRIDGE_BODY_FX ];
	if( gotRepaired )
	{

		// play the sound
		TheAudio->addAudioEvent( &m_repairToSound[ newState ] );

		for( Int i = 0; i < MAX_BRIDGE_BODY_FX; i++ )
			doAreaEffects( bridgeTemplate, bridge, m_repairToOCL[ newState ][ i ], m_repairToFX[ newState ][ i ] );

	}  // end if
	else
	{

		// play the sound
		TheAudio->addAudioEvent( &m_damageToSound[ newState ] );

		for( Int i = 0; i < MAX_BRIDGE_BODY_FX; i++ )
			doAreaEffects( bridgeTemplate, bridge, m_damageToOCL[ newState ][ i ], m_damageToFX[ newState ][ i ] );

	}  // end else

	// update bridge damage states
	///@todo this should be re-written, there no need for this looping bridge examination
	TheTerrainLogic->updateBridgeDamageStates();

	//
	// for the local player, if this bridge has switched from rubble, to usable, or from
	// usable to rubble, we should reflect the change on the radar.  note that we
	// request that the radar queue a refresh sometime in the future because it keeps
	// track of how often we makes requests to do a refresh and doesn't do them too 
	// often because it's expensive to refresh the terrain
	//
	if( oldState == BODY_RUBBLE || newState == BODY_RUBBLE )
		TheRadar->queueTerrainRefresh();

}  // end onBodyDamageStateChange

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
UpdateSleepTime BridgeBehavior::update( void )
{

	// if we're dead, we need to possibly throw off some effects
	if( m_deathFrame != 0 )
	{
		AsciiString boneName;

		// get object
		Object *us = getObject();

		// get module data
		const BridgeBehaviorModuleData *modData = getBridgeBehaviorModuleData();

		// get bridge information
		Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() );
		const BridgeInfo *bridgeInfo = NULL;
		TerrainRoadType *bridgeTemplate = NULL;
		if ( bridge )
		{
			DEBUG_ASSERTCRASH( bridge, ("BridgeBehavior::update - Unable to find bridge\n") );

			// get bridge info
			bridgeInfo = bridge->peekBridgeInfo();

			// get the bridge template info
			AsciiString bridgeTemplateName = bridge->getBridgeTemplateName();
			bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName );
			DEBUG_ASSERTCRASH( bridgeTemplate, ("BridgeBeahvior::getRandomSurfacePosition - Encountered a bridge with no template!\n") );
		}

		// how much time has passed between now and our destruction frame
		UnsignedInt deathTime = TheGameLogic->getFrame() - m_deathFrame;

		// see if there are any fx visuals we need to execute
		BridgeFXList::const_iterator fxIt;
		for( fxIt = modData->m_fx.begin(); fxIt != modData->m_fx.end(); ++fxIt )
		{

			//
			// we'll launch an fx list if our death time is equal to exactly the delay 
			// we're waiting for to launch the list
			//
			if( deathTime == (*fxIt).timeAndLocationInfo.delay )
			{
				Coord3D pos;

				//
				// if a bone name is present, we'll use the bone position, otherwise we'll pick a
				// spot somewhere on the bridge surface
				//
				boneName = (*fxIt).timeAndLocationInfo.boneName;
				if( boneName.isEmpty() == FALSE )
					us->getSingleLogicalBonePosition( boneName.str(), &pos, NULL );
				else if ( bridge && bridgeTemplate && bridgeInfo)//we have valid Terrain data for the bridge
					getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos );
				else
					pos.set( getObject()->getPosition() );
					

				// launch the fx list
				FXList::doFXPos( (*fxIt).fx, &pos );

			}  // end if

		}  // end for, fxIt

		// see if there are any ocl visuals we need to execute
		BridgeOCLList::const_iterator oclIt;
		for( oclIt = modData->m_ocl.begin(); oclIt != modData->m_ocl.end(); ++oclIt )
		{

			//
			// we'll launch an ocl list if our death time is equal to exactly the delay 
			// we're waiting for to launch the list
			//
			if( deathTime == (*oclIt).timeAndLocationInfo.delay )
			{
				Coord3D pos;

				//
				// if a bone name is present, we'll use the bone position, otherwise we'll pick a
				// spot somewhere on the bridge surface
				//
				boneName = (*oclIt).timeAndLocationInfo.boneName;
				if( boneName.isEmpty() == FALSE )
				{

					//
					// special case for creating an OCL for using the bridge object parent center location
					// why do we have this ... well, apparently the OCL ignores the obj parameter
					// passed in if you give it a position parameter, and we need it to pay attention
					// to that object parameteter so the OCL stuff can inherit the LIKE_EXISTING 
					// properties from its parent
					//
					if( boneName.compare( "ParentObject" ) == 0 )
					{
						
						// launch the effects just using the parent object for location info
						ObjectCreationList::create( (*oclIt).ocl, us, NULL );

					}  // endif 
					else
					{

						// get bone position
						us->getSingleLogicalBonePosition( boneName.str(), &pos, NULL );

						// launch the fx list
						ObjectCreationList::create( (*oclIt).ocl, us, &pos, NULL, INVALID_ANGLE );
					
					}  // end else

				}  // end if, bone name not empty
				else
				{

					// get random place on bridge
					if ( bridge && bridgeTemplate && bridgeInfo )//we have valid Terrain data for the bridge
						getRandomSurfacePosition( bridgeTemplate, bridgeInfo, &pos );
					else
						pos.set( getObject()->getPosition() );

					// launch the fx list
					ObjectCreationList::create( (*oclIt).ocl, us, &pos, NULL, INVALID_ANGLE );

				}  // end else

			}  // end if

		}  // end for, oclIt

	}  // end if

	return UPDATE_SLEEP_NONE;

}  // end update

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::onDie( const DamageInfo *damageInfo )
{

	// kill the towers associated with us
	Object *tower;
	for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
	{

		tower = TheGameLogic->findObjectByID( getTowerID( (BridgeTowerType)i ) );
		if( tower )
			tower->kill();

	}  // end for, i

	// we need to handle anything that was on top of us now that we've been destroyed
	handleObjectsOnBridgeOnDie();

	// we have now died, record the death frame
	m_deathFrame = TheGameLogic->getFrame();
			
}  // end onDie

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::handleObjectsOnBridgeOnDie( void )
{
	const Object *bridge = getObject();
	const Coord3D *bridgePos = bridge->getPosition();

	Bridge *terrainBridge = TheTerrainLogic->findBridgeAt( getObject()->getPosition() );
	if( terrainBridge )
	{
		PathfindLayerEnum bridgeLayer = terrainBridge->getLayer();

		BridgeInfo bridgeInfo;

		// get the bridge info
		terrainBridge->getBridgeInfo( &bridgeInfo );

		// setup a polygon area using the bridge extents
		Coord3D bridgePolygon[ 4 ];
		bridgePolygon[ 0 ] = bridgeInfo.fromLeft;
		bridgePolygon[ 1 ] = bridgeInfo.fromRight;
		bridgePolygon[ 2 ] = bridgeInfo.toRight;
		bridgePolygon[ 3 ] = bridgeInfo.toLeft;

		//
		// find the lowest Z point of the bridge area ... we will use this to figure out of
		// objects in the bridge area are "on top" of the bridge
		//
		Real lowBridgeZ = bridgePolygon[ 0 ].z;
		for( Int i = 0; i < 4; ++i )
			if( bridgePolygon[ i ].z < lowBridgeZ )
				lowBridgeZ = bridgePolygon[ i ].z;
		
		//
		// given the polygon area, how big is the radius that we need to scan in the world
		// to cover from the center of the bridge (the bridge object position) to the edge
		// of the bridge
		//
		Coord2D v;
		v.x = bridgeInfo.toLeft.x - bridgePos->x;
		v.y = bridgeInfo.toLeft.y - bridgePos->y;
		Real radius = v.length();

		// scan the objects in the radius
		ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( bridgePos, 
																																			 radius, 
																																			 FROM_CENTER_2D );
		MemoryPoolObjectHolder hold( iter );
		Object *other;
		for( other = iter->first(); other; other = iter->next() )
		{

			// ingnore some kind of objects
			if( other->isKindOf( KINDOF_BRIDGE ) || other->isKindOf( KINDOF_BRIDGE_TOWER ) )
				continue;

			// ignore airborne objects
			if( other->isAboveTerrain() )
				continue;

			// ignore objects that were not actually on the bridge
			if( other->getPosition()->z < lowBridgeZ )
				continue;

			// ignore objects that are not inside the bridge polygon
			if( PointInsideArea2D( other->getPosition(), bridgePolygon, 4 ) == FALSE )
				continue;

			// if object not on same layer as bridge do nothing
			if( bridgeLayer != other->getLayer() )
				continue;

			if (other->getLayer() == bridgeLayer)
				other->setLayer(LAYER_GROUND);

			// if they have physics, let 'em fall, otherwise just kill 'em
			PhysicsBehavior* physics = other->getPhysics();
			if (physics)
				physics->setAllowToFall(true);
			else
				other->kill();

		}  // end for, other	

	}  // end if, terrainBridge

}  // end handleObjectsOnBridgeDie

// ------------------------------------------------------------------------------------------------
/** Set all the position, angle, and speed data we need to for a single scaffold object */
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::setScaffoldData( Object *obj, 
																			Real *angle, 
																			Real *sunkenHeight, 
																			const Coord3D *riseToPos, 
																			const Coord3D *buildPos, 
																			const Coord3D *bridgeCenter )
{

	// sanity
	if( obj == NULL || angle == NULL || riseToPos == NULL || buildPos == NULL )
		return;

	const BridgeBehaviorModuleData *modData = getBridgeBehaviorModuleData();

	// get the scaffold behavior interface
	BridgeScaffoldBehaviorInterface *scaffoldBehavior;
	scaffoldBehavior = BridgeScaffoldBehavior::getBridgeScaffoldBehaviorInterfaceFromObject( obj );
	DEBUG_ASSERTCRASH( scaffoldBehavior, ("Unable to find bridge scaffold behavior interface\n") );

	// compute the sunken position that the object will initially start at
	Real fudge = 8.0f;
	Coord3D sunkenPos = *riseToPos;
	sunkenPos.z = sunkenPos.z - *sunkenHeight - fudge;

	// set object initial position
	obj->setPosition( &sunkenPos );

	// set all the destination points for all scaffold motion
	scaffoldBehavior->setPositions( &sunkenPos, riseToPos, buildPos );
	
	// set the scaffold object in motion rising up out of the ground
	scaffoldBehavior->setMotion( STM_RISE );

	// set object angle
	obj->setOrientation( *angle );

	//
	// set the speed of the scaffold "animation" which is based on how big of a distance
	// all the scaffold objects have to traverse in order to meet up and be complete in
	// the center of the bridge in an interesting way
	//
	Real lateralSpeed = modData->m_lateralScaffoldSpeed;
	Coord3D buildUpPosToBridgeCenter, riseToPosToBridgeCenter;
	buildUpPosToBridgeCenter.x = buildPos->x - riseToPos->x;
	buildUpPosToBridgeCenter.y = buildPos->y - riseToPos->y;
	buildUpPosToBridgeCenter.z = buildPos->z - riseToPos->z;
	riseToPosToBridgeCenter.x = bridgeCenter->x - riseToPos->x;
	riseToPosToBridgeCenter.y = bridgeCenter->y - riseToPos->y;
	riseToPosToBridgeCenter.z = bridgeCenter->z - riseToPos->z;
	Real distBuildUpPosToBridgeCenter = buildUpPosToBridgeCenter.length();
	Real distRiseToPosToBridgeCenter = riseToPosToBridgeCenter.length();
	scaffoldBehavior->setLateralSpeed( lateralSpeed * (distBuildUpPosToBridgeCenter / distRiseToPosToBridgeCenter) );

	// rising speed is always the same for all objects
	Real verticalSpeed = modData->m_verticalScaffoldSpeed;
	scaffoldBehavior->setVerticalSpeed( verticalSpeed );

}  // end setScaffoldData

// ------------------------------------------------------------------------------------------------
/** Start the bridge repair scaffolding.  If we already have scaffolding this call
	* is ignored */
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::createScaffolding( void )
{

	// if we have scaffolding up already do nothing
	if( m_scaffoldPresent == TRUE )
		return;

	// get the bridge world object
	Object *us = getObject();
	const Coord3D *center = us->getPosition();

	// get our bridge object
	Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() );

	// get the bridge template
	AsciiString bridgeTemplateName = bridge->getBridgeTemplateName();
	TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName );
	DEBUG_ASSERTCRASH( bridgeTemplate, ("Unable to find bridge template to create scaffolding\n") );

	// get the thing template for the scaffold object we're going to use
	AsciiString scaffoldObjectName = bridgeTemplate->getScaffoldObjectName();
	const ThingTemplate *scaffoldTemplate = TheThingFactory->findTemplate( scaffoldObjectName );
	if( scaffoldTemplate == NULL )
	{

		DEBUG_CRASH(( "Unable to find bridge scaffold template\n" ));
		return;

	}  // end if

	// get thing template for scaffold support object
	AsciiString scaffoldSupportObjectName = bridgeTemplate->getScaffoldSupportObjectName();
	const ThingTemplate *scaffoldSupportTemplate = TheThingFactory->findTemplate( scaffoldSupportObjectName );
	if( scaffoldSupportTemplate == NULL )
	{

		DEBUG_CRASH(( "Unable to find bridge support scaffold template\n" ));
		return;

	}  // end if

	// how much space is going to be between each of the scaffold objects at their final positions
	Real spacing = scaffoldTemplate->getTemplateGeometryInfo().getMajorRadius() * 2.0f;

	// how tall are the scaffold objects
	Real scaffoldHeight = scaffoldTemplate->getTemplateGeometryInfo().getMaxHeightAbovePosition() +
												scaffoldTemplate->getTemplateGeometryInfo().getMaxHeightBelowPosition();
	Real scaffoldSupportHeight = scaffoldSupportTemplate->getTemplateGeometryInfo().getMaxHeightAbovePosition() +
															 scaffoldSupportTemplate->getTemplateGeometryInfo().getMaxHeightBelowPosition();

	// get the bridge info
	BridgeInfo bridgeInfo;
	bridge->getBridgeInfo( &bridgeInfo );

	//
	// given the area of the bridge, figure out what the start and end points are to create
	// all the scaffold objects at (just in the 2D bridge plane, not thinking about
	// rising the objects up through the ground yet)
	//
	Coord3D leftStart;
	leftStart.x = ((bridgeInfo.fromLeft.x - bridgeInfo.fromRight.x) / 2.0f) + bridgeInfo.fromRight.x;
	leftStart.y = ((bridgeInfo.fromLeft.y - bridgeInfo.fromRight.y) / 2.0f) + bridgeInfo.fromRight.y;
	leftStart.z = ((bridgeInfo.fromLeft.z - bridgeInfo.fromRight.z) / 2.0f) + bridgeInfo.fromRight.z;

	Coord3D rightStart;
	rightStart.x = ((bridgeInfo.toLeft.x - bridgeInfo.toRight.x) / 2.0f) + bridgeInfo.toRight.x;
	rightStart.y = ((bridgeInfo.toLeft.y - bridgeInfo.toRight.y) / 2.0f) + bridgeInfo.toRight.y;
	rightStart.z = ((bridgeInfo.toLeft.z - bridgeInfo.toRight.z) / 2.0f) + bridgeInfo.toRight.z;

	//
	// now that we have the left and right start points, we will compute two angles to
	// use for all the objects that we will create ... objects on the left side of the
	// bridge will be given a 'leftAngle' pointing from 'leftStart' to 'rightStart' and
	// the opposite will be given to objects on the right side of the bridge
	//
	Coord2D angleV;
	angleV.x = rightStart.x - leftStart.x;
	angleV.y = rightStart.y - leftStart.y;
	Real leftAngle = angleV.toAngle();
	Real rightAngle = leftAngle + TWO_PI;

	// compute vector from left to right across bridge and the reverse
	Coord3D leftVector;
	leftVector.x = rightStart.x - leftStart.x;
	leftVector.y = rightStart.y - leftStart.y;
	leftVector.z = rightStart.z - leftStart.z;
	Coord3D rightVector;
	rightVector.x = leftStart.x - rightStart.x;
	rightVector.y = leftStart.y - rightStart.y;
	rightVector.z = leftStart.z - rightStart.z;

	//
	// how many of these scaffold objects will take to tile from each of the endpoints 
	// to the center area of the bridge
	//
	Real tileDistance = leftVector.length();
	Int numObjects = REAL_TO_INT_CEIL( tileDistance / spacing ) + 1;

	//
	// given the number of objects that we need to tile across the whole bridge, we will
	// go through the creation loop ceil( numObjects / 2.0f ) times, and each
	// time through the loop we'll create an object to move from each side of the
	// bridge, except the last object if the number of objects is odd is dead in the
	// center
	//
	Int numIterations = REAL_TO_INT_CEIL( INT_TO_REAL( numObjects ) / 2.0f );

	//
	// normalize left and right vectors as it is a vector that goes from our left start 
	// position to the destination right start position ... we will multiply this vector by a
	// spacing amount and add to the left start position to find a destination position
	// for a particular scaffold object along the bridge surface
	//
	leftVector.normalize();
	rightVector.normalize();

	// create the scaffold objects for now
	Int scaffoldObjectsCreated = 0;
	Coord3D destinationPos, *riseToPos;
	Real *angle;
	Object *obj;
	for( Int i = 0; i < numIterations; ++i )
	{

		// sanity
		DEBUG_ASSERTCRASH( scaffoldObjectsCreated < numObjects, 
											 ("Creating too many scaffold objects\n") );

		// create object
		obj = TheThingFactory->newObject( scaffoldTemplate, us->getTeam() );

		// this object is from the "left" side of the bridge
		riseToPos = &leftStart;
		angle = &leftAngle;

		//
		// compute position for object moving from the left side, we're adding 0.1 here
		// so that all scaffold objects *have* to move some distance ... that way we
		// can just assign the speeds so they line up perfectly and not have to worry
		// about any object reaching a destination before any other object
		//
		destinationPos.x = leftVector.x * (spacing * i) + riseToPos->x + 0.1f;
		destinationPos.y = leftVector.y * (spacing * i) + riseToPos->y;
		destinationPos.z = leftVector.z * (spacing * i) + riseToPos->z;

		//
		// now that they key positions are calculated, set the rest of the position data
		// and movement speeds for the object
		//
		setScaffoldData( obj, angle, &scaffoldHeight, riseToPos, &destinationPos, center );

		// keeping track of objects created
		scaffoldObjectsCreated++;
		m_scaffoldObjectIDList.push_back( obj->getID() );

		//
		// create support object layers under the scaffold object, this object mirrors the scaffold
		// object except is on a lower layer
		//
		Real offset = riseToPos->z;
		Coord3D supportRiseToPos = *riseToPos;
		Coord3D supportDestinationPos = destinationPos;
		Coord3D supportBridgeCenter = *center;
		while( offset >= 0.0f )
		{

			supportRiseToPos.z -= scaffoldSupportHeight;
			supportDestinationPos.z -= scaffoldSupportHeight;
			supportBridgeCenter.z -= scaffoldSupportHeight;
			obj = TheThingFactory->newObject( scaffoldSupportTemplate, us->getTeam() );
			setScaffoldData( obj, 
											 angle, 
											 &scaffoldSupportHeight,
											 &supportRiseToPos, 
											 &supportDestinationPos, 
											 &supportBridgeCenter );
			m_scaffoldObjectIDList.push_back( obj->getID() );


			// off to the next layer
			offset -= scaffoldSupportHeight;

		}  // end while

		//
		// now create the object from the "right" side of the bridge ... but note that
		// we don't do this on the last iteration through this loop when we have an odd
		// number of objects we're tiling because all the space is already perfectly used up)
		//
		if( scaffoldObjectsCreated < numObjects )
		{

			// sanity
			DEBUG_ASSERTCRASH( scaffoldObjectsCreated < numObjects, 
												 ("Creating too many scaffold objects\n") );

			// create new object
			obj = TheThingFactory->newObject( scaffoldTemplate, us->getTeam() );

			// this object is on the "right" side of the bridge
			riseToPos = &rightStart;
			angle = &rightAngle;

			//
			// compute position for object moving from the left side, we're adding 0.1 here
			// so that all scaffold objects *have* to move some distance ... that way we
			// can just assign the speeds so they line up perfectly and not have to worry
			// about any object reaching a destination before any other object
			//
			destinationPos.x = rightVector.x * (spacing * i) + riseToPos->x + 0.1f;
			destinationPos.y = rightVector.y * (spacing * i) + riseToPos->y;
			destinationPos.z = rightVector.z * (spacing * i) + riseToPos->z;

			// set the rest scaffold data again
			setScaffoldData( obj, angle, &scaffoldHeight, riseToPos, &destinationPos, center );

			// keeping track of objects created
			scaffoldObjectsCreated++;
			m_scaffoldObjectIDList.push_back( obj->getID() );

			//
			// create support object layers under the scaffold object, this object mirrors the scaffold
			// object except is on a lower layer
			//
			Real offset = riseToPos->z;
			Coord3D supportRiseToPos = *riseToPos;
			Coord3D supportDestinationPos = destinationPos;
			Coord3D supportBridgeCenter = *center;
			while( offset >= 0.0f )
			{

				supportRiseToPos.z -= scaffoldSupportHeight;
				supportDestinationPos.z -= scaffoldSupportHeight;
				supportBridgeCenter.z -= scaffoldSupportHeight;
				obj = TheThingFactory->newObject( scaffoldSupportTemplate, us->getTeam() );
				setScaffoldData( obj, 
												 angle, 
												 &scaffoldSupportHeight,
												 &supportRiseToPos, 
												 &supportDestinationPos, 
												 &supportBridgeCenter );
				m_scaffoldObjectIDList.push_back( obj->getID() );


				// off to the next layer
				offset -= scaffoldSupportHeight;

			}  // end while

		}  // end if

	}  // end for i	

	// scaffolding is now present
	m_scaffoldPresent = TRUE;

	// when scaffolding is present, a bridge cannot be used
	TheAI->pathfinder()->changeBridgeState( bridge->getLayer(), FALSE );

}  // end createScaffolding

// ------------------------------------------------------------------------------------------------
/** Remove the bridge scaffolding.  If we don't have any then this call is ignored */
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::removeScaffolding( void )
{

	// if we have no scaffolding, do nothing
	if( m_scaffoldPresent == FALSE )
		return;

	// go through each object and tell them to reverse course
	Object *obj;
	ObjectIDListIterator it;
	BridgeScaffoldBehaviorInterface *scaffoldBehavior;
	for( it = m_scaffoldObjectIDList.begin(); it != m_scaffoldObjectIDList.end(); ++it )
	{

		// get the object
		obj = TheGameLogic->findObjectByID( (*it) );
		if( obj == NULL )
			continue;

		// get the scaffold behavior
		scaffoldBehavior = BridgeScaffoldBehavior::getBridgeScaffoldBehaviorInterfaceFromObject( obj );
		DEBUG_ASSERTCRASH( scaffoldBehavior, ("Unable to find bridge scaffold behavior interface\n") );
		
		// reverse the motion
		scaffoldBehavior->reverseMotion();

	}  // end for, it

	// clear our scaffold object list
	m_scaffoldObjectIDList.clear();

	// scaffolding is no longer present
	m_scaffoldPresent = FALSE;

	// when scaffolding is gone, a bridge can be used again if we're not in a rubble state
	Object *us = getObject();
	BodyModuleInterface *body = us->getBodyModule();

	if( body->getDamageState() != BODY_RUBBLE )
	{
		Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() );

		if( bridge )
			TheAI->pathfinder()->changeBridgeState( bridge->getLayer(), TRUE );

	}  // end if

}  // end removeScaffolding

// ------------------------------------------------------------------------------------------------
/** Is any of the scaffolding in motion */
// ------------------------------------------------------------------------------------------------
Bool BridgeBehavior::isScaffoldInMotion( void )
{
	Object *obj;

	// go through the scaffold objects, if any of them are in motion the scaffold is in motion
	ObjectIDListIterator it;
	for( it = m_scaffoldObjectIDList.begin(); it != m_scaffoldObjectIDList.end(); ++it )
	{

		// get object
		obj = TheGameLogic->findObjectByID( (*it) );
		if( obj == NULL )
			continue;

		// get scaffold interface
		BridgeScaffoldBehaviorInterface *bsbi = BridgeScaffoldBehavior::getBridgeScaffoldBehaviorInterfaceFromObject( obj );
		if( bsbi == NULL )
			continue;

		// check in motion
		if( bsbi->getCurrentMotion() != STM_STILL )
			return TRUE;

	}  // end for

	// not in motion
	return FALSE;

}  // end isScaffoldInMotion

// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::crc( Xfer *xfer )
{

	// extend base class
	UpdateModule::crc( xfer );

}  // end crc

// ------------------------------------------------------------------------------------------------
/** Xfer method
	* Version Info:
	* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::xfer( Xfer *xfer )
{
	Object *us = getObject();

	// version
	XferVersion currentVersion = 1;
	XferVersion version = currentVersion;
	xfer->xferVersion( &version, currentVersion );

	// extend base class
	UpdateModule::xfer( xfer );

	// set us as the bridge object in the bridge info
	if( xfer->getXferMode() == XFER_LOAD )
	{
		Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() );

		// sanity
		DEBUG_ASSERTCRASH( bridge, ("BridgeBehavior::xfer - Unable to find bridge\n" ));

		// set new object ID in bridge info to us
		bridge->setBridgeObjectID( us->getID() );

	}  // end if

	// xfer the tower object ids
	for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
		xfer->xferObjectID( &m_towerID[ i ] );

	// set the tower object ids in the bridge info
	if( xfer->getXferMode() == XFER_LOAD )
	{
		Object *us = getObject();
		Bridge *bridge = TheTerrainLogic->findBridgeAt( us->getPosition() );

		// sanity
		DEBUG_ASSERTCRASH( bridge, ("BridgeBehavior::xfer - Unable to find bridge\n" ));

		// set new object ID in bridge info to us
		for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
			bridge->setTowerObjectID( m_towerID[ i ], (BridgeTowerType)i );

	}  // end if

	// scaffold present flag
	xfer->xferBool( &m_scaffoldPresent );

	// scaffold object id list
	UnsignedShort scaffoldObjectCount = 0;
	scaffoldObjectCount = m_scaffoldObjectIDList.size();
	xfer->xferUnsignedShort( &scaffoldObjectCount );
	ObjectID scaffoldObjectID;
	if( xfer->getXferMode() == XFER_SAVE )
	{
		
		// write out all object IDs
		ObjectIDListIterator it;
		for( it = m_scaffoldObjectIDList.begin(); it != m_scaffoldObjectIDList.end(); ++it )
		{

			scaffoldObjectID = *it;
			xfer->xferObjectID( &scaffoldObjectID );

		}  // end for

	}  // end if, save
	else
	{

		// read all object IDs
		DEBUG_ASSERTCRASH( m_scaffoldObjectIDList.size() == 0,
											 ("BridgeBehavior::xfer - scaffold object list should be empty\n") );
		for( Int i = 0; i < scaffoldObjectCount; ++i )
		{

			// read id
			xfer->xferObjectID( &scaffoldObjectID );

			// put on list
			m_scaffoldObjectIDList.push_back( scaffoldObjectID );

		}  // end for i

	}  // end load

	// death frame
	xfer->xferUnsignedInt( &m_deathFrame );
		
}  // end xfer

// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void BridgeBehavior::loadPostProcess( void )
{

	// extend base class
	UpdateModule::loadPostProcess();

}  // end loadPostProcess
