//============================================================================= // Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. // // File: chasemanager.cpp // // Description: ChaseManager Class Implementation // // History: 11/5/2002 + Created -- Dusit Eakkachaichanvet // //============================================================================= #include #include #include #include #include /* #include #include */ #include #include #include #include #include #include #include //////////////////////////////////// // Initialize Statics //ChaseManager* spChaseManager = NULL; static const float HARASS_BEELINE_DIST = 60.0f; ///////////////////////////////////// // Constructors/Destructors ChaseManager::ChaseManager() : mNumRegisteredModels( 0 ), mTotalSpawnFrequencies( 0 ), mMaxVehicles( MAX_CHASE_VEHICLES ) { GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED ); //GetEventManager()->AddListener( this, EVENT_REPAIR_CAR ); // Chase cars won't be repairing. for( int i=0; iRemoveAll( this ); DeactivateAllVehicles(); } bool ChaseManager::IsChaseVehicle( Vehicle* v ) { ChaseVehicle* cv = FindChaseVehicle( v ); return( cv != NULL ); } ChaseManager::ChaseVehicle* ChaseManager::FindChaseVehicle( Vehicle* v ) { for( int i=0; iisActive ); DeactivateVehicle( cv ); /*** WELL now they don't want husks for specifically for chase/harass cars and so we go round in circles.... Initially, they wanted ALL to have husks "for consistency"... Now, not so much. // obtain info from the vehicle rmt::Vector initPos, initDir; v->GetPosition( &initPos ); initDir = v->GetFacing(); rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) ); // Remove original from VehicleCentral's ActiveList VehicleCentral* vc = ::GetVehicleCentral(); rAssert( vc != NULL ); vc->RemoveVehicleFromActiveList( cv->v ); GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_DESTROYED, cv->v ); // Update chase AI vc->SetVehicleController( cv->activeListIndex, NULL ); cv->activeListIndex = -1; cv->vAI->SetActive( false ); cv->vAI->Finalize(); // // Now we grab husk and put it in place of the original vehicle // Vehicle* husk = GetVehicleCentral()->mHuskPool.RequestHusk( VT_AI, v ); if( husk == NULL ) { return; } int res = GetVehicleCentral()->AddVehicleToActiveList( husk ); if( res == -1 ) { // not supposed to happen since the list can't be full!!! // we TOOK something out of the list before adding something in // If this assert happens, it is both fatal and strange rAssert( false ); return; } cv->activeListIndex = res; husk->AddRef(); husk->SetInitialPosition( &initPos ); float angle = GetRotationAboutY( initDir.x, initDir.z ); husk->SetResetFacingInRadians( angle ); husk->Reset(); husk->SetLocomotion( VL_PHYSICS ); cv->husk = husk; */ } } void ChaseManager::Init() { SpawnManager::Init(); //////////////////////////////////////////////////////////////// // CLEAN UP PREVIOUS RUN // DeactivateAllVehicles(); //////////////////////////////////////////////////////////////// // INITIALIZE FOR NEW RUN // int count = 0; // create a temp string array of exactly this many elems or "buckets" char** names = new char* [mTotalSpawnFrequencies]; for( int i=0; iInitVehicle( names[coinflip], false, mConfileName, VT_AI, VehicleCentral::ALLOW_DRIVER, false, // not a playercar false); // start with in car bv representation immediately rAssert( v != NULL ); mVehicles[i].v = v; mVehicles[i].husk = NULL; mVehicles[i].v->AddRef(); mVehicles[i].v->SetLocomotion( VL_PHYSICS ); mVehicles[i].vAI = new (GMA_LEVEL_OTHER) ChaseAI( v, HARASS_BEELINE_DIST ); mVehicles[i].vAI->AddRef(); // Corresponding call to Release() will already call delete if refcount<=1 mVehicles[i].isActive = false; mVehicles[i].isOutOfSight = true; mVehicles[i].markedForDeletion = false; mVehicles[i].secondsOutOfSight = 0.0f; } // clean up our temporary name buckets... for( int i=0; i SECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL ) { DeactivateVehicle( &mVehicles[i] ); } } } void ChaseManager::ClearObjectsInsideRadius( rmt::Vector center, float radius ) { int nObjectsRemoved = 0; float minDistSqr = radius * radius; for( int i=0; iGetPosition( &vPos ); rmt::Vector toSphere = center - vPos; if( toSphere.MagnitudeSqr() <= minDistSqr ) { DeactivateVehicle( &mVehicles[i] ); } } } } void ChaseManager::ClearObjectsOutsideRadius( rmt::Vector center, float radius ) { float minDistSqr = radius * radius; for( int i=0; iGetPosition( &vPos ); rmt::Vector toSphere = center - vPos; if( toSphere.MagnitudeSqr() > minDistSqr ) { DeactivateVehicle( &mVehicles[i] ); } } } } bool ChaseManager::RegisterModel( const char* name, int spawnFreq ) { rAssert( name != NULL ); rAssert( spawnFreq > 0 ); // search existing SPARSE list for name int freeIndex = -1; for( int i=0; iSetActive( false ); mVehicles[i].vAI->EnterLimbo(); } } } void ChaseManager::EnableAllActiveVehicleAIs() { for( int i=0; iSetActive( true ); mVehicles[i].vAI->ExitLimbo(); } } } void ChaseManager::AddObjects( float seconds ) { rAssert( seconds >= 0.0f ); // Get VehicleCentral. We'll need it lots. VehicleCentral* vc = ::GetVehicleCentral(); rAssert( vc != NULL ); // Get Player info. rmt::Vector playerPos, playerVel; /* SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam(); rAssert( superCam != NULL ); superCam->GetPosition( &playerPos ); superCam->GetVelocity( &playerVel ); */ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0); rAssert( avatar != NULL ); avatar->GetPosition( playerPos ); avatar->GetVelocity(playerVel); float spawnRadius = GetSpawnRadius(); // Do FindRoadElems to get the road segments that intersect our sphere. // For each road segment's lane, tally a list of all intersection points (max of 2) // For each intersection point, // if mNumObjects < mMaxObjects, // ActivateVehicle() // Initialize vehicle ReserveArray orList; ::GetIntersectManager()->FindRoadSegmentElems( playerPos, spawnRadius, orList ); // This is the vehicle from our list of inactive chase vehicles that we'll spawn // Abort early if there's no more vehicle available to spawn... ChaseVehicle* cv = GetInactiveVehicle(); if( cv == NULL ) { return; } for( int i=0; iGetNumLanes(); j++ ) { // Find places where lane intersects with spawn radius (max 2 locations) rmt::Vector startPos, startDir, endPos, endDir; segment->GetLaneLocation( 0.0f, j, startPos, startDir ); segment->GetLaneLocation( 1.0f, j, endPos, endDir ); rmt::Vector intPts[2]; rmt::Sphere playerSphere( playerPos, spawnRadius ); int numIntersections = IntersectLineSphere( startPos, endPos, playerSphere, intPts ); if(numIntersections <= 0) { continue; // doesn't intersect our spawn sphere; skip this lane } // for each intersection point found, plant a vehicle for( int k=0; kv; rAssert( v != NULL ); // // Detect if we're placing this car on top of another car // by looping through active vehicles list. If there is car beneath us, // don't spawn here and try next point. // rmt::Sphere vSphere; v->GetBoundingSphere( &vSphere ); vSphere.centre = vPos; if( SpawningOnTopOfAnotherVehicle(vSphere) ) { continue; // gonna spawn on a car; try next spawn point } // // Check if in the next n seconds, vehicle will still be in // player's spawn zone (so we didn't add it in vain). // float timeToLiveInSeconds = 1.0f; //GetSecondsBetwAdds(); // // Predict player pos after timeToLiveInSeconds seconds // rmt::Vector playerPos2 = playerPos + playerVel * timeToLiveInSeconds; // // Predict v pos after timeToLiveInSeconds seconds // rmt::Vector vDir = endPos - startPos; vDir.Normalize(); // *** SQUARE ROOT! *** float vAccel = v->mDesignerParams.mDpGasScale * v->mSlipGasModifier * 1.0f; //v->mGas; float vDistWillTravel = 0.5f * vAccel * timeToLiveInSeconds * timeToLiveInSeconds; rmt::Vector vPos2 = vPos + vDir * vDistWillTravel; // // Make sure vPos2 still inside playerPos2's radius before we spawn it // float minDistSqr = spawnRadius * spawnRadius; float distSqr = (vPos2 - playerPos2).MagnitudeSqr(); if( distSqr < minDistSqr ) { // want the vehicle to spawn facing the player rmt::Vector initFacing = playerPos - vPos; // initialize vehicle v->SetInitialPosition( &vPos ); float angle = GetRotationAboutY( initFacing.x, initFacing.z ); v->SetResetFacingInRadians( angle ); v->Reset(); bool succeeded = ActivateVehicle( cv ); if( !succeeded ) { // vehiclecentral's activelist is full.. // no point trying to add anymore return; } // just used the inactive vehicle; grab a new inactive vehicle cv = GetInactiveVehicle(); } // end-of-if vehicle's next pos lies in radius of player's next spawn sphere } // end-of-if mNumObjects < mMaxObjects } // end-of-loop through all intersection points on spawn radius for this lane } // end-of-loop through all lanes belonging to one segment }// end-of-loop through all segments returned by DSGFind } void ChaseManager::RemoveObjects( float seconds ) { // Get Player info rmt::Vector playerPos; /* SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam(); rAssert( superCam != NULL ); superCam->GetPosition( &playerPos ); */ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0); rAssert( avatar != NULL ); avatar->GetPosition( playerPos ); float radius = GetRemoveRadius(); // Remove! ClearObjectsOutsideRadius( playerPos, radius ); ClearOutOfSightVehicles(); } void ChaseManager::UpdateObjects( float seconds ) { for( int i=0; imVehicleDestroyed && mVehicles[i].vAI->GetState() != VehicleAI::STATE_LIMBO ) { mVehicles[i].vAI->EnterLimbo(); // NOTE: // Never set mVehicles[i].isActive to false here because // it means something entirely different (it means that it's not // within your radius). Just set the AI's active flag to false //mVehicles[i].isActive = false; } if( mVehicles[i].husk || mVehicles[i].markedForDeletion ) { Vehicle* vehicle = mVehicles[i].v; if(mVehicles[i].husk) { vehicle = mVehicles[i].husk; } // Test for out of sight of player 0... If so, increment timer, if not reset timer rmt::Vector pos; vehicle->GetPosition( &pos ); mVehicles[i].isOutOfSight = !GetGameplayManager()->TestPosInFrustrumOfPlayer( pos, 0 ); mVehicles[i].secondsOutOfSight += seconds; if( !mVehicles[i].isOutOfSight ) { mVehicles[i].secondsOutOfSight = 0.0f; } } } } /* // VehicleCentral automatically updates all vehicles & their AI // controllers if they're under Physics Locomotion (which they are) // so we need do nothing here. for( int i=0; iUpdate( seconds ); } } */ } void ChaseManager::SuspendAllVehicles() { for( int i=0; iCarDisplay( false ); GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicles[i].v ); } } } void ChaseManager::ResumeAllVehicles() { VehicleCentral* vc = ::GetVehicleCentral(); rAssert( vc != NULL ); for( int i=0; iCarDisplay( true ); // add to vehiclecentral's activelist if( vc->ActiveVehicleListIsFull() ) { rAssert(false); return; } int res = vc->AddVehicleToActiveList( mVehicles[i].v ); if( res == -1 ) { // not supposed to happen since we already eliminated the // safe failure condition (activelistisfull).. So something else // went wrong... rAssert( false ); } // update chase AI vc->SetVehicleController( res, mVehicles[i].vAI ); mVehicles[i].vAI->Initialize(); mVehicles[i].vAI->SetActive( true ); } } } void ChaseManager::DeactivateVehicle( ChaseVehicle* cv ) { rAssert( cv->v != NULL ); rAssert( cv->vAI != NULL ); rAssert( cv->isActive ); // Remove from VehicleCentral's ActiveList VehicleCentral* vc = ::GetVehicleCentral(); rAssert( vc != NULL ); if( cv->husk == NULL ) { vc->RemoveVehicleFromActiveList( cv->v ); GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_DESTROYED, cv->v ); } else { bool succeeded = vc->RemoveVehicleFromActiveList( cv->husk ); rAssert( succeeded ); ::GetVehicleCentral()->mHuskPool.FreeHusk( cv->husk ); cv->husk->Release(); // don't verify destruction cuz huskpool has final ownership cv->husk = NULL; } //if( cv->activeListIndex != -1 ) //{ // vc->SetVehicleController( cv->activeListIndex, NULL ); //} cv->vAI->SetActive( false ); cv->vAI->Finalize(); //cv->activeListIndex = -1; cv->isActive = false; cv->isOutOfSight = true; cv->markedForDeletion = false; cv->secondsOutOfSight = 0.0f; mNumObjects--; } bool ChaseManager::ActivateVehicle( ChaseVehicle* cv ) { rAssert( cv->v != NULL ); rAssert( cv->vAI != NULL ); //rAssert( cv->activeListIndex == -1 ); rAssert( !cv->isActive ); VehicleCentral* vc = ::GetVehicleCentral(); rAssert( vc != NULL ); // add to vehiclecentral's activelist if( vc->ActiveVehicleListIsFull() ) { return false; } int res = vc->AddVehicleToActiveList( cv->v ); if( res == -1 ) { // not supposed to happen since we already eliminated the // safe failure condition (activelistisfull).. So something else // went wrong... rAssert( false ); } // update chase AI vc->SetVehicleController( res, cv->vAI ); cv->vAI->Initialize(); cv->vAI->SetActive( true ); // update our variables //cv->activeListIndex = res; cv->isActive = true; rAssert( cv->husk == NULL ); cv->isOutOfSight = true; cv->markedForDeletion = false; cv->secondsOutOfSight = 0.0f; mNumObjects++; // tell sound GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_SPAWNED, cv->v ); return true; } void ChaseManager::DeactivateAllVehicles() { for( int i=0; iRelease(); mVehicles[i].v = NULL; } if( mVehicles[i].vAI != NULL ) { mVehicles[i].vAI->Release(); mVehicles[i].vAI = NULL; } } rAssert( mNumObjects == 0 ); } void ChaseManager::MarkAllVehiclesForDeletion() { for( int i=0; iGetActiveVehicleList( activeVehicles, nActiveVehicles ); Vehicle* aCar; rmt::Vector aPos; rmt::Sphere aSphere; // // Because VehicleCentral's ActiveList is a SPARSE array, we have to loop // through the MAX array size. But since we know how many actual vehicles // there are in there, we can quit after we've reached that number. So keep // a counter. // int vCount = 0; for( int i=0; iGetMaxActiveVehicles(); i++ ) { if( vCount >= nActiveVehicles ) { break; } aCar = activeVehicles[i]; if( aCar == NULL ) { continue; } vCount++; aCar->GetPosition( &aPos ); aCar->GetBoundingSphere( &aSphere ); float distSqr = (aPos - s.centre).MagnitudeSqr(); float minDist = aSphere.radius + s.radius; float minDistSqr = minDist * minDist; // if we're colliding with another car if( distSqr < minDistSqr ) { return true; } } return false; } float ChaseManager::GetClosestCarPosition(rmt::Vector* toPos, rmt::Vector* carPos) { float distSqr = 999999999.0f; for( int i=0; iGetPosition(&currPos); float currDistSqr = (*toPos - currPos).MagnitudeSqr(); if(currDistSqr < distSqr) { distSqr = currDistSqr; *carPos = currPos; } } } } return distSqr; }