#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Choreo includes. // #include #include #include #include #include #include #include #include #include #include #include // Temp hack to get NPCs in. // TBJ [9/3/2002] // #include #include #include #include #include #include #include #include #include #include #include #include #include #include CharacterManager* CharacterManager::spCharacterManager = 0; /************ OUTPUT_TIMES FOR DLOP **********/ //#define OUTPUT_TIMES /**********************************************/ // Init the character tunables. // // Statics. // float CharacterTune::sfLocoRotateRate; float CharacterTune::sfLocoAcceleration; float CharacterTune::sfLocoDecceleration; bool CharacterTune::bLocoTest; float CharacterTune::sfAirRotateRate; float CharacterTune::sfAirAccelScale; float CharacterTune::sfAirGravity; float CharacterTune::sfStompGravityScale; float CharacterTune::sfDashBurstMax; float CharacterTune::sfDashAcceleration; float CharacterTune::sfDashDeceleration; float CharacterTune::sfJumpHeight; float CharacterTune::sfDoubleJumpHeight; float CharacterTune::sfDoubleJumpAllowUp; float CharacterTune::sfDoubleJumpAllowDown; float CharacterTune::sfHighJumpHeight; float CharacterTune::sfMaxSpeed; float CharacterTune::sfTurboRotateRate; float CharacterTune::sfGetInOutOfCarAnimSpeed; rmt::Vector CharacterTune::sGetInPosition; float CharacterTune::sGetInHeightThreshold; float CharacterTune::sGetInOpenDelay; float CharacterTune::sGetInOpenSpeed; float CharacterTune::sGetInCloseDelay; float CharacterTune::sGetInCloseSpeed; float CharacterTune::sGetOutOpenDelay; float CharacterTune::sGetOutOpenSpeed; float CharacterTune::sGetOutCloseDelay; float CharacterTune::sGetOutCloseSpeed; float CharacterTune::sfKickingForce; float CharacterTune::sfSlamForce; // How long in seconds that the character will get shocked for float CharacterTune::sfShockTime; bool CharacterManager::sbFixedSimRate; static const unsigned INVALID_LOAD = 0xffffffff; Blinker g_Blinkers[ 64 ]; //should match max_characters /* ============================================================================== CharacterManager::CreateInstance ============================================================================== Description: Comment Parameters: ( void ) Return: CharacterManager ============================================================================= */ CharacterManager* CharacterManager::CreateInstance( void ) { rAssertMsg( spCharacterManager == 0, "CharacterManager already created.\n" ); #ifdef RAD_GAMECUBE spCharacterManager = new ( GMA_GC_VMM ) CharacterManager; #else spCharacterManager = new ( GMA_PERSISTENT ) CharacterManager; #endif return( spCharacterManager ); } /* ============================================================================== CharacterManager::GetInstance ============================================================================== Description: Comment Parameters: ( void ) Return: CharacterManager ============================================================================= */ CharacterManager* CharacterManager::GetInstance( void ) { rAssertMsg( spCharacterManager != 0, "CharacterManager has not been created yet.\n" ); return spCharacterManager; } /* ============================================================================== CharacterManager::DestroyInstance ============================================================================== Description: Comment Parameters: ( void ) Return: void ============================================================================= */ void CharacterManager::DestroyInstance( void ) { rAssertMsg( spCharacterManager != 0, "CharacterManager has not been created.\n" ); delete ( GMA_PERSISTENT, spCharacterManager ); } /* ============================================================================== CharacterManager::CharacterManager ============================================================================== Description: Comment Parameters: ( void ) Return: CharacterManager ============================================================================= */ CharacterManager::CharacterManager( void ) { int i; for ( i = 0; i < MAX_CHARACTERS; i++ ) { mpCharacter[ i ] = 0; mGarbage[i] = false; } GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::CAR_DOOR ) ); GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::BOUNCEPAD ) ); GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT ) ); GetEventManager()->AddListener( this, EVENT_DEATH_VOLUME_SCREEN_BLANK ); GetEventManager()->AddListener( this, EVENT_STAGE_COMPLETE ); GetEventManager()->AddListener( this, EVENT_MISSION_SUCCESS ); GetEventManager()->AddListener( this, EVENT_CARD_COLLECTED ); GetEventManager()->AddListener( this, EVENT_MISSION_CHARACTER_RESET ); GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START ); GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END ); GetEventManager()->AddListener( this, EVENT_TOGGLE_FIRSTPERSON ); CharacterTune::sfLocoRotateRate = 10.0f; CharacterTune::sfLocoAcceleration = 20.0f; CharacterTune::sfLocoDecceleration = -10.0f; CharacterTune::bLocoTest = false; CharacterTune::sfAirRotateRate = 4.0f; CharacterTune::sfAirAccelScale = 0.078f; //was .1 CharacterTune::sfAirGravity = -25.0f; //was -30 CharacterTune::sfStompGravityScale = 3.22f; CharacterTune::sfJumpHeight = 1.9f; //was 1.65 CharacterTune::sfDoubleJumpHeight = 1.0f; CharacterTune::sfDoubleJumpAllowUp = 2.0f; CharacterTune::sfDoubleJumpAllowDown = 12.0f; //was 6 CharacterTune::sfHighJumpHeight = 1.9f; CharacterTune::sfDashBurstMax = 4.0f; // 7.0 CharacterTune::sfDashAcceleration = 200.0f; CharacterTune::sfDashDeceleration = 200.0f; CharacterTune::sfMaxSpeed = 4.0f; CharacterTune::sGetInPosition.Set( 1.05f, 0.0f, -0.86f ); CharacterTune::sGetInHeightThreshold = 0.1f; CharacterTune::sGetInOpenDelay = 0.0f; CharacterTune::sGetInOpenSpeed = 0.2f; CharacterTune::sGetInCloseDelay = 0.0f; CharacterTune::sGetInCloseSpeed = 0.2f; CharacterTune::sGetOutOpenDelay = 0.0f; CharacterTune::sGetOutOpenSpeed = 0.2f; CharacterTune::sGetOutCloseDelay = 0.0f; CharacterTune::sGetOutCloseSpeed = 0.2f; CharacterTune::sfTurboRotateRate = 2.0f; // 1.2 CharacterTune::sfGetInOutOfCarAnimSpeed = 30.0f; CharacterTune::sfKickingForce = 400.0f; CharacterTune::sfSlamForce = 800.0f; CharacterManager::sbFixedSimRate = false; Console* pConsole = GetConsole( ); rAssert( pConsole ); if ( pConsole ) { pConsole->AddFunction( "SetCharacterPosition", CharacterManager::SetCharacterPosition, "Sets the character position", 3, 3 ); pConsole->AddFunction( "ResetCharacter", CharacterManager::ResetCharacter, "Sets the character to the named locator", 2, 2 ); pConsole->AddFunction( "AddTeleportDest", AddTeleportDest, "Set a valid location for a teleport", 3, 5 ); pConsole->AddFunction( "SetInitialWalk", SetInitialWalk, "Set locator to walk to on startup", 1, 1 ); } #ifdef DEBUGWATCH const int MAX_LEN = 64; char debugName[ MAX_LEN ]; int len = sprintf( debugName, "Character" ); rAssert( len < MAX_LEN ); radDbgWatchAddBoolean(&CharacterTune::bLocoTest, "Steer Facing", debugName, NULL, 0 ); radDbgWatchAddFloat(&CharacterTune::sfLocoRotateRate, "Loco Turning Rate", debugName, NULL, 0, 0.1f, 50.0f); radDbgWatchAddFloat(&CharacterTune::sfLocoAcceleration, "Loco Acceleration", debugName, NULL, 0, 0.1f, 50.0f); radDbgWatchAddFloat(&CharacterTune::sfLocoDecceleration, "Loco Deceleration", debugName, NULL, 0, -50.0f, -0.1f); radDbgWatchAddFloat(&CharacterTune::sfAirRotateRate, "Air Turning Rate", debugName, NULL, 0, 0.0f, 20.0f); radDbgWatchAddFloat(&CharacterTune::sfAirAccelScale, "Air Acceleration", debugName, NULL, 0, 0.0f, 1.0f ); radDbgWatchAddFloat(&CharacterTune::sfAirGravity, "Air Gravity", debugName, NULL, 0, -100.0f, 0.1f ); radDbgWatchAddFloat(&CharacterTune::sfStompGravityScale, "Stomp Gravity Scale", debugName, NULL, 0, 0.1f, 4.0f); radDbgWatchAddFloat(&CharacterTune::sfJumpHeight, "Jump Height (Tap)", debugName, NULL, 0, 0.0f, 100.0f ); radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpHeight, "Jump Height (Double Jump)", debugName, NULL, 0, 0.0f, 100.0f ); radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpAllowUp, "Double Jump Sweet Spot (Up)", debugName, NULL, 0, 0.0f, 30.0f ); radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpAllowDown, "Double Jump Sweet Spot (Down)", debugName, NULL, 0, 0.0f, 30.0f ); radDbgWatchAddFloat(&CharacterTune::sfHighJumpHeight, "Jump Height (Full Press)", debugName, NULL, 0, 0.0f, 100.0f ); radDbgWatchAddFloat(&CharacterTune::sfDashAcceleration, "Dash Accel", debugName, NULL, 0, 0.0f, 200.0f ); radDbgWatchAddFloat(&CharacterTune::sfDashDeceleration, "Dash Decel", debugName, NULL, 0, 0.0f, 200.0f ); radDbgWatchAddFloat(&CharacterTune::sfMaxSpeed, "Max Speed", debugName, NULL, 0, 0.0f, 8.0f ); radDbgWatchAddFloat((float*)&CharacterTune::sGetInPosition.x, "Get In Offset X", "Get In/Get Out", NULL, 0, 0.0f, 3.0f ); radDbgWatchAddFloat((float*)&CharacterTune::sGetInPosition.z, "Get In Offset Z", "Get In/Get Out", NULL, 0, -1.5f, 1.5f ); radDbgWatchAddFloat(&CharacterTune::sGetInHeightThreshold, "Height Threshold", "Get In/Get Out", NULL, 0, -2.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sfGetInOutOfCarAnimSpeed, "Speed", "Get In/Get Out", NULL, 0, 15.0f, 240.0f ); radDbgWatchAddFloat(&CharacterTune::sGetInOpenDelay, "Get In Open Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sGetInOpenSpeed, "Get In Open Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sGetInCloseDelay, "Get In Close Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sGetInCloseSpeed, "Get In Close Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sGetOutOpenDelay, "Get Out Open Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sGetOutOpenSpeed, "Get Out Open Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sGetOutCloseDelay, "Get Out Close Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddFloat(&CharacterTune::sGetOutCloseSpeed, "Get Out Close Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f ); radDbgWatchAddBoolean( &CharacterManager::sbFixedSimRate, "Fixed sim rate", debugName, NULL, 0 ); radDbgWatchAddFloat(&CharacterTune::sfDashBurstMax, "Turbo Speed", debugName, NULL, 0, 0.0f, 100.0f ); radDbgWatchAddFloat(&CharacterTune::sfTurboRotateRate, "Turbo Rotate Rate", debugName, NULL, 0, 0.0f, 10.0f ); radDbgWatchAddString(sCharacterToSpawn, 64, "NPC to Spawn", "CharacterManager"); radDbgWatchAddFunction( "Spawn NPC", &Spawn, NULL, "CharacterManager"); radDbgWatchAddFunction( "Next Skin", &NextSkin, NULL, "CharacterManager"); radDbgWatchAddFloat(&CharacterTune::sfKickingForce, "Kicking Force", debugName, NULL, 0, 0.0f, 2000.0f ); radDbgWatchAddFloat(&CharacterTune::sfSlamForce, "Stomping Force", debugName, NULL, 0, 0.0f, 4000.0f ); radDbgWatchAddFloat(&CharacterTune::sfShockTime, "Time(sec) to be shocked",debugName , NULL, NULL ,0 , 10.0f); #ifdef RAD_XBOX extern float g_XBoxMipmapBias; radDbgWatchAddFloat(&g_XBoxMipmapBias, "Mipmap Bias", "XBox", NULL, NULL , -10.0f , 10.0f); #endif #endif //DEBUGWATCH strcpy(mDummyLoadData.modelName, "npd"); strcpy(mDummyLoadData.animName, "npd"); FillLoadData( mDummyLoadData, "npd", "npd"); mGarbageCollect = false; mUniversalPose = new tPose(64); mUniversalPose->AddRef(); mNumCharactersAdded = 0; } CharacterManager::~CharacterManager( void ) { Destroy(true); tRefCounted::Release(mUniversalPose); GetEventManager()->RemoveAll( this ); } void CharacterManager::PreLoad( void ) { // Load the global textures char fname[128]; sprintf(fname, "art\\chars\\global.p3d"); char section[128]; sprintf(section, "Eakkachaichanvet"); p3d::inventory->AddSection(section); GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, fname, GMA_LEVEL_OTHER, section ); // Load in a dummy, lightweight model LoadModel("nps"); LoadModel("ndr"); LoadModel("npd"); // these animation banks are loaded all the time LoadAnimation("nps"); LoadAnimation("ndr"); LoadAnimation("npd"); } void CharacterManager::Destroy(bool permenant) { mUniversalPose->SetSkeleton(NULL); ClearTeleportDests(); if(permenant) { p3d::inventory->RemoveSectionElements( "Eakkachaichanvet" ); p3d::inventory->DeleteSection( "Eakkachaichanvet" ); } int i; for ( i = 0; i < MAX_CHARACTERS; i++ ) { g_Blinkers[ i ].SetCharacter( NULL ); if ( mpCharacter[ i ] != 0 ) { GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(mpCharacter[i]); mpCharacter[ i ]->RemoveFromWorldScene( ); mpCharacter[ i ]->ClearAllActionButtonHandlers(); mpCharacter[ i ]->SetManaged(false); mpCharacter[ i ]->Release(); mpCharacter[ i ] = 0; } // Clean out the load data names, to make sure we clean up all memory in use // mCharacterLoadData[ i ].modelSection.SetText (0); mCharacterLoadData[ i ].animSection.SetText (0); mCharacterLoadData[ i ].animModelSection.SetText (0); mCharacterLoadData[ i ].mModelHigh.SetText (0); mCharacterLoadData[ i ].mModelMedium.SetText (0); mCharacterLoadData[ i ].mModelLow.SetText (0); mCharacterLoadData[ i ].mChoreoName.SetText (0); } for ( i = permenant ? 0 : 3; i < MAX_LOADS; i++ ) { rAssert(mModelData[i].state != LOADING); rAssert(mAnimData[i].state != LOADING); if(mModelData[i].state == LOADED || mModelData[i].state == GARBAGE ) { p3d::inventory->RemoveSectionElements(mModelData[i].section); p3d::inventory->DeleteSection(mModelData[i].section); } if(mAnimData[i].state == LOADED || mAnimData[i].state == GARBAGE ) { p3d::inventory->RemoveSectionElements(mAnimData[i].section); p3d::inventory->DeleteSection(mAnimData[i].section); } mModelData[i].state = NOT_LOADED; mAnimData[i].state = NOT_LOADED; } mNumCharactersAdded = 0; } void CharacterManager::RemoveCharacter( Character* c) { rAssert(c); if(c) { for ( int i = 0; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] == c ) { if(mpCharacter[ i ]->GetRole() == Character::ROLE_COMPLETED_BONUS) { GetVehicleCentral()->RemoveSuppressedDriver(this->mRealModelNames[i]); } // if you get this assert, please leave it running and go get Nigel or Dusit rAssert(mpCharacter[ i ]->GetRole() != Character::ROLE_PEDESTRIAN); g_Blinkers[ i ].SetCharacter( NULL ); GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(mpCharacter[i]); mpCharacter[ i ]->RemoveFromWorldScene( ); GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mpCharacter[ i ]); mpCharacter[ i ]->SetManaged(false); mpCharacter[ i ] = 0; GarbageCollectModel(mCharacterModelData[i]); GarbageCollectAnim(mCharacterAnimData[i]); return; } } } } void CharacterManager::SetGarbage(Character* c, bool garbage) { for( int i = 0; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] == c) { mGarbage[i] = garbage; } } } void CharacterManager::GarbageCollectModel(unsigned modelIndex) { bool modelUsed = (modelIndex < 3); // first three animation are npd, ndr, nps, don't garbage colelct for ( int j = 0; (j < MAX_CHARACTERS) && !modelUsed; j++ ) { if(mpCharacter[j]) { modelUsed |= modelIndex == mCharacterModelData[j]; } } if(!modelUsed) { if(mModelData[modelIndex].state == LOADING) { mModelData[modelIndex].state = LOADING_GARBAGE; mModelData[modelIndex].gracePeriod = 3.0f; } else { mModelData[modelIndex].state = GARBAGE; mModelData[modelIndex].gracePeriod = 3.0f; } } } void CharacterManager::GarbageCollectAnim(unsigned animIndex) { bool animUsed = (animIndex < 3); // first three animation are npd, ndr, nps, don't garbage colelct for ( int j = 0; (j < MAX_CHARACTERS) && !animUsed; j++ ) { if(mpCharacter[j]) { animUsed |= animIndex == mCharacterAnimData[j]; } } if(!animUsed) { if(mAnimData[animIndex].state == LOADING) { mAnimData[animIndex].state = LOADING_GARBAGE; } else { mAnimData[animIndex].state = GARBAGE; } } } bool CharacterManager::IsModelLoaded(const char* name) { unsigned index = FindLoad(mModelData, tEntity::MakeUID(name)); if( index != INVALID_LOAD && mModelData[index].state == LOADED ) { return true; } return false; //return FindLoad(mModelData, tEntity::MakeUID(name)) != INVALID_LOAD; } bool CharacterManager::IsAnimLoaded(const char* name) { unsigned index = FindLoad(mAnimData, tEntity::MakeUID(name)); if( index != INVALID_LOAD && mAnimData[index].state == LOADED ) { return true; } return false; //return FindLoad(mAnimData, tEntity::MakeUID(name)) != INVALID_LOAD; } unsigned CharacterManager::LoadModel(const char* model, LoadingManager::ProcessRequestsCallback* callback, void* userData) { tUID uid = tEntity::MakeUID(model); unsigned loadIndex = AllocLoad(mModelData, uid); if(mModelData[loadIndex].state == GARBAGE) { mModelData[loadIndex].state = LOADED; } if(mModelData[loadIndex].state == LOADING_GARBAGE) { mModelData[loadIndex].state = LOADING; } if((mModelData[loadIndex].state == LOADED) && callback) { callback->OnProcessRequestsComplete(userData); } else { mModelData[loadIndex].callback = callback; mModelData[loadIndex].userData = userData; } if(mModelData[loadIndex].state == NOT_LOADED) { char truncModel[ 32 ]; // filenames are truncated at 6 characters strcpy(truncModel, model); truncModel[6] = 0; char modelName[128]; sprintf(modelName, "art\\chars\\%s_m.p3d", truncModel); char modelSection[128]; sprintf(modelSection, "%s_m", model); mModelData[loadIndex].name = uid; mModelData[loadIndex].section = tEntity::MakeUID(modelSection); mModelData[loadIndex].state = LOADING; HeapMgr()->PushHeap( GMA_LEVEL_OTHER ); p3d::inventory->AddSection(modelSection); HeapMgr()->PopHeap( GMA_LEVEL_OTHER ); GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, modelName, GMA_CHARS_AND_GAGS, modelSection); GetLoadingManager()->AddCallback(this, (void*)loadIndex); } return loadIndex; } unsigned CharacterManager::LoadAnimation(const char* anim) { tUID uid = tEntity::MakeUID(anim); unsigned loadIndex = AllocLoad(mAnimData, uid); if(mAnimData[loadIndex].state == GARBAGE) { mAnimData[loadIndex].state = LOADED; } if(mAnimData[loadIndex].state == LOADING_GARBAGE) { mAnimData[loadIndex].state = LOADING; } if(mAnimData[loadIndex].state == NOT_LOADED) { char truncAnim[ 32 ]; // filenames are truncated at 6 characters strcpy(truncAnim, anim); truncAnim[6] = 0; char animName[128]; char choreoName[128]; sprintf(animName, "art\\chars\\%s_a.p3d", truncAnim); sprintf(choreoName, "art\\chars\\%s.cho", anim); char animSection[128]; sprintf(animSection, "%s_a", anim); mAnimData[loadIndex].name = uid; mAnimData[loadIndex].section = tEntity::MakeUID(animSection); mAnimData[loadIndex].state = LOADING; HeapMgr()->PushHeap( GMA_TEMP ); p3d::inventory->AddSection(animSection); HeapMgr()->PopHeap( GMA_TEMP ); GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, animName, GMA_LEVEL_OTHER, animSection); GetLoadingManager()->AddRequest( FILEHANDLER_CHOREO, choreoName, GMA_LEVEL_OTHER, animSection); GetLoadingManager()->AddCallback(this, (void*)(0x10000000 | loadIndex)); } return loadIndex; } unsigned CharacterManager::FindLoad(Load* loads, tUID name) { for(int i = 0; i < MAX_LOADS; i++) { if(loads[i].name == name) { return i; } } return INVALID_LOAD; } unsigned CharacterManager::AllocLoad(Load* loads, tUID name) { unsigned index = FindLoad(loads, name); if(index == INVALID_LOAD) { for(int i = 0; i < MAX_LOADS; i++) { if(loads[i].state == NOT_LOADED) { loads[i].name = name; return i; } } } else { return index; } rAssertMsg(0, "Can't load any more character data"); return INVALID_LOAD; } CharacterManager::LoadState CharacterManager::GetState(Load* loads, tUID name) { unsigned index = FindLoad(loads, name); if(index != INVALID_LOAD) { return loads[index].state; } return NOT_LOADED; } void CharacterManager::FillLoadData( CharacterLoadData& data, const char* useModel, const char* useAnim) { const int MAX_LEN = 32; char modelName[ MAX_LEN ]; int len = sprintf( modelName, "%s_h", useModel); rAssert( len < MAX_LEN ); data.mModelHigh.SetText( modelName ); len = sprintf( modelName, "%s_m", useModel ); rAssert( len < MAX_LEN ); data.mModelMedium.SetText( modelName ); len = sprintf( modelName, "%s_l", useModel ); rAssert( len < MAX_LEN ); data.mModelLow.SetText( modelName ); data.mChoreoName.SetText( useAnim ); char sec[256]; sprintf(sec, "%s_m", useModel); data.modelSection.SetText(sec); sprintf(sec, "%s_a", useAnim); data.animSection.SetText(sec); sprintf(sec, "%s_m", useAnim); data.animModelSection.SetText(sec); } Character* CharacterManager::AddCharacter( CharacterType type, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location) { unsigned int addCharTime = radTimeGetMicroseconds(); unsigned int MakeUIDTime = radTimeGetMicroseconds(); tUID modelUID = tEntity::MakeUID(modelName); tUID animUID = tEntity::MakeUID(choreoPuppet); MakeUIDTime = radTimeGetMicroseconds() - MakeUIDTime; Character* character; // #ifdef RAD_GAMECUBE // HeapMgr()->PushHeap( GMA_GC_VMM ); // #else HeapMgr()->PushHeap( GMA_LEVEL_OTHER ); // #endif unsigned int NewTime = radTimeGetMicroseconds(); if(type == PC) { if( (int)mNumCharactersAdded >= GetGameplayManager()->GetNumPlayers() ) { rReleasePrintf("Tried to add too many PCs, not supported right now. Check level scrips for multiple AddCharacter calls.\n"); // #ifdef RAD_GAMECUBE // HeapMgr()->PopHeap( GMA_GC_VMM ); // #else HeapMgr()->PopHeap( GMA_LEVEL_OTHER ); // #endif return NULL; } MEMTRACK_PUSH_GROUP( "CharacterManager - Add PC" ); character = new Character; character->mbAllowUnload = false; //character->SetSimpleShadow( false ); } else { rAssert( mNumCharactersAdded == (unsigned int)GetGameplayManager()->GetNumPlayers() ); MEMTRACK_PUSH_GROUP( "CharacterManager - Add NPC" ); character = new NPCharacter; } if((strcmp(modelName, "lisa") == 0) || (strncmp(modelName, "l_", 2) == 0)) { character->SetIsLisa(true); } if((strcmp(modelName, "marge") == 0) || (strncmp(modelName, "m_", 2) == 0)) { character->SetIsMarge(true); } rAssert( character ); NewTime = radTimeGetMicroseconds() - NewTime; // #ifdef RAD_GAMECUBE // HeapMgr()->PopHeap( GMA_GC_VMM ); // #else HeapMgr()->PopHeap( GMA_LEVEL_OTHER ); // #endif unsigned int SetNameTime = radTimeGetMicroseconds(); character->SetName( characterName ); SetNameTime = radTimeGetMicroseconds() - SetNameTime; unsigned int AddCharacterTime = radTimeGetMicroseconds(); int characterIndex = AddCharacter( character, type ); strcpy(mRealModelNames[ characterIndex ], modelName); strcpy(mRealAnimNames[ characterIndex ], choreoPuppet); mLoaded[characterIndex] = true; mGarbage[characterIndex] = false; if ( type == PC ) { rAssert( characterIndex == (int)mNumCharactersAdded ); ++mNumCharactersAdded; } AddCharacterTime = radTimeGetMicroseconds() - AddCharacterTime; unsigned int FillLoadTime = radTimeGetMicroseconds(); strcpy(mCharacterLoadData[characterIndex].modelName, modelName); strcpy(mCharacterLoadData[characterIndex].animName, choreoPuppet); FillLoadData( mCharacterLoadData[characterIndex], modelName, choreoPuppet); FillLoadTime = radTimeGetMicroseconds() - FillLoadTime; unsigned int LoadModelTime = 0; unsigned int SetupCharacterTime; LoadModelTime = radTimeGetMicroseconds(); mCharacterModelData[characterIndex] = LoadModel(modelName); mCharacterAnimData[characterIndex] = LoadAnimation(choreoPuppet); LoadModelTime = radTimeGetMicroseconds() - LoadModelTime; if( (GetState(mAnimData, animUID) == LOADED) && (GetState(mModelData, modelUID) == LOADED) ) { SetupCharacterTime = radTimeGetMicroseconds(); SetupCharacter( mCharacterLoadData[characterIndex], character ); SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime; } else { SetupCharacterTime = radTimeGetMicroseconds(); SetupCharacter( mDummyLoadData, character ); SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime; } unsigned int InitTime = radTimeGetMicroseconds(); character->Init(); InitTime = radTimeGetMicroseconds() - InitTime; if(type == NPC) { // Dusit [Oct 22, 2002]: // Don't solve collisions for NPCs.. They just sit there... // We'll enable solve collisions as needed... character->SetSolveCollisions( false ); } else { // PCs get immediatly added to scene, NPCs might not be visible (i.e pedestrians) character->AddToWorldScene(); } if(location) { //Set the initial character position. Locator* loc = p3d::find( location ); CarStartLocator* cloc = dynamic_cast( loc ); rmt::Vector position; if ( cloc ) { cloc->GetLocation( &position ); character->RelocateAndReset( position, cloc->GetRotation() ); } else if ( loc ) { rmt::Vector position; loc->GetLocation( &position ); character->RelocateAndReset( position, 0 ); } else { rDebugPrintf( "Couldn't find locator \"%s\" for %s\n", location, characterName ); } // if NPC, add the locator as the first waypoint of this character if( loc && type == NPC ) { // the controller should have been created in NPC constructor NPCController* npcController = (NPCController*) character->GetController(); rAssert( npcController ); bool res = npcController->AddNPCWaypoint( position ); rAssert( res ); } } addCharTime = radTimeGetMicroseconds() - addCharTime; #ifdef OUTPUT_TIMES rReleasePrintf("AddCharacter %s Time %d\n",characterName, addCharTime); rReleasePrintf(" AddCharacter MakeUIDTime %s Time %d\n",characterName, MakeUIDTime); rReleasePrintf(" AddCharacter NewTime %s Time %d\n",characterName, NewTime); rReleasePrintf(" AddCharacter SetNameTime %s Time %d\n",characterName, SetNameTime); rReleasePrintf(" AddCharacter AddCharacterTime %s Time %d\n",characterName, AddCharacterTime); rReleasePrintf(" AddCharacter FillLoadTime %s Time %d\n",characterName, FillLoadTime); rReleasePrintf(" AddCharacter LoadModelTime %s Time %d\n",characterName, LoadModelTime); rReleasePrintf(" AddCharacter SetupCharacterTime %s Time %d\n",characterName, SetupCharacterTime); rReleasePrintf(" AddCharacter InitTime %s Time %d\n",characterName, InitTime); #endif if(type == PC) { MEMTRACK_POP_GROUP( "CharacterManager - Add PC" ); } else { MEMTRACK_POP_GROUP( "CharacterManager - Add NPC" ); } if(type == PC && GetGameplayManager()->IsSuperSprint() == false && GetGameplayManager()->mIsDemo == false) { //check to see if there is a valid skin that is not called NULL if(strcmp(GetCharacterSheetManager()->QueryCurrentSkin(GetGameplayManager()->GetCurrentLevelIndex()),"NULL") != 0) { SwapData(character, GetCharacterSheetManager()->QueryCurrentSkin(GetGameplayManager()->GetCurrentLevelIndex()), GetAnimName(character)); } } return character; } Character* CharacterManager::AddCharacterDeferedLoad( CharacterType type, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location) { Character* c = AddCharacter(type, characterName, "npd", "npd", location); unsigned characterIndex = INVALID_LOAD; for ( int i = 0; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] == c ) { characterIndex = i; break; } } rAssert(characterIndex != INVALID_LOAD); strcpy(mRealModelNames[ characterIndex ], modelName); strcpy(mRealAnimNames[ characterIndex ], choreoPuppet); mLoaded[characterIndex] = false; return c; } void CharacterManager::SwapData(Character* character, const char* modelName, const char* choreoPuppet) { unsigned index = InternalSwapData(character, modelName, choreoPuppet); rAssert(index != INVALID_LOAD); strcpy(mRealModelNames[ index ], modelName); strcpy(mRealAnimNames[ index ], choreoPuppet); } unsigned CharacterManager::InternalSwapData(Character* character, const char* modelName, const char* choreoPuppet) { unsigned int SwapDataTime = radTimeGetMicroseconds(); unsigned characterIndex = INVALID_LOAD; for ( int i = 0; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] == character ) { characterIndex = i; break; } } rAssert(characterIndex != INVALID_LOAD); tUID modelUID; tUID animUID; unsigned modelIndex = mCharacterModelData[characterIndex]; unsigned animIndex = mCharacterAnimData[characterIndex]; if(modelName) { modelUID = tEntity::MakeUID(modelName); strcpy(mCharacterLoadData[characterIndex].modelName, modelName); } else { modelUID = tEntity::MakeUID(mCharacterLoadData[characterIndex].modelName); } if(choreoPuppet) { animUID = tEntity::MakeUID(choreoPuppet); strcpy(mCharacterLoadData[characterIndex].animName, choreoPuppet); } else { animUID = tEntity::MakeUID(mCharacterLoadData[characterIndex].animName); } unsigned int FillLoadTime = radTimeGetMicroseconds(); FillLoadData( mCharacterLoadData[characterIndex], mCharacterLoadData[characterIndex].modelName, mCharacterLoadData[characterIndex].animName); FillLoadTime = radTimeGetMicroseconds() - FillLoadTime; unsigned int LoadModelTime = 0; unsigned int SetupCharacterTime = 0; unsigned int CreatePuppetTime = 0; LoadModelTime = radTimeGetMicroseconds(); mCharacterModelData[characterIndex] = LoadModel(mCharacterLoadData[characterIndex].modelName); mCharacterAnimData[characterIndex] = LoadAnimation(mCharacterLoadData[characterIndex].animName); LoadModelTime = radTimeGetMicroseconds() - LoadModelTime; if((GetState(mAnimData, animUID) == LOADED) && (GetState(mModelData, modelUID) == LOADED)) { SetupCharacterTime = radTimeGetMicroseconds(); CreatePuppetTime = SetupCharacter( mCharacterLoadData[characterIndex], character ); SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime; } else { SetupCharacterTime = radTimeGetMicroseconds(); CreatePuppetTime = SetupCharacter( mDummyLoadData, character ); SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime; } unsigned int GarbageCollectTime = radTimeGetMicroseconds(); GarbageCollectModel(modelIndex); GarbageCollectAnim(animIndex); GarbageCollectTime = radTimeGetMicroseconds() - GarbageCollectTime; SwapDataTime = radTimeGetMicroseconds() - SwapDataTime; #ifdef OUTPUT_TIMES rReleasePrintf("SwapDataTime %s Time %d\n",modelName, SwapDataTime); rReleasePrintf(" FillLoadTime %s Time %d\n",modelName, FillLoadTime); if( LoadModelTime != 0 ) rReleasePrintf(" LoadModelTime %s Time %d\n",modelName, LoadModelTime); rReleasePrintf(" GarbageCollectTime %s Time %d\n",modelName, GarbageCollectTime ); rReleasePrintf(" SetupCharacterTime %s Time %d\n",modelName, SetupCharacterTime ); rReleasePrintf(" CreatePuppetTime %s Time %d\n\n",modelName, CreatePuppetTime ); #endif return characterIndex; } //============================================================================= // CharacterManager::SetupCharacter //============================================================================= // Description: Comment // // Parameters: ( CharacterLoadData& data, Character* pCharacter, bool isNPC ) // // Return: void // //============================================================================= unsigned int CharacterManager::SetupCharacter( CharacterLoadData& data, Character* pCharacter) { p3d::inventory->PushSection(); bool currOnly = p3d::inventory->GetCurrentSectionOnly(); tName curr = p3d::inventory->GetCurrentSection()->GetName(); p3d::inventory->SetCurrentSectionOnly(true); // #ifdef RAD_GAEMCUBE // HeapMgr()->PushHeap( GMA_GC_VMM ); // #else HeapMgr()->PushHeap( GMA_LEVEL_OTHER ); // #endif MEMTRACK_PUSH_GROUP( "CharacterManager - CharacterRenderable" ); p3d::inventory->SelectSection(data.modelSection.GetUID()); CharacterRenderable* pCharacterRenderable; if(data.modelSection.GetUID() != tEntity::MakeUID("npd_m")) { pCharacterRenderable = new CharacterRenderable( p3d::find( data.mModelHigh.GetUID() ), p3d::find( data.mModelMedium.GetUID() ), p3d::find( data.mModelLow.GetUID() )); pCharacterRenderable->SetSwatchShader( p3d::find( "char_swatches_lit_m" ) ); } else { pCharacterRenderable = new CharacterRenderable(NULL, NULL, NULL); pCharacterRenderable->SetSwatchShader( NULL ); } pCharacterRenderable->SetShadowColour( pCharacter->GetShadowColour() ); // Lets find the electrocution effect p3d::inventory->SelectSection( P3D_DEFAULT_INV_SECTION ); p3d::inventory->SetCurrentSectionOnly( false ); pCharacterRenderable->SetShockEffect( p3d::find< tDrawable >( "electrocuted" ) ); p3d::inventory->SetCurrentSectionOnly( true ); p3d::inventory->SelectSection( "Eakkachaichanvet" ); pCharacterRenderable->SetSwatchTexture( 0, p3d::find( "char_swatches_lit.bmp" ) ); pCharacterRenderable->SetSwatchTexture( 1, p3d::find( "char_swatches1.bmp" ) ); pCharacterRenderable->SetSwatchTexture( 2, p3d::find( "char_swatches2.bmp" ) ); pCharacterRenderable->SetSwatchTexture( 3, p3d::find( "char_swatches3.bmp" ) ); pCharacterRenderable->SetSwatchTexture( 4, p3d::find( "char_swatches4.bmp" ) ); pCharacterRenderable->SetSwatch( 0 ); p3d::inventory->SelectSection( data.modelSection.GetUID() ); pCharacter->SetDrawable( pCharacterRenderable ); MEMTRACK_POP_GROUP("CharacterManager - CharacterRenderable"); tDrawablePose* pDrawablePose = pCharacterRenderable->GetDrawable(); tSkeleton* pSkeleton = NULL; if(pDrawablePose) { pSkeleton = pDrawablePose->GetSkeleton(); } else { p3d::inventory->SelectSection(data.animModelSection.GetUID()); pSkeleton = p3d::find( data.mChoreoName.GetUID() ); } if(!pSkeleton) { tUID uid = tEntity::MakeUID("npd"); p3d::inventory->SelectSection(uid); pSkeleton = p3d::find(uid); } rAssert( pSkeleton); p3d::inventory->SelectSection(data.animSection.GetUID()); choreo::Bank* bank = p3d::find( data.mChoreoName.GetUID() ); rAssert( bank != 0 ); MEMTRACK_PUSH_GROUP( "CharacterManager - Puppet" ); unsigned int PuppetTime = radTimeGetMicroseconds(); // Make sure the skeleton to be remapped are equivalent. // choreo::Rig* rig = bank->GetRig(); rAssert( rig != 0 ); tSkeleton* rigSkeleton = rig->GetSkeleton( ); int i = 0; if ( rigSkeleton->GetNumJoint() != pSkeleton->GetNumJoint() ) { rDebugPrintf( "%s skeleton is not the same as choreo::Rig skeleton %s. Animations will be hooped.\n", rigSkeleton->GetName( ), pSkeleton->GetName( ) ); } mUniversalPose->SetSkeleton(pSkeleton); mUniversalPose->ResetToRestPose(); pCharacter->SetInCar(false); pCharacter->SetPuppet( new choreo::Puppet( mUniversalPose, bank, false, 0x20, 5 ) ); choreo::Puppet* pPuppet = pCharacter->GetPuppet(); PuppetTime = radTimeGetMicroseconds() - PuppetTime; // #ifdef RAD_GAEMCUBE // HeapMgr()->PopHeap( GMA_GC_VMM ); // #else HeapMgr()->PopHeap( GMA_LEVEL_OTHER ); // #endif MEMTRACK_POP_GROUP( "CharacterManager - Puppet" ); // // Determine & store y offset needed for this character // float yAdjustDrawable = 0.0f, yAdjustPuppet = 0.0f; if( pCharacterRenderable->GetDrawable() && pCharacterRenderable->GetDrawable()->GetSkeleton() ) { yAdjustDrawable = pCharacterRenderable->GetDrawable()->GetSkeleton()-> FindJoint( tEntity::MakeUID("Balance_Root") )->worldMatrix.Row(3).y; } if( pPuppet && pPuppet->GetSkeleton() ) { yAdjustPuppet = pPuppet->GetSkeleton()-> FindJoint( tEntity::MakeUID("Balance_Root") )->worldMatrix.Row(3).y; } pCharacter->SetYAdjust((yAdjustDrawable - yAdjustPuppet) * 0.30f); // // Setup the blinker for this character // unsigned int characterIndex = GetCharacterIndex( pCharacter ); g_Blinkers[ characterIndex ].SetCharacter( pCharacter ); p3d::inventory->SetCurrentSectionOnly(currOnly); p3d::inventory->SelectSection( curr ); p3d::inventory->PopSection(); return PuppetTime; } void CharacterManager::GarbageCollect(bool ignoreDist) { int i; const float GARBAGE_COLLECT_DISTANCE = 100.0f; // garbage collect if(mGarbageCollect) { for(i = MAX_PLAYERS; i < MAX_CHARACTERS; i++) { if(mpCharacter[i]) { rmt::Vector charPos; rmt::Vector camPos; rmt::Vector distance; mpCharacter[i]->GetPosition(charPos); // camPos = GetSuperCamManager()->GetSCC(0)->GetCamera()->GetPosition(); GetCharacter(0)->GetPosition(camPos); distance.Sub(camPos, charPos); float fDist = rmt::Abs(distance.Magnitude()); if(fDist > 10000) { continue; } bool suppressLoad = false; if(mpCharacter[i]->GetRole() == Character::ROLE_ACTIVE_BONUS) { if(!GetGameplayManager()->GetCurrentMission()->IsBonusMission() && !GetGameplayManager()->GetCurrentMission()->IsRaceMission() && !GetGameplayManager()->GetCurrentMission()->IsWagerMission() && !GetGameplayManager()->GetCurrentMission()->IsSundayDrive()) { suppressLoad = true; } } if(mGarbage[i]) { if(fDist > GARBAGE_COLLECT_DISTANCE || GetGameplayManager()->IsIrisClosed() || ignoreDist ) { if(mpCharacter[i]->GetRole() != Character::ROLE_ACTIVE_BONUS) { if(mpCharacter[i]->IsAmbient()) { mGarbage[i] = false; mpCharacter[i]->EnableAmbientDialogue(true); mpCharacter[i]->ResetAmbientPosition(); } else { RemoveCharacter(mpCharacter[i]); } } else { mGarbage[i] = false; } } } else if (mLoaded[i] && (fDist > 150) && mpCharacter[i]->mbAllowUnload) { InternalSwapData(mpCharacter[i], "npd", "npd"); mLoaded[i] = false; mpCharacter[i]->RemoveFromPhysics(); } else if (!mLoaded[i] && (fDist < 150) && !suppressLoad) { if( !CommandLineOptions::Get( CLO_NO_PEDS ) ) InternalSwapData(mpCharacter[i], mRealModelNames[i], mRealAnimNames[i]); mLoaded[i] = true; if(!mpCharacter[i]->IsInCar()) { mpCharacter[i]->AddToPhysics(); } } } } for(i = 1; i < MAX_LOADS; i++) { if(mModelData[i].state == GARBAGE) { if(mModelData[i].gracePeriod < 0.0f) { p3d::inventory->RemoveSectionElements(mModelData[i].section); p3d::inventory->DeleteSection(mModelData[i].section); mModelData[i].state = NOT_LOADED; } } if(mAnimData[i].state == GARBAGE) { if(mAnimData[i].gracePeriod < 0.0f) { p3d::inventory->RemoveSectionElements(mAnimData[i].section); p3d::inventory->DeleteSection(mAnimData[i].section); mAnimData[i].state = NOT_LOADED; } } } } } /* ============================================================================== CharacterManager::PreSimUpdate ============================================================================== Description: Comment Parameters: ( float timeins ) Return: void ============================================================================= */ void CharacterManager::PreSimUpdate( float timeins ) { int i; for ( i = 1; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] && (mpCharacter[ i ]->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) ) { BEGIN_PROFILE("Character::PreSimUpdate") mpCharacter[ i ]->PreSimUpdate( timeins ); END_PROFILE("Character::PreSimUpdate") } } for(i = 1; i < MAX_LOADS; i++) { if(mModelData[i].state == GARBAGE) { mModelData[i].gracePeriod -= timeins; } if(mAnimData[i].state == GARBAGE) { mAnimData[i].gracePeriod -= timeins; } } } /* ============================================================================== CharacterManager::PostSimUpdate ============================================================================== Description: Comment Parameters: ( float timeins ) Return: void ============================================================================= */ void CharacterManager::PostSimUpdate( float timeins ) { int i; for ( i = 1; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] && (mpCharacter[ i ]->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) ) { BEGIN_PROFILE("Character::PostSimUpdate") mpCharacter[ i ]->PostSimUpdate( timeins ); END_PROFILE("Character::PostSimUpdate") } } } /* ============================================================================== CharacterManager::Update ============================================================================== Description: Comment Parameters: ( float timeins ) Return: void ============================================================================= */ void CharacterManager::Update( float timeins ) { int i; for( i = 0; i < MAX_CHARACTERS; i++ ) { Character* character = mpCharacter[i]; if( character && (character->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) ) { g_Blinkers[ i ].Update( static_cast< int >( timeins * 1000 ) ); int collisionAreaIndex = character->GetCollisionAreaIndex(); if( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA ) { BEGIN_PROFILE("Character::UpdatePhObjects"); character->UpdatePhysicsObjects( timeins, collisionAreaIndex ); END_PROFILE("Character::UpdatePhObjects"); } character->UpdateRoot( timeins ); sim::SimState* simState = character->GetSimState(); if( simState != NULL && simState->GetControl() == sim::simSimulationCtrl ) { simState->GetSimulatedObject()->Update( timeins ); character->UpdateSimState( timeins ); sim::CollisionObject* collObj = simState->GetCollisionObject(); collObj->Update(); } } } } /* ============================================================================== CharacterManager::PreSubstepUpdate ============================================================================== Description: Comment Parameters: ( float timeins ) Return: void ============================================================================= */ void CharacterManager::PreSubstepUpdate( float timeins ) { for( int i=0; iIsInSubstep() ) { BEGIN_PROFILE("Character::PreSimUpdate") mpCharacter[ i ]->PreSimUpdate( timeins ); END_PROFILE("Character::PreSimUpdate") } } } /* ============================================================================== CharacterManager::PostSubstepUpdate ============================================================================== Description: Comment Parameters: ( float timeins ) Return: void ============================================================================= */ void CharacterManager::PostSubstepUpdate( float timeins ) { if ( mpCharacter[ 0 ] ) { BEGIN_PROFILE("Character::PostSimUpdate") mpCharacter[ 0 ]->PostSimUpdate( timeins ); END_PROFILE("Character::PostSimUpdate") } } void CharacterManager::ClearTargetVehicle(Vehicle* v) { for (int i = 0; i < MAX_CHARACTERS; i++ ) { if ( NULL != mpCharacter[ i ] ) { if(mpCharacter[ i ]->GetTargetVehicle() == v) { mpCharacter[ i ]->SetTargetVehicle(NULL); } } } } void CharacterManager::ResetBonusCharacters(void) { for (int i = 0; i < MAX_CHARACTERS; i++ ) { if ( NULL != mpCharacter[ i ] ) { if(mpCharacter[ i ]->GetRole() == Character::ROLE_ACTIVE_BONUS) { ((NPCController*)(mpCharacter[ i ]->GetController()))->TeleportToPath(); } } } } /* ============================================================================== CharacterManager::AddCharacter ============================================================================== Description: Add a character to the array. Returns true if succesfully added. Parameters: ( Character* pCharacter, int& number, bool isNPC ) Return: bool ============================================================================= */ int CharacterManager::AddCharacter( Character* pCharacter, CharacterType type ) { int i = 0; // if it's an NPC, skip the first couple of entries (reserved for players) if ( type == NPC ) { i = MAX_PLAYERS; } // find an empty slot for (; i < MAX_CHARACTERS; i++ ) { if ( 0 == mpCharacter[ i ] ) { break; } } rAssertMsg(( i < MAX_CHARACTERS ), "Couldn't add character, no space in character array"); tRefCounted::Assign( mpCharacter[ i ], pCharacter ); mpCharacter[ i ]->SetManaged(true); mGarbage[i] = false; return i; } void CharacterManager::OnProcessRequestsComplete( void* pUserData ) { unsigned index = (unsigned)pUserData; if(index & 0x10000000) { index &= 0x0fffffff; if(mAnimData[index].state == LOADING) { mAnimData[index].state = LOADED; for(int i = 0; i < MAX_CHARACTERS; i++) { if(mpCharacter[i] && (mCharacterAnimData[i] == index)) { if(mModelData[mCharacterModelData[i]].state == LOADED) { unsigned int PuppetTime = SetupCharacter(mCharacterLoadData[i], mpCharacter[i] ); #ifdef OUTPUT_TIMES rReleasePrintf( "A) Calling SetupCharacter from OnProcessRequestsComplete for %s took %d\n", mCharacterLoadData[i].modelName, PuppetTime ); #endif } } } } else if(mAnimData[index].state == LOADING_GARBAGE) { mAnimData[index].state = GARBAGE; } else { rAssert(0); } } else { if(mModelData[index].state == LOADING) { mModelData[index].state = LOADED; for(int i = 0; i < MAX_CHARACTERS; i++) { if(mpCharacter[i] && (mCharacterModelData[i] == index)) { if(mAnimData[mCharacterAnimData[i]].state == LOADED) { unsigned int PuppetTime = SetupCharacter(mCharacterLoadData[i], mpCharacter[i] ); #ifdef OUTPUT_TIMES rReleasePrintf( "B) Calling SetupCharacter from OnProcessRequestsComplete for %s took %d\n", mCharacterLoadData[i].modelName, PuppetTime ); #endif } } } if(mModelData[index].callback) { mModelData[index].callback->OnProcessRequestsComplete(mModelData[index].userData); mModelData[index].callback = NULL; } } else if(mModelData[index].state == LOADING_GARBAGE) { mModelData[index].state = GARBAGE; } else { rAssert(0); } } } //============================================================================= Character* CharacterManager::GetCharacterByName( const char* name ) const { return GetCharacterByName( tEntity::MakeUID( name ) ); } /* ============================================================================== CharacterManager::GetCharacterByName ============================================================================== Description: Comment Parameters: ( const tUID uid ) Return: Character ============================================================================= */ Character* CharacterManager::GetCharacterByName( const tUID uid ) const { int i; for( i = 0; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] != 0 && mpCharacter[ i ]->GetUID() == uid ) { return mpCharacter[ i ]; } } return NULL; } //============================================================================= Character* CharacterManager::GetMissionCharacter( const char* name ) const { Character* c = NULL; // pick an appropriate name based on if we are a bonus mission or not char n[64]; if(GetGameplayManager()->GetCurrentMission()->IsBonusMission()) { sprintf(n, "b_%s", name); c = GetCharacterManager()->GetCharacterByName( n ); } if(!c) { sprintf(n, "reward_%s", name); c = GetCharacterManager()->GetCharacterByName( n ); } if(!c) { c = GetCharacterManager()->GetCharacterByName( name ); } return c; } /* ============================================================================== CharacterManager::GetCharacter ============================================================================== Description: Return a pointer to the character. Parameters: ( int i ) Return: Character ============================================================================= */ Character* CharacterManager::GetCharacter( int i ) const { if ( i < MAX_CHARACTERS ) { return mpCharacter[ i ]; } else { return NULL; } } /* ============================================================================== CharacterManager::HandleEvent ============================================================================== Description: Comment Parameters: ( EventEnum id, void* pEventData ) Return: void ============================================================================= */ void CharacterManager::HandleEvent( EventEnum id, void* pEventData ) { switch ( id ) { case EVENT_GETINTOVEHICLE_START : { Character* c = (Character*)pEventData; if(c->GetTargetVehicle()) { if(c->GetTargetVehicle()->mpDriver && (c->GetTargetVehicle()->mpDriver != c)) { c->GetTargetVehicle()->mpDriver->GetController()->SetIntention(CharacterController::WaveHello); } } } break; case EVENT_GETOUTOFVEHICLE_END : { Character* c = (Character*)pEventData; if(c->GetTargetVehicle()) { if(c->GetTargetVehicle()->mpDriver && (c->GetTargetVehicle()->mpDriver != c)) { c->GetTargetVehicle()->mpDriver->GetController()->SetIntention(CharacterController::WaveGoodbye); } } } break; case EVENT_TOGGLE_FIRSTPERSON : { CGuiScreenHud* currentHud = GetCurrentHud(); if(pEventData == 0) { if(GetCharacter(0)->GetActionButtonHandler()) { if ( GetCharacter(0)->GetActionButtonHandler()->IsInstanceEnabled() ) { if( currentHud != NULL ) { currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON ); } } } } else { if( currentHud != NULL ) { currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON ); } } } break; case EVENT_STAGE_COMPLETE : { if(pEventData) { Character* ch = GetCharacter(0); ch->GetController()->SetIntention(CharacterController::CelebrateSmall); } } break; case EVENT_CARD_COLLECTED : case EVENT_MISSION_SUCCESS : { Character* ch = GetCharacter(0); ch->GetController()->SetIntention(CharacterController::CelebrateBig); } break; case EVENT_MISSION_CHARACTER_RESET : { if(pEventData && (sInitialWalkLocator[0] != 0)) { rmt::Vector dest; Locator* l = p3d::find(sInitialWalkLocator); if(l) { l->GetLocation(&dest); Character* c = GetCharacterManager()->GetCharacter(0); if(c->GetWalkerLocomotionAction()->GetStatus() != RUNNING) { c->GetWalkerLocomotionAction()->SetStatus(SLEEPING); } Sequencer* pSeq = c->GetActionController()->GetNextSequencer(); pSeq->BeginSequence(); pSeq->AddAction( new Arrive( c, dest) ); pSeq->AddAction( c->GetWalkerLocomotionAction() ); pSeq->EndSequence( ); } } sInitialWalkLocator[0] = 0; } break; case EVENT_LOCATOR + LocatorEvent::CAR_DOOR: { EventLocator* pLocator = static_cast( pEventData ); rAssert( pLocator ); unsigned int playerId = pLocator->GetPlayerID(); Character* pCharacter = GetCharacter( playerId ); unsigned int actionId = pLocator->GetData( ); rAssert( actionId >= 0 ); ActionButton::GetInCar* pGetInCarAction = static_cast( GetActionButtonManager()->GetActionByIndex( actionId ) ); rAssert( dynamic_cast( pGetInCarAction ) ); rAssert( pGetInCarAction ); if ( pLocator->GetPlayerEntered() ) { // Entered a door volume. // #ifdef RAD_DEBUG int vehicleId = pGetInCarAction->GetVehicleId(); Vehicle* pVehicle = GetVehicleCentral()->GetVehicle( vehicleId ); rAssert( pVehicle ); #endif pCharacter->AddActionButtonHandler( pGetInCarAction ); pGetInCarAction->Enter( pCharacter ); // TODO: Think about what will happen when the character is in two vols. // at the same time. // } else if ( !pLocator->GetPlayerEntered() ) { // Exited a door volume. // pGetInCarAction->Exit( pCharacter ); pCharacter->RemoveActionButtonHandler( pGetInCarAction ); } break; } case EVENT_LOCATOR + LocatorEvent::BOUNCEPAD: { EventLocator* pLocator = static_cast( pEventData ); if ( pLocator->GetPlayerEntered() ) { rAssert( pLocator ); unsigned int playerId = pLocator->GetPlayerID(); Character* pCharacter = GetCharacter( playerId ); ActionButton::Bounce::OnEnter( pCharacter, pLocator ); } break; } case EVENT_LOCATOR + LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT: { EventLocator* pLocator = static_cast( pEventData ); rAssert( pLocator ); if ( pLocator->GetPlayerEntered() ) { unsigned int playerId = pLocator->GetPlayerID(); Character* pCharacter = GetCharacter( playerId ); unsigned int actionId = pLocator->GetData( ); rAssert( actionId >= 0 ); ActionButton::GenericEventButtonHandler* pTED = static_cast( GetActionButtonManager()->GetActionByIndex( actionId ) ); rAssert( dynamic_cast< ActionButton::GenericEventButtonHandler* > (pTED) ); rAssert( pTED ); if ( pCharacter != static_cast(pTED->GetEventData()) ) { pCharacter->AddActionButtonHandler( pTED ); pTED->Enter( pCharacter ); } } else { unsigned int playerId = pLocator->GetPlayerID(); Character* pCharacter = GetCharacter( playerId ); unsigned int actionId = pLocator->GetData( ); rAssert( actionId >= 0 ); ActionButton::GenericEventButtonHandler* pTED = static_cast( GetActionButtonManager()->GetActionByIndex( actionId ) ); if ( pTED && (pCharacter != static_cast(pTED->GetEventData())) ) { rAssert( dynamic_cast( pTED ) != NULL ); pTED->Exit( pCharacter ); pCharacter->RemoveActionButtonHandler( pTED ); } } break; } case EVENT_DEATH_VOLUME_SCREEN_BLANK: { //TODO: Make the blends go away. //Also, cut the camera properly... EventLocator* pLocator = static_cast( pEventData ); if( pLocator == NULL ) { break; } if ( pLocator->GetPlayerID() >= static_cast(MAX_PLAYERS) ) { //Ignore this! break; } //Ignore when player leaves. if ( pLocator->GetPlayerEntered() ) { unsigned int playerId = pLocator->GetPlayerID(); Character* pCharacter = GetCharacter( playerId ); //Only do this if he's on foot. if ( pCharacter->GetStateManager()->GetState() == CharacterAi::GET_IN) { GetAvatarManager()->PutCharacterInCar(pCharacter, pCharacter->GetTargetVehicle()); GetEventManager()->TriggerEvent(EVENT_GETINTOVEHICLE_END, pCharacter); } else { if ( pCharacter->GetStateManager()->GetState() == CharacterAi::GET_OUT ) { GetAvatarManager()->PutCharacterOnGround( pCharacter, pCharacter->GetTargetVehicle() ); } else if ( pCharacter->IsInCar() ) { return; } rmt::Matrix mat = pLocator->GetMatrix(); rmt::Vector facing = mat.Row(2); rmt::Vector pos; pLocator->GetLocation( &pos ); float fDesiredDir = choreo::GetWorldAngle( facing.x, facing.z ); pCharacter->RelocateAndReset( pos, fDesiredDir, true, false ); GetSuperCamManager()->GetSCC( 0 )->DoCameraCut(); } } break; } default: { break; } } } const char* CharacterManager::GetModelName(Character* c) { for(int i = 0; i < MAX_CHARACTERS; i++) { if(c == mpCharacter[i]) { return mRealModelNames[i]; } } return NULL; } const char* CharacterManager::GetAnimName(Character* c) { for(int i = 0; i < MAX_CHARACTERS; i++) { if(c == mpCharacter[i]) { return mRealAnimNames[i]; } } return NULL; } /* ============================================================================== CharacterManager::SubmitStatics ============================================================================== Description: Comment Parameters: ( void ) Return: void ============================================================================= */ void CharacterManager::SubmitStatics( void ) { int i; for ( i = 0; i < MAX_CHARACTERS; i++ ) { Character* character = mpCharacter[i]; if( character != NULL ) { character->SubmitStatics(); } } } /* ============================================================================== CharacterManager::SubmitAnimCollisions ============================================================================== Description: Comment Parameters: ( void ) Return: void ============================================================================= */ void CharacterManager::SubmitAnimCollisions( void ) { int i; for ( i = 0; i < MAX_CHARACTERS; i++ ) { if ( mpCharacter[ i ] ) { mpCharacter[ i ]->SubmitAnimCollisions(); } } } /* ============================================================================== CharacterManager::SubmitDynamics ============================================================================== Description: Comment Parameters: ( void ) Return: void ============================================================================= */ void CharacterManager::SubmitDynamics( void ) { int i; for ( i = 0; i < MAX_CHARACTERS; i++ ) { Character* character = mpCharacter[i]; if( character != NULL ) { character->SubmitDynamics(); } } } /* ============================================================================== CharacterManager::SetCharacterPosition ============================================================================== Description: Comment Parameters: ( int argc, char** argv ) Return: void ============================================================================= */ void CharacterManager::SetCharacterPosition( int argc, char** argv ) { int index = ::atoi( argv[ 1 ] ); Character* pCharacter = GetCharacterManager()->GetCharacter( index ); if ( pCharacter ) { rmt::Vector pos; pos.x = static_cast( atoi( argv[ 2 ] ) ); pos.y = 0.0f; pos.z = static_cast( atoi( argv[ 3 ] ) ); pCharacter->SetPosition( pos ); } } /* ============================================================================== CharacterManager::ResetCharacter ============================================================================== Description: Comment Parameters: ( int argc, char** argv ) Return: void ============================================================================= */ void CharacterManager::ResetCharacter( int argc, char** argv ) { int index = ::atoi( argv[ 1 ] ); Character* pCharacter = GetCharacterManager()->GetCharacter( index ); Locator* loc = p3d::find( argv[2] ); if ( pCharacter && loc ) { rmt::Vector pos; loc->GetLocation( &pos ); pCharacter->RelocateAndReset(pos, pCharacter->GetDesiredDir()); } } char CharacterManager::sInitialWalkLocator[64] = ""; void CharacterManager::SetInitialWalk(int argc, char** argv) { strcpy(sInitialWalkLocator, argv[1]); } char CharacterManager::sCharacterToSpawn[64] = "homer"; Character* CharacterManager::sSpawnedCharacter = NULL; void CharacterManager::Spawn(void*) { if(sSpawnedCharacter) { GetCharacterManager()->RemoveCharacter(sSpawnedCharacter); sSpawnedCharacter = NULL; } sSpawnedCharacter = GetCharacterManager()->AddCharacter(NPC, "dummy", sCharacterToSpawn, "npd", "next_to_pc"); rmt::Vector position; rmt::Vector facing; GetCharacterManager()->GetCharacter(0)->GetPosition( &position ); GetCharacterManager()->GetCharacter(0)->GetFacing( facing ); position += facing; position += facing; sSpawnedCharacter->RelocateAndReset( position, 0); if(sSpawnedCharacter) sSpawnedCharacter->AddToWorldScene(); } static const int sNumBartSkins = 7; static int sCurBartSkin = 1; static char sBartSkins [sNumBartSkins][16] = { "bart", "b_man", "b_military", "b_ninja", "b_football", "b_tall", "b_hugo" }; static const int sNumHomerSkins = 7; static int sCurHomerSkin = 1; static char sHomerSkins [sNumHomerSkins][16] = { "homer", "h_fat", "h_donut", "h_stcrobe", "h_undrwr", "h_scuzzy", "h_evil" }; static const int sNumLisaSkins = 4; static int sCurLisaSkin = 1; static char sLisaSkins [sNumLisaSkins][16] = { "lisa", "l_cool", "l_florida", "l_jersey" }; static const int sNumMargeSkins = 4; static int sCurMargeSkin = 1; static char sMargeSkins [sNumMargeSkins][16] = { "marge", "m_police", "m_pink", "m_prison" }; static const int sNumApuSkins = 4; static int sCurApuSkin = 1; static char sApuSkins [sNumApuSkins][16] = { "apu", "a_american", "a_besharp", "a_army" }; void CharacterManager::NextSkin( void* ) { tUID bart = tEntity::MakeUID("bart"); tUID homer = tEntity::MakeUID("homer"); tUID lisa = tEntity::MakeUID("lisa"); tUID marge = tEntity::MakeUID("marge"); tUID apu = tEntity::MakeUID("apu"); Character* pc = GetCharacterManager()->GetCharacter(0); if(pc->GetUID() == bart) { GetCharacterManager()->SwapData(pc, sBartSkins[sCurBartSkin], "bart"); sCurBartSkin = (sCurBartSkin + 1) % sNumBartSkins; } if(pc->GetUID() == homer) { GetCharacterManager()->SwapData(pc, sHomerSkins[sCurHomerSkin], "homer"); sCurHomerSkin = (sCurHomerSkin + 1) % sNumHomerSkins; } if(pc->GetUID() == lisa) { GetCharacterManager()->SwapData(pc, sLisaSkins[sCurLisaSkin], "lisa"); sCurLisaSkin = (sCurLisaSkin + 1) % sNumLisaSkins; } if(pc->GetUID() == marge) { GetCharacterManager()->SwapData(pc, sMargeSkins[sCurMargeSkin], "marge"); sCurMargeSkin = (sCurMargeSkin + 1) % sNumMargeSkins; } if(pc->GetUID() == apu) { GetCharacterManager()->SwapData(pc, sApuSkins[sCurApuSkin], "apu"); sCurApuSkin = (sCurApuSkin + 1) % sNumApuSkins; } } static const int s_numCheatModels = 107; static char s_CheatModels[s_numCheatModels][107] = { "apu", "askinner", "a_american", "a_army", "a_besharp", "barney", "bart", "beeman", "brn_unf", "burns", "b_football", "b_hugo", "b_man", "b_military", "b_ninja", "b_tall", "captain", "carl", "cbg", "cletus", "dolph", "eddie", "frink", "gil", "grandpa", "hibbert", "homer", "h_donut", "h_evil", "h_fat", "h_scuzzy", "h_stcrobe", "h_undrwr", "jasper", "jimbo", "kearney", "krusty", "lenny", "lisa", "lou", "louie", "l_cool", "l_florida", "l_jersey", "marge", "milhouse", "moe", "moleman", "m_pink", "m_police", "m_prison", "ned", "nelson", "nriviera", "otto", "patty", "ralph", "selma", "skinner", "smithers", "snake", "teen", "wiggum", "willie", "boy1", "boy2", "boy3", "bum", "busm1", "busm2", "busw1", "const1", "const2", "farmr1", "fem1", "fem2", "fem3", "fem4", "girl1", "girl2", "hooker", "joger1", "joger2", "male1", "male2", "male3", "male4", "male5", "male6", "mobstr", "nuclear", "olady1", "olady2", "olady3", "rednk1", "rednk2", "sail1", "sail2", "sail3", "sail4", "zfem1", "zfem5", "zmale1", "zmale3", "zmale4", "frankenstein", "witch" }; static int s_curCheatModel = 0; void CharacterManager::NextCheatModel(void) { if( CommandLineOptions::Get( CLO_NO_PEDS ) ) return; Character* pc = GetCharacterManager()->GetCharacter(0); GetCharacterManager()->SwapData(pc, s_CheatModels[s_curCheatModel], GetCharacterManager()->GetAnimName(pc)); s_curCheatModel = (s_curCheatModel + 1) % s_numCheatModels; } #include #include static const unsigned MAX_TELEPORT_DESTS = 64; static ZoneEventLocator* s_dynaloadLoc = NULL; static unsigned s_numTeleportDests = 0; struct TeleportDest { char name[32]; bool useLoc; char loc[32]; rmt::Vector pos; char zone[64]; } s_teleportDests[MAX_TELEPORT_DESTS]; unsigned CharacterManager::GetNumTeleportDests(void) { return s_numTeleportDests; } const char* CharacterManager::GetTeleportDest(unsigned i) { static char dummy[] = "Invalid Teleport Destination"; if(i < s_numTeleportDests) { return s_teleportDests[i].name; } return dummy; } void CharacterManager::ClearTeleportDests(void) { for(unsigned i = 0; i < s_numTeleportDests; i++) { radDbgWatchDelete( &DoTeleport ); } tRefCounted::Release(s_dynaloadLoc); s_numTeleportDests = 0; } void CharacterManager::AddTeleportDest(int argc, char** argv) { rAssert(argc != 5); strcpy(s_teleportDests[s_numTeleportDests].name, argv[1]); if(argc == 4) { strcpy(s_teleportDests[s_numTeleportDests].loc, argv[2]); strcpy(s_teleportDests[s_numTeleportDests].zone, argv[3]); s_teleportDests[s_numTeleportDests].useLoc = true; } else { s_teleportDests[s_numTeleportDests].pos.Set((float)atof(argv[2]), (float)atof(argv[3]), (float)atof(argv[4])); strcpy(s_teleportDests[s_numTeleportDests].zone, argv[5]); s_teleportDests[s_numTeleportDests].useLoc = false; } radDbgWatchAddFunction( argv[1], &DoTeleport, (void*)s_numTeleportDests, "Teleport"); s_numTeleportDests++; } void CharacterManager::DoTeleport(void* data) { int which = (int)data; GetRenderManager()->mpLayer( RenderEnums::LevelSlot )->DumpAllDynaLoads(1, GetRenderManager()->mEntityDeletionList ); tRefCounted::Assign(s_dynaloadLoc,new(GetGameplayManager()->GetCurrentMissionHeap()) ZoneEventLocator); s_dynaloadLoc->SetZoneSize( strlen(s_teleportDests[which].zone) + 1 ); s_dynaloadLoc->SetZone( s_teleportDests[which].zone ); s_dynaloadLoc->SetPlayerEntered(); GetEventManager()->TriggerEvent( EVENT_FIRST_DYNAMIC_ZONE_START, s_dynaloadLoc ); rmt::Vector pos = s_teleportDests[which].pos; if(s_teleportDests[which].useLoc) { Locator* l = p3d::find(s_teleportDests[which].loc); l->GetLocation(&pos); } if(GetCharacterManager()->GetCharacter(0)->IsInCar()) { GetCharacterManager()->GetCharacter(0)->GetTargetVehicle()->SetPosition(&pos); } else { GetCharacterManager()->GetCharacter(0)->RelocateAndReset(pos, 0); } } //============================================================================= // CharacterManager::ResetCharacter //============================================================================= // Description: Comment // // Parameters: ( int argc, char** argv ) // // Return: void // //============================================================================= unsigned int CharacterManager::GetCharacterIndex( const Character* character ) const { unsigned int i; unsigned int size = MAX_CHARACTERS; for( i = 0; i < size; ++i ) { if( mpCharacter[ i ] == character ) { return i; } } rAssertMsg( false, "Searched for a chraracter that does not exist" ); return 0; }