//============================================================================= // Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. // // File: pedestrianmanager.cpp // // Description: Implements Pedestrian Manager class // // History: 09/18/2002 + Created -- Dusit Eakkachaichanvet // //============================================================================= #define USEAVATARPOS #include // for rAssert & other debug print outs #include #include #include // for the Profiler #include // for rmt::Vector #include #include #include #include #include #include /* #include #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include // in meters, how much to translate spawn & remove radii forward // in the direction of the camera lookat //#define PED_TEST #ifdef PED_TEST const float PedestrianManager::FADE_RADIUS = 5.0f; // in meters const float PedestrianManager::CENTER_OFFSET = 5.0f; // in meters const float PedestrianManager::ADD_RADIUS = 10.0f; // in meters const float PedestrianManager::REMOVE_RADIUS = 15.0f; // in meters const float PedestrianManager::INITIAL_ADD_RADIUS = 10.0f; // in meters #else const float PedestrianManager::FADE_RADIUS = 60.0f; // in meters const float PedestrianManager::CENTER_OFFSET = 30.0f; // in meters const float PedestrianManager::ADD_RADIUS = 45.0f; // in meters const float PedestrianManager::REMOVE_RADIUS = 50.0f; // in meters const float PedestrianManager::INITIAL_ADD_RADIUS = 90.0f; // in meters #endif const unsigned int PedestrianManager::MILLISECONDS_PER_GROUND_INTERSECT = 10; const unsigned int PedestrianManager::MILLISECONDS_BETW_ADDS = 1; const unsigned int PedestrianManager::MILLISECONDS_BETW_REMOVES = 1000; const unsigned int PedestrianManager::MILLISECONDS_POPULATE_WORLD = 3000; int PedestrianManager::mDefaultModelGroup = 0; // *********************** STATICS *********************** PedestrianManager* PedestrianManager::mInstance = NULL; PedestrianManager* PedestrianManager::GetInstance() { if( mInstance == NULL ) { mInstance = new(GMA_LEVEL_OTHER) PedestrianManager(); } return mInstance; } void PedestrianManager::DestroyInstance() { if( mInstance != NULL ) { mInstance->mFreePeds.Clear(); mInstance->mActivePeds.Clear(); // Clean up Pedestrians. int i = 0; for( i=0; imPeds[i]); rAssert( pedStr != NULL ); Pedestrian* ped = pedStr->ped; rAssert( ped != NULL ); if( ped->GetIsActive() ) { ped->Deactivate(); } } // Delete self delete mInstance; } mInstance = NULL; } // ******************** PUBLICS ************************* // call this in loading context void PedestrianManager::InitDefaultModelGroups() { // initialize all model groups with a default group // This is why we put PedManager::Init in LoadingContext, after // CharacterManager::PreLoad and GamePlayManager::LoadBlahBlah, // cuz GamePlayManager will want to parse the level script which // will register the modelgroups... for( int i=0; iped; rAssert( ped != NULL ); char name[5]; sprintf( name, "ped%d", i ); name[4] = '\0'; // call AddCharacter with an initial, lightweight dummy model so // we can init all the sim objects, the animation drivers, etc. Character* character = ::GetCharacterManager()->AddCharacter( CharacterManager::NPC, name, "npd", "npd" ); character->SetRole(Character::ROLE_PEDESTRIAN); rAssert( character != NULL ); character->AddRef(); character->SetController( pedStr->ped ); character->mbAllowUnload = false; pedStr->ped->SetCharacter( character ); int nameLen = strlen("npd"); rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN ); strncpy( pedStr->modelName, "npd", PedestrianManager::MAX_STRING_LEN ); pedStr->modelName[nameLen] = '\0'; ped->InitZero(); // add ped to list of free peds pedStr->listIndex = mFreePeds.AddLast( pedStr ); rAssert( pedStr->listIndex != -1 ); } } void PedestrianManager::Update( unsigned int milliseconds ) { BEGIN_PROFILE( "Pedestrian Manager" ); //rTunePrintf( "<<<<<<<<<<<<<<<<<<<<<<< NUM ACTIVE PEDS %d >>>>>>>>>>>>>>>>>>>>>>\n", mActivePeds.GetNumElems() ); PedestrianStruct* pedStr = NULL; int i = 0; // // ================================ // Get information about the player // ================================ // rmt::Vector pPos, pVel;//, pDir; float pSpeed; Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 ); rAssert( avatar != NULL ); // Avatar::GetPosition() will return position of vehicle if avatar inside // vehicle. Same deal with Avatar::GetVelocity() & GetSpeedMps() // Remember that we should use VELOCITY rather than facing because // the player can face one way and travel in reverse. // avatar->GetPosition(pPos); avatar->GetVelocity(pVel); pSpeed = avatar->GetSpeedMps(); /* SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam(); rAssert( superCam != NULL ); superCam->GetPosition( &pPos ); superCam->GetVelocity( &pVel ); pSpeed = pVel.Magnitude(); // *** SQUARE ROOT! *** */ int debugCount, debugExpCount; // Get the camera for Player 1. // It's what we need to apply the center offset to our spawn & remove radii SuperCam* pCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam(); rmt::Vector camTarget; pCam->GetHeadingNormalized( &camTarget ); rAssert( rmt::Epsilon(camTarget.MagnitudeSqr(), 1.0f, 0.0005f) ); rmt::Vector center; if( mMillisecondsPopulateWorld > 0 ) { center = pPos; } else { center = pPos + camTarget * PedestrianManager::CENTER_OFFSET; } // ==================================== // Update PedestrianManager properties // ====================================== if( mMillisecondsPopulateWorld > milliseconds ) { mMillisecondsPopulateWorld -= milliseconds; } else { mMillisecondsPopulateWorld = 0; } static bool remove = true; remove = !remove; // // ================================================== // Remove active pedestrians that are outside radius // ================================================== // if( remove && (mMillisecondsPopulateWorld == 0)) { mMillisecondsTillRemove = PedestrianManager::MILLISECONDS_BETW_REMOVES; debugCount = 0; debugExpCount = mActivePeds.GetNumElems(); int j = mActivePeds.GetHead(); while( 0 <= j && j < DListArray::MAX_ELEMS ) { debugCount++; pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j ); rAssert( pedStr != NULL ); // Do radius test on character's bounding sphere // versus pedestrian radius... // bool remove = false; rmt::Vector pedPos; pedStr->ped->GetCharacter()->GetPosition( pedPos ); float distSqr = (center - pedPos).MagnitudeSqr(); float zoneDistSqr = PedestrianManager::REMOVE_RADIUS * PedestrianManager::REMOVE_RADIUS; if( distSqr >= zoneDistSqr ) { remove = true; } // Do frustrum test too... if outside our frustrum, remove it within n seconds const float MAX_OUT_OF_SIGHT_MILLISECONDS = 2000; if( !pedStr->ped->GetCharacter()->mbInAnyonesFrustrum && pedStr->ped->mMillisecondsOutOfSight > MAX_OUT_OF_SIGHT_MILLISECONDS ) { remove = true; pedStr->ped->mMillisecondsOutOfSight = 0; } j = mActivePeds.GetNextOf( j ); if( remove ) { #ifdef RAD_DEBUG CheckModelCounts(); #endif DeactivatePed( pedStr ); #ifdef RAD_DEBUG CheckModelCounts(); #endif break; } } // rAssert( debugCount == debugExpCount ); } else { // // ==================== // Add Pedestrians // ==================== // if( mMillisecondsPopulateWorld > 0 ) { AddPeds( center, PedestrianManager::INITIAL_ADD_RADIUS ); } else { AddPeds( center, PedestrianManager::ADD_RADIUS ); } } // // ================================= // Update active pedestrians: // - fade them as necessary // ================================= // i = mActivePeds.GetHead(); while( 0 <= i && i < DListArray::MAX_ELEMS ) { pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(i); rAssert( pedStr != NULL ); if( pedStr->ped->GetIsActive() ) { //////////////////////////////////////////////////// // Update out of sight stuff if( !pedStr->ped->GetCharacter()->mbInAnyonesFrustrum ) { pedStr->ped->mMillisecondsOutOfSight += milliseconds; } else { pedStr->ped->mMillisecondsOutOfSight = 0; } //////////////////////////////////////////////////// // Set fade if getting near fringe... // rmt::Vector myPos; pedStr->ped->GetCharacter()->GetPosition( myPos ); float dist2Player = (pPos - myPos).Length(); // *** SQUARE ROOT! *** float fadeMinLimit = PedestrianManager::FADE_RADIUS; float fadeMaxLimit = PedestrianManager::ADD_RADIUS + PedestrianManager::CENTER_OFFSET; int fadeAlpha = 255; if( fadeMinLimit <= dist2Player && dist2Player <= fadeMaxLimit ) { // if we're in the fading zone, gotta change fade alpha float fadeRatio = (dist2Player - fadeMinLimit)/(fadeMaxLimit - fadeMinLimit); fadeAlpha = (int) (255.0f * (1.0f - fadeRatio)); } else if( dist2Player > fadeMaxLimit ) { fadeAlpha = 0; } pedStr->ped->GetCharacter()->SetFadeAlpha( fadeAlpha ); } i = mActivePeds.GetNextOf( i ); } END_PROFILE( "Pedestrian Manager" ); } void PedestrianManager::RemovePed( Character* character ) { if( character == NULL ) { return; } int j = mActivePeds.GetHead(); while( 0 <= j && j < DListArray::MAX_ELEMS ) { PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j ); rAssert( pedStr != NULL ); if( pedStr->ped->GetCharacter() == character ) { #ifdef RAD_DEBUG CheckModelCounts(); #endif DeactivatePed( pedStr ); #ifdef RAD_DEBUG CheckModelCounts(); #endif return; } j = mActivePeds.GetNextOf( j ); } } void PedestrianManager::RemoveAllPeds() { int j = mActivePeds.GetHead(); while( 0 <= j && j < DListArray::MAX_ELEMS ) { PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j ); rAssert( pedStr != NULL ); j = mActivePeds.GetNextOf( j ); #ifdef RAD_DEBUG CheckModelCounts(); #endif DeactivatePed( pedStr ); #ifdef RAD_DEBUG CheckModelCounts(); #endif } } void PedestrianManager::AllowAddingPeds( bool allowed ) { mAllowAdd = allowed; } // ************************ PRIVATES ************************* PedestrianManager::PedestrianManager() : mMillisecondsTillRemove( 0 ), mMillisecondsTillAdd( 0 ), mMillisecondsPopulateWorld( 0 ), mNumRegisteredModels( 0 ), mCurrGroupID( -1 ), mNumActiveModels( 0 ), mAllowAdd( true ) { // check some stuff once per compile.. rAssert( PedestrianManager::ADD_RADIUS > 0 ); rAssert( PedestrianManager::REMOVE_RADIUS > 0 ); rAssert( PedestrianManager::MAX_STRING_LEN >= 6 ); rAssert( 1 <= PedestrianManager::MAX_MODELS ); rAssert( 0 <= PedestrianManager::MAX_PEDESTRIANS && PedestrianManager::MAX_PEDESTRIANS <= DListArray::MAX_ELEMS ); // listen to appropriate events GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::LOAD_PED_MODEL_GROUP ) ); GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED ); GetEventManager()->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC ); GetEventManager()->AddListener( this, EVENT_OBJECT_KICKED ); GetEventManager()->AddListener( this, EVENT_PLAYER_VEHICLE_HORN ); /*** DEBUG *** GetEventManager()->AddListener( this, (EventEnum)(EVENT_GETOUTOFVEHICLE_END) ); *** DEBUG ***/ for( int i=0; iAddRef(); mPeds[i].listIndex = -1; } } PedestrianManager::~PedestrianManager() { GetEventManager()->RemoveAll( this ); int i; for( i=0; iGetCharacter(); if( character != NULL ) { // Just release the pointer; don't bother calling // RemoveCharacter(). The character gets cleaned up // later by CharacterManager's destructor character->Release(); } mPeds[i].ped->Release(); } } } void PedestrianManager::DeactivatePed( PedestrianStruct* pedStr ) { rAssert( pedStr != NULL ); rAssert( pedStr->ped != NULL ); rAssert( 1 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE ); //rDebugPrintf( "*** Removing %s\n", pedStr->modelName ); // remove ped from path Path* path = pedStr->ped->GetPath(); rAssert( path != NULL ); bool res = path->RemovePedestrian(); rAssert( res ); // reset ped parameters pedStr->ped->Deactivate(); #ifdef RAD_DEBUG CheckModelCounts(); #endif // If we can find the model in list of currently registered models, // decrement count. The reason we need to search is that the model // can be unregistered and re-registered in a different location. // If we can't find it anywhere, then it must have been unregistered. int modelID = GetModelIDByName( pedStr->modelName ); if( modelID != -1 ) { mModels[modelID].currCount--; } else { rDebugPrintf( " Not found in model group, so I'll swap in npd!\n" ); #ifdef RAD_DEBUG SanityCheck(); #endif // we're swapping out the model for npd... better update the modelsinuse list FindModelInUseAndRemove( pedStr->modelName ); // since this model has been unregistered, it's not in the current // model group, so keeping it around will only waste memory... // we need to swap in "npd" in its place. Character* character = pedStr->ped->GetCharacter(); rAssert( character != NULL ); ::GetCharacterManager()->SwapData( character, "npd", "npd" ); sprintf( pedStr->modelName, "npd" ); #ifdef RAD_DEBUG SanityCheck(); #endif } // manage the ped manager's active/free lists res = mActivePeds.Remove( pedStr->listIndex ); rAssert( res ); pedStr->listIndex = mFreePeds.AddLast( pedStr ); rAssert( pedStr->listIndex > -1 ); #ifdef RAD_DEBUG CheckModelCounts(); #endif } float PedestrianManager::GetRandomMinDistSqr() { static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_A = 16.0f; static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_B = 49.0f; static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_C = 144.0f; int coinflip = rand() % 3; float mindistsqr = 0.0f; switch( coinflip ) { case 0: { mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_A; } break; case 1: { mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_B; } break; case 2: { mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_C; } break; default: { mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_A; } break; } return mindistsqr; } bool PedestrianManager::ActivatePed( PathSegment* segment, rmt::Vector spawnPos ) { rAssert( segment != NULL ); PedestrianStruct* pedStr = NULL; Pedestrian* ped = NULL; // get the path for this pedestrian & try to add ped to it & // store index Path* path = segment->GetParentPath(); rAssert( path != NULL ); // Check to make sure it isn't already at max ped limit if( path->IsFull() ) { return false; } /* // Make sure we're not too close to another ped... float minDistSqr = GetRandomMinDistSqr(); rmt::Vector pedPos; int i = mActivePeds.GetHead(); while( 0 <= i && i < DListArray::MAX_ELEMS ) { pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(i); rAssert( pedStr != NULL ); pedStr->ped->GetCharacter()->GetPosition( &pedPos ); pedPos.y = spawnPos.y; // ignore diff in y if( (pedPos-spawnPos).MagnitudeSqr() < minDistSqr ) { return false; } i = mActivePeds.GetNextOf( i ); } */ // make sure we're not spawning too close to another character // or vehicle float minDistSqr = GetRandomMinDistSqr(); float distSqr = 100000.0f; rmt::Vector hisPos; CharacterManager* cm = GetCharacterManager(); int maxChars = cm->GetMaxCharacters(); for( int i=0; iGetCharacter(i); if( c ) { c->GetPosition( hisPos ); hisPos.y = spawnPos.y; // ignore diff in y distSqr = (hisPos - spawnPos).MagnitudeSqr(); if( distSqr < minDistSqr ) { return false; } } } VehicleCentral* vc = GetVehicleCentral(); for( int i=0; iGetVehicle(i); if( v ) { v->GetPosition( &hisPos ); hisPos.y = spawnPos.y; // ignore diff in y rmt::Sphere s; v->GetBoundingSphere( &s ); distSqr = (hisPos - spawnPos).MagnitudeSqr(); minDistSqr = s.radius + 1.0f; minDistSqr *= minDistSqr; if( distSqr < minDistSqr ) { return false; } } } // Choose a model for our new pedestrian int modelID = GetRandomModel(); if( modelID <= -1 ) { return false; } //*** Test *** //rDebugPrintf( "Model randomly chosen: %d, %s\n", modelID, mModels[modelID].name ); //*** Test *** // if reached maxcount for this model, go to next model and use it instead bool foundUsable = false; for( int i=0; imodelName, mModels[modelID].name)==0 ) { foundInFreeList = true; pedStrFoundInFreeList = pedStr; //rDebugPrintf( "*** Yes! We found an existing model %s in the freelist!\n", pedStr->modelName ); //break; } else { if( strcmp(pedStr->modelName, "npd") != 0 && GetModelIDByName( pedStr->modelName ) == -1 ) { freeNonNPDNotInCurrModelGroup = pedStr; } } j = mFreePeds.GetNextOf( j ); } // if none of the freepeds has the model we want, we need // to swap out one of them and put in the model we want. if( !foundInFreeList ) { // // Since we never found the model we want in the list of free peds, // we should try to find a "free" model (npd, or a model not // currently in our model group, so we don't clobber another real // model that might be wanted later).. // // We could achieve this by searching for models not in current group in free list... // However, if we never found one, then we have to just pick one, preferrably an "npd" // Since we always add the most recently removed ped to the END of the freepeds // list, we should take from the FRONT of the freepeds list... sort of a mini-scheduling // algorithm, ya know... // if( freeNonNPDNotInCurrModelGroup ) { pedStr = freeNonNPDNotInCurrModelGroup; } else { pedStr = (PedestrianStruct*) mFreePeds.GetFirst(); } rAssert( pedStr != NULL ); bool res = SwapModels( modelID, pedStr ); if( !res ) { return false; } } else { pedStr = pedStrFoundInFreeList; } ////////////////////////////////////////////////////////// // NOTE: // At this point we're ready to do the actual activating (no more bailing out) //*** Test *** //rDebugPrintf( "*** Adding %s\n", mModels[modelID].name ); //*** Test *** #ifdef RAD_DEBUG CheckModelCounts(); #endif mModels[modelID].currCount++; rAssert( strcmp( pedStr->modelName, mModels[modelID].name ) == 0 ); rAssert( pedStr->ped != NULL ); // choose a random swatch int swatch = rand() % CharacterRenderable::NUM_CHARACTER_SWATCHES; pedStr->ped->GetCharacter()->SetSwatch( swatch ); // add ped to path bool res = path->AddPedestrian( ); rAssert( res ); /* // get the segment's index (to the parent path's list of segments) // this will be needed by Pedestrian::Update() when locomoting. int pathSegmentIndex = segment->GetIndexToParentPath(); rAssert( 0 <= pathSegmentIndex && pathSegmentIndex < path->GetNumPathSegments() ); */ // set the ped's parameters pedStr->ped->Activate( path, segment, spawnPos, pedStr->modelName ); // manage the ped manager's active/free list res = mFreePeds.Remove( pedStr->listIndex ); rAssert( res ); pedStr->listIndex = mActivePeds.AddLast( pedStr ); rAssert( pedStr->listIndex != -1 ); #ifdef RAD_DEBUG CheckModelCounts(); #endif return true; } bool PedestrianManager::SwapModels( int modelID, PedestrianStruct* pedStr ) { rAssert( pedStr != NULL ); rTuneAssert( 0 <= modelID && modelID < PedestrianManager::MAX_MODELS ); Character* character = NULL; #ifdef RAD_DEBUG SanityCheck(); #endif FindModelInUseAndRemove( pedStr->modelName ); bool res = FindModelInUseAndAdd( mModels[modelID].name ); if( res == false ) { // we already removed pedStr->modelName from list, but haven't really // swapped him out... // If he's in the list of currently registered models, we don't want // to really swap him out anyway (so call FindModelInUseAndAdd(pedStr->modelName)) // But if he's not in the list of currently registered models, we DO // want to swap him out.. so call SwapData passing in "npd" if( strcmp( pedStr->modelName, "npd" ) != 0 ) { int index = GetModelIDByName( pedStr->modelName ); if( index == -1 ) { character = pedStr->ped->GetCharacter(); rAssert( character != NULL ); ::GetCharacterManager()->SwapData( character, "npd", "npd" ); sprintf( pedStr->modelName, "npd" ); } else { res = FindModelInUseAndAdd( pedStr->modelName ); rAssert( res ); } } return false; } character = pedStr->ped->GetCharacter(); rAssert( character != NULL ); ::GetCharacterManager()->SwapData( character, mModels[modelID].name, "npd" ); int nameLen = strlen( mModels[modelID].name ); rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN ); strncpy( pedStr->modelName, mModels[modelID].name, PedestrianManager::MAX_STRING_LEN ); pedStr->modelName[nameLen] = '\0'; #ifdef RAD_DEBUG SanityCheck(); #endif return true; } int PedestrianManager::GetRandomModel() { if( mNumRegisteredModels < 1 ) { rTunePrintf( "PedestrianManager: You haven't registered a pedestrian model!\n" ); return -1; } // generate a number between 1 and mNumRegisteredModels int num = (rand() % mNumRegisteredModels) + 1; rAssert( 1 <= num && num <= mNumRegisteredModels ); // loop through models array counting till we get to num & return this modelID int count = 0; for(int i=0; i 0 ); // search existing SPARSE list for name int freeIndex = -1; for( int i=0; i= PedestrianManager::MAX_MODELS ) { return false; } if( 0 <= freeIndex && freeIndex < PedestrianManager::MAX_MODELS ) { // loop through activelist looking for this model and counting the // number of instances... init it with this value int count = 0; int n = mActivePeds.GetHead(); while( 0 <= n && n < DListArray::MAX_ELEMS ) { PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(n); rAssert( pedStr ); if( strcmp( pedStr->modelName, name )==0 ) { count++; } n = mActivePeds.GetNextOf( n ); } mModels[freeIndex].Init( false, name, maxCount, count ); mNumRegisteredModels++; return true; } return false; } bool PedestrianManager::UnregisterModel( const char* name ) { rAssert( name != NULL ); if( mNumRegisteredModels <= 0 ) { return false; } // search existing SPARSE list for name for( int i=0; i 0 for( int i=0; iGetCharacter() == character ) { return true; } } return false; } void PedestrianManager::HandleEvent( EventEnum id, void* pEventData ) { /*** DEBUG *** switch( id ) { case EVENT_GETOUTOFVEHICLE_END: { static int count = 0; SwitchModelGroup( count % 2 ); count++; } break; } *** DEBUG ***/ // Handle the events appropriately: // - determine which model group we want to switch to and call SwitchToGroup on it switch ( id ) { case EVENT_LOCATOR + LocatorEvent::LOAD_PED_MODEL_GROUP: { PedGroupLocator* pLocator = (PedGroupLocator*)pEventData; rAssert( pLocator != NULL ); unsigned int groupID = pLocator->GetGroupNum(); if( pLocator->GetPlayerEntered() ) { SwitchModelGroup( (int)groupID ); } break; } case EVENT_VEHICLE_DESTROYED: { // spawn a pedestrian in th driver's seat when a traffic vehicle is destroyed Vehicle* vehicle = (Vehicle*)pEventData; if( (GetGameplayManager()->GetCurrentLevelIndex() != RenderEnums::L7) && (strcmp(vehicle->GetDriverName(), "phantom") == 0) && mActivePeds.GetNumElems() > 0 ) { int randPedNum = rand() % mActivePeds.GetNumElems(); Character* character = NULL; int count = 0; int ind = mActivePeds.GetHead(); while( 0 <= ind && ind <= MAX_PEDESTRIANS ) { if( count == randPedNum ) { PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( ind ); character = pedStr->ped->GetCharacter(); break; } count++; ind = mActivePeds.GetNextOf( ind ); } //Character* character = mPeds[rand() % MAX_PEDESTRIANS].ped->GetCharacter(); if(character) { vehicle->SetDriver(character); character->SetFadeAlpha( 255 ); // make sure we're not faded. character->SetTargetVehicle( vehicle ); character->SetInCar( true ); character->UpdateTransformToInCar(); character->GetStateManager()->SetState(); } } } break; case EVENT_PLAYER_VEHICLE_HORN: // fall thru case EVENT_PLAYER_CAR_HIT_NPC: // fall thru case EVENT_OBJECT_KICKED: { static const float TRIGGER_PANIC_RADIUS_SQR = 144.0f; // Get player data Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0); rmt::Vector playerPos; player->GetPosition( playerPos ); // for any ped nearby, make him run away... int i = mActivePeds.GetHead(); while( 0 <= i && i <= DListArray::MAX_ELEMS ) { PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( i ); rAssert( pedStr ); if( pedStr->ped->GetIsActive() ) { rmt::Vector pedPos; pedStr->ped->GetCharacter()->GetPosition( pedPos ); float distSqr = (playerPos - pedPos).MagnitudeSqr(); if( distSqr < TRIGGER_PANIC_RADIUS_SQR ) { pedStr->ped->IncitePanic(); } } i = mActivePeds.GetNextOf( i ); } } break; default: { break; } } } void PedestrianManager::AddPeds( rmt::Vector center, float addRadius ) { if( CommandLineOptions::Get( CLO_NO_PEDS ) ) { return; } if( !mAllowAdd ) { return; } // if no more free pedestrians... if( mFreePeds.GetNumElems() <= 0 ) { return; } // Invoke search on DSG for path segments that intersect with // our radius ReserveArray orList; ::GetIntersectManager()->FindPathSegmentElems( center, addRadius, orList ); // // For each segment we "intersect" with (in the loose sense), we // a) break if we have no more pedestrians to spawn // b) break if segment (or path?) already has a pedestrian // c) put pedestrian on the segment at the intersection point. // d) set pedestrian's facing/velocity/position, whatever's necessary // if(orList.mUseSize == 0) return; int which = rand() % orList.mUseSize; PathSegment* segment = NULL; segment = orList.mpData[which]; rAssert( segment != NULL ); // determine intersection point of the path segment & ped radius // at normalized value "t" rmt::Sphere s; s.Set( center, addRadius ); rmt::Vector start, end; segment->GetStartPos( start ); segment->GetEndPos( end ); rmt::Vector intPts[2]; int numIntersections = IntersectLineSphere( start, end, s, intPts ); // if no intersection, a mild surprise. if( numIntersections <= 0 ) { return; } // Just use the first intersection point rmt::Vector spawnPos = intPts[0]; float t = GetLineSegmentT( start, end, spawnPos ); // activate the pedestrian #ifdef RAD_DEBUG CheckModelCounts(); #endif bool succeeded = ActivatePed( segment, spawnPos ); #ifdef RAD_DEBUG CheckModelCounts(); #endif } void PedestrianManager::FindModelInUseAndRemove( const char* name ) { if( tName::MakeUID(name) == tName::MakeUID("npd") ) { return; } // should be able to find it in the list of models being used... tUID uid = tEntity::MakeUID( name ); bool foundUid = false; rAssert( 0 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE ); for( int i=0; i 0 if it was found in this list rAssert( mModelsInUse[i].currCount > 0 ); mModelsInUse[i].currCount--; // if currCount is zero, remove it from list of models currently in use. if( mModelsInUse[i].currCount == 0 ) { // swap last entry into this position //rDebugPrintf( "*** Removing from ModelsInUse %s\n", name ); mModelsInUse[i].currCount = mModelsInUse[mNumActiveModels-1].currCount; mModelsInUse[i].uid = mModelsInUse[mNumActiveModels-1].uid; mNumActiveModels--; } foundUid = true; break; // NOTE: MUST break here since we may have modified mNumActiveModels } } if( !foundUid ) { // should have found it! Something wrong with our code... rAssert( false ); } } bool PedestrianManager::FindModelInUseAndAdd( const char* name ) { if( strcmp( name, "npd" ) == 0 ) { return true; } // PS2 memory can only support a few (4) models in use at all times... // see if we have room for this model bool founduid = false; tUID uid = tEntity::MakeUID( name ); rAssert( 0 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE ); for( int i=0; imodelName, mModels[m].name )==0 ) { count++; } n = mActivePeds.GetNextOf( n ); } rAssert( count == expectedCount ); } // *** DEBUG *** } void PedestrianManager::DumpAllPedModels() { // deactivate all current peds RemoveAllPeds(); // Swap "npd" in for the other models for( int i=0; imodelName ) == tName::MakeUID( "npd" ) ) { continue; } #ifdef RAD_DEBUG SanityCheck(); #endif // we're swapping out the model for npd... better update the modelsinuse list FindModelInUseAndRemove( pedStr->modelName ); // since this model has been unregistered, it's not in the current // model group, so keeping it around will only waste memory... // we need to swap in "npd" in its place. Character* character = pedStr->ped->GetCharacter(); rAssert( character != NULL ); ::GetCharacterManager()->SwapData( character, "npd", "npd" ); sprintf( pedStr->modelName, "npd" ); #ifdef RAD_DEBUG SanityCheck(); #endif } }