The-Simpsons-Hit-and-Run/game/code/worldsim/ped/pedestrianmanager.cpp

1593 lines
46 KiB
C++

//=============================================================================
// 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 <raddebug.hpp> // for rAssert & other debug print outs
#include <stdlib.h>
#include <string.h>
#include <debug/profiler.h> // for the Profiler
#include <radmath/radmath.hpp> // for rmt::Vector
#include <memory/srrmemory.h>
#include <meta/locatorevents.h>
#include <meta/pedgrouplocator.h>
#include <worldsim/ped/pedestrianmanager.h>
#include <worldsim/avatar.h>
#include <worldsim/avatarmanager.h>
/*
#include <camera/supercammanager.h>
#include <camera/supercam.h>
*/
#include <worldsim/character/character.h>
#include <worldsim/character/charactermanager.h>
#include <worldsim/character/characterrenderable.h>
#include <render/Culling/ReserveArray.h>
#include <render/IntersectManager/IntersectManager.h>
#include <roads/geometry.h>
#include <camera/supercammanager.h>
#include <camera/supercamcentral.h>
#include <camera/supercam.h>
#include <pedpaths/path.h>
#include <pedpaths/pathsegment.h>
#include <mission/gameplaymanager.h>
#include <worldsim/vehiclecentral.h>
// 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; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
{
PedestrianStruct* pedStr = &(mInstance->mPeds[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; i<PedestrianManager::MAX_MODEL_GROUPS; i++ )
{
mModelGroups[i].numModels = 4;
mModelGroups[i].models[0].Init( "male1", 5 );
mModelGroups[i].models[1].Init( "fem1", 5 );
mModelGroups[i].models[2].Init( "boy1", 5 );
mModelGroups[i].models[3].Init( "girl1", 5 );
for( int j=4; j<PedestrianManager::MAX_MODELS; j++ )
{
mModelGroups[i].models[j].InitZero();
}
}
}
void PedestrianManager::SetDefaultModelGroup( int toGroupID )
{
mDefaultModelGroup = toGroupID;
}
void PedestrianManager::Init()
{
mAllowAdd = true;
mMillisecondsTillRemove = PedestrianManager::MILLISECONDS_BETW_REMOVES;
mMillisecondsTillAdd = PedestrianManager::MILLISECONDS_BETW_ADDS;
mMillisecondsPopulateWorld = PedestrianManager::MILLISECONDS_POPULATE_WORLD;
mFreePeds.Clear();
mActivePeds.Clear();
for( int i=0; i<PedestrianManager::MAX_MODELS_IN_USE; i++ )
{
mModelsInUse[i].uid.SetText( NULL );
mModelsInUse[i].currCount = 0;
}
mNumActiveModels = 0;
// Recall that at this point, MissionManager has parsed the level script
// which may or may not have populated our model groups. Recall also that
// before that happened, we init all our model groups with a default group
// Therefore, we can safely switch our current model group to group 0
// (it should exist one way or another).
// switch to our group zero
UnregisterAllModels();
mCurrGroupID = -1;
SwitchModelGroup( mDefaultModelGroup ); // will set mCurrGroupID to 0
for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
{
PedestrianStruct* pedStr = &(mPeds[i]);
rAssert( pedStr != NULL );
Pedestrian* ped = pedStr->ped;
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; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
{
mPeds[i].ped = new (GMA_LEVEL_OTHER) Pedestrian();
mPeds[i].ped->AddRef();
mPeds[i].listIndex = -1;
}
}
PedestrianManager::~PedestrianManager()
{
GetEventManager()->RemoveAll( this );
int i;
for( i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
{
if( mPeds[i].ped != NULL )
{
Character* character = mPeds[i].ped->GetCharacter();
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; i<maxChars; i++ )
{
Character* c = cm->GetCharacter(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; i<VehicleCentral::MAX_ACTIVE_VEHICLES; i++ )
{
Vehicle* v = vc->GetVehicle(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; i<PedestrianManager::MAX_MODELS; i++ )
{
if( !mModels[modelID].free &&
mModels[modelID].currCount < mModels[modelID].maxCount )
{
foundUsable = true;
break;
}
// go to next model
modelID = (modelID+1) % PedestrianManager::MAX_MODELS;
}
// if we never found a model with currCount < maxCount, then
// we can't actually do any spawning...
if( !foundUsable )
{
rTunePrintf( "Can't spawn a new ped. Not enough Ped model "
"instances allowed in leveli.mfk. See Dusit or Sheik.\n" );
return false;
}
PedestrianStruct* freeNonNPDNotInCurrModelGroup = NULL;
PedestrianStruct* pedStrFoundInFreeList = NULL;
// See if this model is already available in our freepeds list...
// If so, we don't need to call SwapData (save some processing time)
bool foundInFreeList = false;
int j = mFreePeds.GetHead();
while( 0 <= j && j < DListArray::MAX_ELEMS )
{
pedStr = (PedestrianStruct*) mFreePeds.GetDataAt(j);
rAssert( pedStr != NULL );
if( !foundInFreeList && strcmp(pedStr->modelName, 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<PedestrianManager::MAX_MODELS; i++)
{
if( !mModels[i].free )
{
count++;
}
if( count==num )
{
return i;
}
}
rAssert( false ); // shouldn't get here
return -1;
}
bool PedestrianManager::RegisterModel( const char* name, int maxCount )
{
rTuneAssert( name != NULL );
rTuneAssert( maxCount > 0 );
// search existing SPARSE list for name
int freeIndex = -1;
for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
{
if( mModels[i].free )
{
freeIndex = i;
continue;
}
// found an existing model registered under same name, so overwrite w/ new values
if( strcmp(mModels[i].name, name)==0 )
{
mModels[i].maxCount = maxCount;
// currCount should be the same as there are active instances
#ifdef RAD_DEBUG
CheckModelCounts();
#endif
return true;
}
}
// If we haven't found an existing model, then we're adding one.
// See if we can...
if( mNumRegisteredModels >= 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<PedestrianManager::MAX_MODELS; i++ )
{
if( mModels[i].free )
{
continue;
}
int nameLen = strlen( name );
int modelNameLen = strlen( mModels[i].name );
if( (nameLen == modelNameLen) &&
(strncmp(mModels[i].name, name, nameLen)==0) )
{
mNumRegisteredModels--;
mModels[i].InitZero();
return true;
}
}
return false;
}
bool PedestrianManager::UnregisterModel( int modelID )
{
rAssert( 0 <= modelID && modelID < PedestrianManager::MAX_MODELS );
if( mNumRegisteredModels <= 0 )
{
return false;
}
if( mModels[modelID].free )
{
return false;
}
mNumRegisteredModels--;
mModels[modelID].InitZero();
return true;
}
void PedestrianManager::UnregisterAllModels()
{
mNumRegisteredModels = 0;
for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
{
mModels[i].InitZero();
}
}
void PedestrianManager::SwitchModelGroup( int toGroupID )
{
rAssert( 0 <= toGroupID && toGroupID < PedestrianManager::MAX_MODEL_GROUPS );
#ifdef RAD_DEBUG
CheckModelCounts();
#endif
// if already in this group, forget it
if( mCurrGroupID == toGroupID )
{
return;
}
// NOTE:
// Don't want to UnregisterAllModels here because if say male1 exists in new group as
// well as in old group, we'll be wiping its currcount. So just go around resetting
// maxcount to zero and let RegisterModel overwrite the ones that are pertinent.
// In the end, unregister everything that doesn't have maxcount > 0
for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
{
mModels[i].maxCount = 0;
}
for( int i=0; i<mModelGroups[toGroupID].numModels; i++ )
{
RegisterModel(
mModelGroups[toGroupID].models[i].name,
mModelGroups[toGroupID].models[i].maxCount );
}
for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
{
if( mModels[i].maxCount <= 0 )
{
UnregisterModel( i );
}
}
#ifdef RAD_DEBUG
CheckModelCounts();
#endif
mCurrGroupID = toGroupID;
#ifdef RAD_DEBUG
char msg[256];
sprintf( msg, "*** SWITCHING TO NEW MODEL GROUP: " );
for( int i=0; i<mModelGroups[toGroupID].numModels; i++ )
{
sprintf( msg, "%s%s ", msg, mModelGroups[toGroupID].models[i].name );
}
sprintf( msg, "%s\n", msg );
//rDebugPrintf( msg );
#endif
}
int PedestrianManager::GetModelIDByName( const char* name )
{
for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
{
if( strcmp( mModels[i].name, name )==0 )
{
return i;
}
}
return -1;
}
void PedestrianManager::SetModelGroup( int groupID, const ModelGroup& group )
{
rAssert( 0 <= groupID && groupID < PedestrianManager::MAX_MODEL_GROUPS );
rAssert( 0 <= group.numModels && group.numModels <= PedestrianManager::MAX_MODELS );
mModelGroups[groupID].numModels = group.numModels;
for( int i=0; i<group.numModels; i++ )
{
mModelGroups[groupID].models[i].Init(
group.models[i].name, group.models[i].maxCount );
}
for( int i=group.numModels ; i<PedestrianManager::MAX_MODELS; i++ )
{
mModelGroups[groupID].models[i].InitZero();
}
}
//=============================================================================
// PedestrianManager::IsPed
//=============================================================================
// Description: Comment
//
// Parameters: ( Character* character )
//
// Return: bool
//
//=============================================================================
bool PedestrianManager::IsPed( Character* character )
{
int i;
for (i = 0; i < MAX_PEDESTRIANS; ++i )
{
if ( mPeds[ i ].ped->GetCharacter() == 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<CharacterAi::InCar>();
}
}
}
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<PathSegment*> 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<mNumActiveModels; i++ )
{
if( uid == mModelsInUse[i].uid.GetUID() )
{
// count for this ped should be > 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; i<mNumActiveModels; i++ )
{
if( uid == mModelsInUse[i].uid.GetUID() )
{
mModelsInUse[i].currCount++;
founduid = true;
break;
}
}
if( !founduid )
{
// couldn't find it in list of models currently in use, so try to add it
// to the list. If we can't, we don't spawn this model
if( mNumActiveModels == PedestrianManager::MAX_MODELS_IN_USE )
{
return false;
}
//rDebugPrintf( "*** Adding to ModelsInUse %s\n", name );
mModelsInUse[mNumActiveModels].uid.SetText( name );
mModelsInUse[mNumActiveModels].currCount = 1;
mNumActiveModels++;
}
return true;
}
void PedestrianManager::SanityCheck()
{
#ifdef RAD_DEBUG
// make sure every non-"npd" model in mPeds exists in ModelsInUse
char msg[256];
for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
{
if( strcmp( mPeds[i].modelName, "npd" ) == 0 )
{
continue;
}
bool found = false;
for( int j=0; j<mNumActiveModels; j++ )
{
tUID test = tEntity::MakeUID( mPeds[i].modelName );
if( test == mModelsInUse[j].uid.GetUID() )
{
sprintf( msg, "%s already came up once in mModelsInUse list\n",
mPeds[i].modelName );
rAssertMsg( !found, msg);
sprintf( msg, "UIDs equal but names are not! %s vs %s\n",
mPeds[i].modelName, mModelsInUse[j].uid.GetText() );
rAssertMsg( strcmp( mPeds[i].modelName, mModelsInUse[j].uid.GetText() ) == 0, msg );
found = true;
}
}
sprintf( msg, "%s in mPeds could not be found in mModelsInUse\n",
mPeds[i].modelName );
rAssertMsg( found, msg );
}
// make sure every model in ModelsInUse is non-"npd" and exists in mPeds, with correct count
for( int i=0; i<mNumActiveModels; i++ )
{
rAssertMsg( strcmp( mModelsInUse[i].uid.GetText(), "npd" ) != 0,
"Found npd in mModelsInUse\n" );
bool found = false;
int count = 0;
for( int j=0; j<PedestrianManager::MAX_PEDESTRIANS; j++ )
{
tUID test = tEntity::MakeUID( mPeds[j].modelName );
if( test == mModelsInUse[i].uid.GetUID() )
{
count++;
sprintf( msg, "UIDs equal but names are not! %s vs %s\n",
mPeds[j].modelName, mModelsInUse[i].uid.GetText() );
rAssertMsg( strcmp( mPeds[j].modelName, mModelsInUse[i].uid.GetText() ) == 0, msg );
found = true;
}
}
sprintf( msg, "%s in mModelsInUse could not be found in mPeds\n",
mModelsInUse[i].uid.GetText() );
rAssertMsg( found, msg );
sprintf( msg, "Not equals numbers of %s found in mPeds (%d) and mModelsInUse (%d)\n",
mModelsInUse[i].uid.GetText(), count, mModelsInUse[i].currCount );
rAssertMsg( count == mModelsInUse[i].currCount, msg );
}
#endif
}
void PedestrianManager::CheckModelCounts()
{
// *** DEBUG ***
// do some checking here to make sure mModels' currcount is
// correct
for( int m=0; m<PedestrianManager::MAX_MODELS; m++ )
{
if( mModels[m].free )
{
continue;
}
int expectedCount = mModels[m].currCount;
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, 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; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
{
PedestrianStruct* pedStr = &(mPeds[i]);
rAssert( pedStr );
if( tName::MakeUID( pedStr->modelName ) == 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
}
}