/* MovingObject.cpp Skaters, Peds, Cars are derived from CMovingObject. These objects are controlled through scripts. Script commands are sent through CallMemberFunction( ). See all available commands by looking at CMovingObject::CallMemberFunction(). */ // start autoduck documentation // @DOC movingobject // @module movingobject | None // @subindex Scripting Database // @index script | movingobject #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for AddDebugLine( ) #include #include #include #include #include #include #include #include namespace Obj { /******************************************************************/ /* */ /* */ /******************************************************************/ void CMovingObject::DrawBoundingBox( SBBox *pBox, Mth::Vector *pOffset, int numFrames, Mth::Vector *pRot ) { Gfx::AddDebugBox( m_matrix, m_pos, pBox, pOffset, numFrames, pRot); } /******************************************************************/ /* */ /* */ /******************************************************************/ CMovingObject::CMovingObject() { // set up default bounding box m_bbox.m_max.Set(10.0f, 10.0f, 10.0f); m_bbox.m_min.Set(-10.0f, -10.0f, -10.0f); m_bbox.centerOfGravity.Set(); // zero center of gravity...... // suspend component should come first, because it // controls whether the rest of the components // should be updated CSuspendComponent* p_suspendComponent = new CSuspendComponent; AddComponent(p_suspendComponent); } /******************************************************************/ /* */ /* */ /******************************************************************/ CMovingObject::~CMovingObject( void ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CMovingObject::MovingObjectCreateComponents() { CMotionComponent* p_motionComponent = new CMotionComponent; AddComponent(p_motionComponent); CSpecialItemComponent* p_specialItemComponent = new CSpecialItemComponent; AddComponent(p_specialItemComponent); CSkaterProximityComponent* p_skaterProximityComponent = new CSkaterProximityComponent; AddComponent( p_skaterProximityComponent ); CSoundComponent* p_soundComponent = new CSoundComponent; AddComponent(p_soundComponent); CStreamComponent* p_streamComponent = new CStreamComponent; AddComponent(p_streamComponent); CLockObjComponent* p_lockObjComponent = new CLockObjComponent; AddComponent(p_lockObjComponent); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CMovingObject::MovingObjectInit( Script::CStruct* pNodeData, CGeneralManager* p_obj_man ) { // Dan: a hack for now to detect whether anyone has called CreateComponents() yet or not // once components are created in a consistent manner across object types, we can remove this sort of foolishness if (!GetMotionComponent()) { MovingObjectCreateComponents(); } Dbg_MsgAssert( GetMotionComponent(), ( "Motion component doesn't exist yet" ) ); GetMotionComponent()->OrientToNode( pNodeData ); //---------------------------------------- // MOVED FROM POSOBJECT.H Dbg_MsgAssert( pNodeData, ( "Couldn't find node structure." )); // Get the checksum ... uint32 NodeNameChecksum = 0; pNodeData->GetChecksum(CRCD(0xa1dc81f9,"Name"),&NodeNameChecksum); if (NodeNameChecksum) { SetID(NodeNameChecksum); } uint32 AIScriptChecksum=0; if ( pNodeData->GetChecksum(CRCD(0x2ca8a299,"TriggerScript"),&AIScriptChecksum) ) { Script::CScriptStructure *pScriptParams=NULL; pNodeData->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptParams); SwitchScript( AIScriptChecksum, pScriptParams ); } //---------------------------------------- GetMotionComponent()->InitFromStructure( pNodeData ); GetSuspendComponent()->InitFromStructure( pNodeData ); } /******************************************************************/ /* */ /* */ /******************************************************************/ Gfx::CSkeleton* CMovingObject::GetSkeleton() { Obj::CSkeletonComponent* p_skeleton_component = GetSkeletonComponent(); if ( p_skeleton_component ) { return p_skeleton_component->GetSkeleton(); } return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ Nx::CModel* CMovingObject::GetModel() { Obj::CModelComponent* p_model_component = GetModelComponent(); if ( p_model_component ) { return p_model_component->GetModel(); } return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::LookAtObject_Init( Script::CStruct* pParams, Script::CScript* pScript ) { uint32 typeChecksum; if ( pParams->GetChecksum( 0x7321a8d6, &typeChecksum ) ) // "type" { int type; switch ( typeChecksum ) { case 0x88c21962: // car type = SKATE_TYPE_CAR; break; case 0x61a741e: // ped type = SKATE_TYPE_PED; break; case 0x5b8ab877: // skater type = SKATE_TYPE_SKATER; break; case 0x3b5737a6: // gameobj type = SKATE_TYPE_GAME_OBJ; break; default: Dbg_MsgAssert( 0,( "\n%s\nUnknown type %s", pScript->GetScriptInfo( ), Script::FindChecksumName( typeChecksum ) )); return false; break; } CCompositeObject* pClosestObject = GetClosestObjectOfType( type ); if ( !pClosestObject ) { Dbg_Message( "\n%s\nWarning: Looking at object that doesn't exist.", pScript->GetScriptInfo( ) ); return false; } return GetMotionComponent()->LookAt_Init( pParams, pClosestObject->m_pos ); } // find a named object: uint32 nameChecksum; if ( pParams->GetChecksum( 0xa1dc81f9, &nameChecksum ) ) // "name" { CCompositeObject* pNext; CCompositeObject* pObj; Lst::Search< CObject > sh; Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject")); pNext = (Obj::CCompositeObject*) sh.FirstItem( mp_manager->GetRefObjectList() ); while ( pNext ) { Dbg_AssertType( pNext, CCompositeObject ); pObj = pNext; pNext = (Obj::CCompositeObject*) sh.NextItem(); // find objects spawned from this node, return true if out of this radius... if ( ( pObj != this ) && ( pObj->GetID() == nameChecksum ) ) { return GetMotionComponent()->LookAt_Init( pParams, pObj->m_pos ); } } Dbg_Message( "\n%s\nWarning: Looking at object that doesn't exist.", pScript->GetScriptInfo( ) ); return false; } Dbg_Message( "\n%s\nWarning: No object specified for Obj_LookAtObject", pScript->GetScriptInfo( ) ); return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::ObjectFromNodeWithinRange( int nodeIndex, int radiusSqr, SiteBox* pBox ) { uint32 nodeNameChecksum = SkateScript::GetNodeNameChecksum(nodeIndex); // TODO: Since there can only be one object from a node, then should be able to get it directly bool retVal = false; CMovingObject* pNext; CMovingObject* pObj; Lst::Search< CObject > sh; // Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject. Node Checksum = 0x%x (%s)",GetID(),Script::FindChecksumName(GetID()))); Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject. Node Checksum = 0x%x (%s)",GetID(),Script::FindChecksumName(GetID()))); pNext = (Obj::CMovingObject *) sh.FirstItem( mp_manager->GetRefObjectList() ); while ( pNext ) { Dbg_AssertType( pNext, CMovingObject ); pObj = (Obj::CMovingObject *) pNext; pNext = (Obj::CMovingObject *) sh.NextItem(); if ( ( pObj->GetID() == nodeNameChecksum ) && ( pObj != this ) ) { if ( pBox ) { if ( ObjInSiteBox( pObj, pBox ) ) { retVal = true; } } else if ( Mth::DistanceSqr( pObj->m_pos, m_pos ) < radiusSqr ) { retVal = true; } } } return ( retVal ); } /******************************************************************/ /* */ /* */ /******************************************************************/ float CMovingObject::GetSiteBoxMaxRadiusSquared( SiteBox* pBox ) { float maxBoxDist; if ( pBox->maxDistInitialized ) { return ( pBox->maxDistSquared ); } maxBoxDist = fabsf( pBox->dist ); if ( fabsf( pBox->height ) > maxBoxDist ) { maxBoxDist = fabsf( pBox->height ); } if ( fabsf( pBox->width ) > maxBoxDist ) { maxBoxDist = fabsf( pBox->width ); } float maxOffset; Mth::Vector* pOffset; pOffset = &pBox->bbox.centerOfGravity; maxOffset = fabsf( pOffset->GetZ( ) ); if ( fabsf( pOffset->GetY( ) ) > maxOffset ) { maxOffset = fabsf( pOffset->GetY( ) ); } if ( fabsf( pOffset->GetX( ) ) > maxOffset ) { maxOffset = fabsf( pOffset->GetX( ) ); } pBox->maxDistSquared = maxOffset + maxBoxDist; pBox->maxDistSquared *= pBox->maxDistSquared; pBox->maxDistInitialized = true; return ( pBox->maxDistSquared ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CMovingObject::InitializeSiteBox( SiteBox* pBox ) { if ( pBox->initialized ) { // already initialized! return; } pBox->matrix = m_matrix; if ( pBox->angle ) { pBox->matrix = pBox->matrix.RotateYLocal( pBox->angle ); } pBox->matrix.Invert( ); SBBox* pBBox = &pBox->bbox; // start by setting min/max to half the dimentions: pBBox->m_max.Set( pBox->width / 2.0f, pBox->height / 2.0f, pBox->dist ); pBBox->m_min = -pBBox->m_max; pBBox->m_min[ Z ] = 0.0f; // using the vector in pBox->bbox.centerOfGravity to store offset // sent by the player! MISNOMER: pBBox->m_max += pBBox->centerOfGravity; pBBox->m_min += pBBox->centerOfGravity; // gots ta move the site box up so that it comes from the object's // center of gravity (as the object's position isn't usually the center // of gravity): /* if ( mp_model ) { //Mth::Vector temp3; //temp3 = m_pos + centerOfGravity; //Gfx::AddDebugLine( m_pos, temp3 ); pBBox->m_max += m_bbox.centerOfGravity; pBBox->m_min += m_bbox.centerOfGravity; }*/ #ifndef __PLAT_NGC__ #ifdef __NOPT_ASSERT__ if ( pBox->debug ) { pBBox->centerOfGravity.Set( ); // zero that out for the render... Mth::Vector drawOffset; // drawOffset.Set( 0, m_ground_offset, 0 ); drawOffset.Set( 0, 0, 0 ); if ( pBox->angle ) { Mth::Vector angles; angles.Set( 0, pBox->angle, 0 ); DrawBoundingBox( pBBox, &drawOffset, 10, &angles ); } else { DrawBoundingBox( pBBox, &drawOffset, 10 ); } } #endif #endif // Phew... now we can quickly check objects by rotating their position // into our world, and seeing if they're in the motherfuckin' box: pBox->initialized = true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::ObjInSiteBox( CMovingObject* pObj, SiteBox* pBox ) { // K: There was a bug whereby the initial distance check below was not taking // into account the offset of the box. // The offset is stored in centerOfGravity which may get zeroed in InitializeSiteBox, // so store it here. Mth::Vector off=pBox->bbox.centerOfGravity; // initialize the site box and the BBox contained therein: InitializeSiteBox( pBox ); // sorta trivial radius check and return... float distSquared; distSquared = Mth::DistanceSqr( pObj->m_pos+off, m_pos ); float boxRadiusSquared = GetSiteBoxMaxRadiusSquared( pBox ); if ( distSquared > boxRadiusSquared ) { return false; } Mth::Vector objPos; objPos = pObj->m_pos; // center of the object... objPos[ Y ] += ( pObj->m_bbox.m_max[ Y ] - pObj->m_bbox.m_min[ Y ] ) / 2.0f; #ifndef __PLAT_NGC__ #ifdef __NOPT_ASSERT__ Mth::Vector preTransform = objPos; #endif #endif objPos -= m_pos; // now we have the head and toes at the right distance from us and from each other... // rotate them into our world: objPos = pBox->matrix.Transform( objPos ); #ifndef __PLAT_NGC__ #ifdef __NOPT_ASSERT__ if ( pBox->debug ) { if ( Gfx::PointInsideBox( objPos, pBox->bbox.m_max, pBox->bbox.m_min ) ) { Gfx::AddDebugLine( m_pos, preTransform, MAKE_RGB( 150, 50, 50 ), MAKE_RGB( 150, 50, 50 ), 10 ); return true; } else { Gfx::AddDebugLine( m_pos, preTransform, MAKE_RGB( 50, 50, 100 ), MAKE_RGB( 50, 50, 100 ), 10 ); return false; } } #endif #endif return ( Gfx::PointInsideBox( objPos, pBox->bbox.m_max, pBox->bbox.m_min ) ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CMovingObject::FillInSiteBox(SiteBox* p_siteBox, Script::CStruct* pParams) { Dbg_MsgAssert(p_siteBox,("NULL p_siteBox")); Dbg_MsgAssert(pParams,("NULL pParams")); p_siteBox->initialized = false; p_siteBox->angle = 0; p_siteBox->maxDistInitialized = false; // Misnomer: used for offsetting the site box... // But we will want it to default to the center of gravity: p_siteBox->bbox.centerOfGravity = m_bbox.centerOfGravity; p_siteBox->height = p_siteBox->width = p_siteBox->dist = 0.0f; Mth::Vector offset; offset[W]=0.0f; // if ( pParams->GetVector( 0xa6f5352f, &offset ) ) // offset { offset.FeetToInches( ); p_siteBox->bbox.centerOfGravity += offset; } if ( pParams->GetFloat( 0xff7ebaf6, &p_siteBox->angle ) ) // angle { p_siteBox->angle = DEGREES_TO_RADIANS( p_siteBox->angle ); } if ( pParams->GetFloat( 0xab21af0, &p_siteBox->height ) ) // height { p_siteBox->height = FEET_TO_INCHES( p_siteBox->height ); } if ( pParams->GetFloat( 0x7e832f08, &p_siteBox->dist ) ) // dist { p_siteBox->dist = FEET_TO_INCHES( p_siteBox->dist ); } if ( pParams->GetFloat( 0x73e5bad0, &p_siteBox->width ) ) // width { p_siteBox->width = FEET_TO_INCHES( p_siteBox->width ); } #ifdef __NOPT_ASSERT__ p_siteBox->debug = 0; p_siteBox->debug = pParams->ContainsFlag( 0x935ab858 ); // debug #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::ObjectWithinRect( Script::CStruct* pParams, Script::CScript* pScript ) { SiteBox siteBox; FillInSiteBox(&siteBox,pParams); return ( ObjectWithinRange( pParams, pScript, &siteBox ) ); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::SkaterInRange( SiteBox* pBox, float radiusSqr ) { int numSkaters = Mdl::Skate::Instance()->GetNumSkaters( ); for ( int i = 0; i < numSkaters; i++ ) { CMovingObject* pObj = Mdl::Skate::Instance()->GetSkater( i ); if ( pObj != this ) { if ( pBox ) { if ( ObjInSiteBox( pObj, pBox ) ) { return true; } } else if ( Mth::DistanceSqr( pObj->m_pos, m_pos ) < radiusSqr ) { return true; } } } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::ObjTypeInRange( Script::CStruct* pParams, Script::CScript* pScript, float radiusSqr, SiteBox* pBox, uint32 typeChecksum ) { int type; Search objSearch; CMovingObject* pObj; switch ( typeChecksum ) { case 0x88c21962: // car type = SKATE_TYPE_CAR; break; case 0x61a741e: // ped type = SKATE_TYPE_PED; break; case 0x5b8ab877: // skater return ( SkaterInRange( pBox, radiusSqr ) ); break; case 0x3b5737a6: // gameobj type = SKATE_TYPE_GAME_OBJ; break; default: Dbg_MsgAssert( "\n%s\nUnknown type %s",((char *)( pScript->GetScriptInfo( ), Script::FindChecksumName( typeChecksum ) ))); return false; break; } Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject")); pObj = (Obj::CMovingObject *) objSearch.FindFirstObjectOfType( mp_manager->GetRefObjectList(), type ); while ( pObj ) { if ( pObj != this ) { if ( pBox ) { if ( ObjInSiteBox( pObj, pBox ) ) { return true; } } else if ( Mth::DistanceSqr( pObj->m_pos, m_pos ) < radiusSqr ) { return true; } } pObj = (Obj::CMovingObject *) objSearch.FindNextObjectOfType( ); } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::ObjectWithinRange( Script::CStruct* pParams, Script::CScript* pScript, SiteBox* pBox ) { float radiusSqr = 0.0f; if ( !pBox ) { if( !( pParams->GetFloat( 0xc48391a5, &radiusSqr ) ) )//"radius" { Dbg_MsgAssert( 0,( "\n%s\nNo radius specified in Obj_*InRadius.", pScript->GetScriptInfo( ) )); return false; } radiusSqr = FEET_TO_INCHES( radiusSqr ); radiusSqr *= radiusSqr; } Script::CArray* pArray=NULL; uint32 typeChecksum; if ( pParams->GetChecksum( 0x7321a8d6, &typeChecksum ) ) // "type" { return ( ObjTypeInRange( pParams, pScript, radiusSqr, pBox, typeChecksum ) ); } else if ( pParams->GetArray( 0x7321a8d6, &pArray, Script::ASSERT ) ) // "type" { Dbg_MsgAssert( pArray->GetType( ) == ESYMBOLTYPE_NAME,( "\n%s\nObjectWithinRange: Array must be of names",pScript->GetScriptInfo())); uint32 i; for ( i = 0; i < pArray->GetSize( ); ++i ) { if ( ObjTypeInRange( pParams, pScript, radiusSqr, pBox, pArray->GetChecksum( i ) ) ) { return true; } } return false; } // find a named object: uint32 nameChecksum; if ( pParams->GetChecksum( 0xa1dc81f9, &nameChecksum ) ) // "name" { int nodeNum = SkateScript::FindNamedNode( nameChecksum ); return ( ObjectFromNodeWithinRange( nodeNum, radiusSqr, pBox ) ); } const char* pPrefix; if ( pParams->GetText( 0x6c4e7971, &pPrefix ) ) // checksum 'prefix' { // Create with a prefix specified: uint16 numNodes = 0; const uint16* pMatchingNodes = SkateScript::GetPrefixedNodes( pPrefix, &numNodes ); int i; for ( i = 0; i < numNodes; i++ ) { if ( ObjectFromNodeWithinRange( pMatchingNodes[ i ], radiusSqr, pBox ) ) { //printf("Oof old\n"); return true; } } return false; } // Dbg_Message( "\n%s\nWarning: Unprocessed radius ( or site box ) check... unrecognized syntax. needs 'type', 'name' or 'prefix'", pScript->GetScriptInfo( ) ); return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CMovingObject::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript ) { switch (Checksum) { // @script | Obj_LookAtObject | // Returns true if the object will rotate itself to point in the required direction. Ie, if // the object is already pointing the right way, the command will return false. // @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z) // @parmopt float | time | vel/acceleration | in seconds // @parmopt float | speed | 360 | angular velocity in degrees per second // @parmopt float | acceleration | 0 | angular acceleration in degrees per second // per second // @parmopt name | name | | name of object // @parmopt name | type | | type of object to look at - will look at closest if // there are several in the world case 0x33e04032: // Obj_LookAtObject return LookAtObject_Init( pParams, pScript ); break; case 0x37185d8a: // Obj_AngleToNearestSkaterGreaterThan { int type = SKATE_TYPE_SKATER; Obj::CCompositeObject* pClosestObject = GetClosestObjectOfType( type ); if ( !pClosestObject ) { Dbg_Message( "\n%s\nWarning: Checking angle to skater that doesn't exist.", pScript->GetScriptInfo() ); return false; break; } float threshold; pParams->GetFloat( NONAME, &threshold, Script::ASSERT ); Dbg_MsgAssert( threshold >= 0, ( "Negative angle passed to Obj_AngleToNearestSkaterGreaterThan" ) ); threshold = DEGREES_TO_RADIANS( threshold ); Mth::Vector pathHeading = pClosestObject->m_pos - m_pos; float test = Mth::GetAngle( m_matrix, pathHeading, 2, 1 ); if ( fabs( test ) > threshold ) { return true; break; } return false; break; } // @script | Obj_ObjectInRadius | // @parmopt name | name | | use one of: name, prefix, or type // @parmopt name | prefix | | use one of: name, prefix, or type // @parmopt name | type | | use one of: name, prefix, or type // (type values are skater, ped, car) // @parm float | radius | radius in feet case 0x67660fe5: // Obj_ObjectInRadius return ( ObjectWithinRange( pParams, pScript ) ); break; // @script | Obj_ObjectInRect | // @parmopt float | angle | 0 | in degrees - default is // along the direction the object is facing // @parmopt float | height | (height of object) | // @parmopt float | distance | (length of object) | in feet - // offset by half of the object in the direction determined by the angle // @parmopt float | width | (width of object) | in feet // @parmopt vector | offset | | in feet - the rectangle by default will be located // sticking out of the XZ plane (or XY plane in Max coords ), // rotated by number of degrees in angle parameter // @flag debug | just a parameter to turn on debugging info, // to quickly set up rectangles on an object. // @parmopt name | name | | use one of: name, prefix, or type // @parmopt name | prefix | | use one of: name, prefix, or type // @parmopt name | type | | use one of: name, prefix, or type // (type values are skater, ped, car) case 0x6520b902: // Obj_ObjectInRect { return ( ObjectWithinRect( pParams, pScript ) ); break; } // @script | Obj_ObjectNotInRect | // @parmopt float | angle | 0 | in degrees - default is // along the direction the object is facing // @parmopt float | height | (height of object) | // @parmopt float | distance | (length of object) | in feet - // offset by half of the object in the direction determined by the angle // @parmopt float | width | (width of object) | in feet // @parmopt vector | offset | | in feet - the rectangle by default will be located // sticking out of the XZ plane (or XY plane in Max coords ), // rotated by number of degrees in angle parameter // @flag debug | just a parameter to turn on debugging info, // to quickly set up rectangles on an object. // @parmopt name | name | | use one of: name, prefix, or type // @parmopt name | prefix | | use one of: name, prefix, or type // @parmopt name | type | | use one of: name, prefix, or type // (type values are skater, ped, car) case 0x3b6b66c3: // Obj_ObjectNotInRect return ( !ObjectWithinRect( pParams, pScript ) ); break; // @script | Obj_SetBodyShape | Sets the body shape for this object // @parm structure | body_shape | a list of per-bone scales (see scaling.q for format) case 0x802a7600: // Obj_SetBodyShape { Script::CStruct* pBodyShapeStructure; pParams->GetStructure( CRCD(0x812684ef,"body_shape"), &pBodyShapeStructure, true ); if ( GetModel() ) { Gfx::CSkeleton* pSkeleton = GetSkeleton(); Dbg_Assert( pSkeleton ); pSkeleton->ApplyBoneScale( pBodyShapeStructure ); Mth::Vector theScale( 1.0f, 1.0f, 1.0f ); if ( Gfx::GetScaleFromParams( &theScale, pBodyShapeStructure ) ) { GetModel()->SetScale( theScale ); } } } break; default: return ( CCompositeObject::CallMemberFunction( Checksum, pParams, pScript ) ); break; } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ CCompositeObject* CMovingObject::GetClosestObjectOfType( int type ) { Search objSearch; CCompositeObject *pObj; pObj = (CCompositeObject *) objSearch.FindFirstObjectOfType( mp_manager->GetRefObjectList(), type ); if ( pObj == this ) { if ( !( pObj = (CCompositeObject*) objSearch.FindNextObjectOfType( ) ) ) { return ( NULL ); } } if ( !( pObj ) ) { return ( NULL ); } float closestDist = FEET_TO_INCHES( 666.0f * 666.0f ); float dist; CCompositeObject* pClosestObj = pObj; while ( pObj ) { dist = Mth::Distance( pObj->m_pos, m_pos ); if ( ( pObj != this ) && ( dist < closestDist ) ) { closestDist = dist; pClosestObj = pObj; } pObj = (CCompositeObject*) objSearch.FindNextObjectOfType( ); } return ( pClosestObj ); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Obj