//called to enumerate the FX that this manager contains, and for each one it will query each //key for the objects it uses, and for each object the provided callback will be called //and provided with the effect, the object, and the provided user data void CClientFXMgr::EnumerateObjects(CBaseFX::TEnumerateObjectsFn pfnObjectCB, void* pUserData) { //don't allow for invalid functions! if(!pfnObjectCB) return; //iterate through all of our effects LTListIter<CClientFXInstance*> itFXInstance = m_FXInstanceList.Begin(); while(itFXInstance != m_FXInstanceList.End()) { CClientFXInstance* pInst = *itFXInstance; itFXInstance++; //now iterate through each key in that effect LTListIter<CBaseFX*> itActiveFX = pInst->m_ActiveFXList.Begin(); while(itActiveFX != pInst->m_ActiveFXList.End()) { CBaseFX* pFX = *itActiveFX; itActiveFX++; //and enumerate the objects used by that effect pFX->EnumerateObjects(pfnObjectCB, pUserData); } } }
bool CLIENTFX_INSTANCE::IsFinished() { if (IsDone()) return true; CLinkListNode<FX_LINK> *pActiveNode = m_collActiveFX.GetHead(); CBaseFX *pFX = LTNULL; while (pActiveNode) { pFX = pActiveNode->m_Data.m_pFX; // Check for expiration if( pFX ) { //determine if this effect has expired bool bExpired = ((pFX->GetElapsed() >= pFX->GetEndTime()) || pFX->IsShuttingDown()) && (pFX->IsFinishedShuttingDown() || !pActiveNode->m_Data.m_pRef->m_bSmoothShutdown); if (!bExpired) return false; } pActiveNode = pActiveNode->m_pNext; } return true; }
CBaseFX* CClientFXMgr::CreateFX(const char *sName, FX_BASEDATA *pBaseData, CBaseFXProps* pProps) { //track our performance CTimedSystemBlock TimingBlock(g_tsClientFXUpdateCreate); CBaseFX *pNewFX = NULL; // Locate the named FX const FX_REF *pFxRef = CClientFXDB::GetSingleton().FindFX(sName); if( pFxRef ) { pNewFX = pFxRef->m_pfnCreate(); } // If we have a new fx, go ahead and add it onto the active list if( pNewFX ) { //initialize the effect if( !pNewFX->Init(pBaseData, pProps) ) { //if this failed to initialize, then destroy the effect pNewFX->Term(); CClientFXDB::GetSingleton().DeleteEffect(pNewFX); pNewFX = NULL; } } // All done.... return pNewFX; }
void CLIENTFX_INSTANCE::SetPos( const LTVector &vWorldPos, const LTVector &vCamRelPos ) { // Loop through all of the Muzzle flash's active FX and set the positions... if( m_collActiveFX.GetSize() ) { uint32 dwFlags; CBaseFX *pFX; LTVector vPos; CLinkListNode<FX_LINK> *pActiveFX = m_collActiveFX.GetHead(); while( pActiveFX ) { pFX = pActiveFX->m_Data.m_pFX; if( m_bPlayerView ) { g_pLTCCommon->GetObjectFlags( pFX->GetFXObject(), OFT_Flags, dwFlags ); dwFlags & FLAG_REALLYCLOSE ? vPos = vCamRelPos : vPos = vWorldPos; } else { vPos = vWorldPos; } pFX->SetPos( vPos ); pActiveFX = pActiveFX->m_pNext; } } }
CBaseFX* CClientFXMgr::CreateFX(const char *sName, FX_BASEDATA *pBaseData, CBaseFXProps* pProps, HOBJECT hInstParent) { CBaseFX *pNewFX = NULL; // Locate the named FX FX_REF *pFxRef = CClientFXDB::GetSingleton().FindFX(sName); if( pFxRef ) { pNewFX = pFxRef->m_pfnCreate(); } // If we have a new fx, go ahead and add it onto the active list if( pNewFX ) { // Assign a unique ID for this FX pBaseData->m_dwID = GetUniqueID(); if( !pNewFX->Init(m_pClientDE, pBaseData, pProps) ) { // See if the FX->Init() filled out data to create a new instance... if( pBaseData->m_sNode[0] ) { CLIENTFX_CREATESTRUCT fxCS( pBaseData->m_sNode, pBaseData->m_dwFlags, pBaseData->m_vPos, pBaseData->m_rRot ); fxCS.m_vTargetNorm = pBaseData->m_vTargetNorm; fxCS.m_hParent = hInstParent; CreateClientFX( fxCS, LTTRUE ); } pNewFX->Term(); CClientFXDB::GetSingleton().DeleteEffect(pNewFX); pNewFX = NULL; } } // All done.... return pNewFX; }
//------------------------------------------------------------------ // // FUNCTION : HandleShutdownEffect() // // PURPOSE : Given an instance and an effect that has just finished shutting down, // it will take the appropriate course of action. Note that this will // invalidate the node that is passed into it // //------------------------------------------------------------------ void CClientFXMgr::HandleShutdownEffect(CLIENTFX_INSTANCE* pInst, CLinkListNode<FX_LINK>* pKeyNode) { //sanity check assert(pInst && pKeyNode); CBaseFX *pFX = pKeyNode->m_Data.m_pFX; //we are finished shutting down, if we aren't looping, we need to destroy //the effect, otherwise just disable it if(pInst->m_bLoop && !pInst->m_bShutdown) { pFX->SetVisible(false); pFX->ClearState(FS_ACTIVE | FS_SHUTTINGDOWN | FS_INITIALFRAME); } else { pInst->DeleteFX(pKeyNode); } }
void CLIENTFX_INSTANCE::DeleteFX(CLinkListNode<FX_LINK> *pDelNode) { if( !pDelNode ) return; CBaseFX* pDelFX = pDelNode->m_Data.m_pFX; if(pDelFX) { // Make sure no other active FX in this instance have this pDelFX has their parent... CLinkListNode<FX_LINK> *pActiveNode = m_collActiveFX.GetHead(); while( pActiveNode ) { CBaseFX *pPossibleChildFX = pActiveNode->m_Data.m_pFX; if( pPossibleChildFX && (pPossibleChildFX->GetParent() == pDelFX->GetFXObject())) { // NULL out the parent otherwise it will reference an object that is no longer there pPossibleChildFX->SetParent( LTNULL ); } pActiveNode = pActiveNode->m_pNext; } // Give the FX a chance to clean itself up pDelFX->Term(); CClientFXDB::GetSingleton().DeleteEffect(pDelFX); } //now remove this node from our list m_collActiveFX.Remove(pDelNode); }
bool CClientFXMgr::CreateFXKey(const CLIENTFX_CREATESTRUCT &fxInit, CClientFXInstance* pInst, const FX_KEY* pKey ) { if( !pKey ) return false; // // We need to go ahead and create this effect // FX_BASEDATA fxData; fxData.m_tTransform = fxInit.m_tTransform; fxData.m_bUseTargetData = fxInit.m_bUseTargetData; fxData.m_hTargetObject = fxInit.m_hTargetObject; fxData.m_vTargetOffset = fxInit.m_vTargetOffset; fxData.m_pFxMgr = this; fxData.m_hParentObject = fxInit.m_hParentObject; fxData.m_hParentRigidBody = fxInit.m_hParentRigidBody; fxData.m_hNodeAttach = fxInit.m_hNode; fxData.m_hSocketAttach = fxInit.m_hSocket; fxData.m_dwFlags = fxInit.m_dwFlags; // Create the FX CBaseFX *pNewFX = CreateFX(pKey->m_pFxRef->m_sName, &fxData, pKey->m_pProps); if( pNewFX ) { pNewFX->SetVisible(false); pNewFX->ClearState(FS_INITIALFRAME | FS_ACTIVE | FS_SHUTTINGDOWN | FS_SUSPENDED); // Add it onto the list for link referencing pInst->m_ActiveFXList.AddTail(&pNewFX->m_FXListLink); } else { return false; } return true; }
bool CClientFXMgr::UpdateAllActiveFX() { //track our performance CTimedSystemBlock TimingBlock(g_tsClientFXUpdate); //Update our frame time, before any early outs so there aren't giant pops when the early //out fails float fFrameTime = m_Timer.GetTimerElapsedS(); //add in all the effects from our next update list and clear that out LTListIter<CClientFXInstance*> itFXInstance = m_NextUpdateFXList.Begin(); while(itFXInstance != m_NextUpdateFXList.End()) { CClientFXInstance* pFXInstance = *itFXInstance; itFXInstance++; pFXInstance->m_FXListLink.Remove(); m_FXInstanceList.AddTail(&pFXInstance->m_FXListLink); } HCONSOLEVAR hVar = m_pClientDE->GetConsoleVariable("UpdateClientFX"); if (hVar) { float fVal = m_pClientDE->GetConsoleVariableFloat(hVar); if (!fVal) return true; } //see if we should even update if( g_pGameClientShell->IsServerPaused( )) { //no time has elapsed, don't bother updating return true; } // // Update the group Instances // itFXInstance = m_FXInstanceList.Begin(); while(itFXInstance != m_FXInstanceList.End()) { CClientFXInstance* pInst = *itFXInstance; itFXInstance++; //see if this instance is suspended, if so, just call the suspended update if(pInst->UpdateSuspended()) { //just run through all effects and give them a suspended updata LTListIter<CBaseFX*> itActiveFX = pInst->m_ActiveFXList.Begin(); while(itActiveFX != pInst->m_ActiveFXList.End()) { CBaseFX* pFX = *itActiveFX; itActiveFX++; pFX->SuspendedUpdate(fFrameTime); } //don't bother with any interval updating continue; } //determine the start and end of our update interval, relative to the instance //time frame float fStartInterval = pInst->m_tmElapsed; float fEndInterval = fStartInterval + fFrameTime; //we now need to iteratively break this interval down into a series of intervals that //do not extend past the end of the effect bool bLastSegment = false; while(!bLastSegment) { //pick whichever is closest, the end of the interval, or the duration of the //effect float fEndSegment = pInst->m_fDuration; if(fEndInterval < pInst->m_fDuration) { bLastSegment = true; fEndSegment = fEndInterval; } //alright, we now have an interval, update all the effects that lie within it pInst->UpdateInterval(fStartInterval, fEndSegment); //now move on to the next interval if necessary if(!bLastSegment) { fStartInterval = 0.0f; fEndInterval -= pInst->m_fDuration; } } //all done, save our time pInst->m_tmElapsed = fEndInterval; //see if we are done with this effect if(pInst->m_ActiveFXList.IsEmpty()) { // Destroy the instance DeleteClientFXInstance( pInst ); } } // Success !! return true; }
//------------------------------------------------------------------ // // FUNCTION : UpdateInstanceInterval() // // PURPOSE : Given an instance and a time interval, this will appropriately update // all effects contained within that interval // //------------------------------------------------------------------ void CClientFXMgr::UpdateInstanceInterval(CLIENTFX_INSTANCE* pInst, float fStartInterval, float fEndInterval) { //here are the possible scenarios: // Inactive // Inactive -> Active -> Shutting down // Inactive -> Active -> Shutting down -> Inactive // Inactive -> Active // Shutting Down // Shutting Down -> Inactive // Shutting Down -> Active -> Shutting Down // Shutting Down -> Active -> Shutting Down -> Inactive // Shutting Down -> Active // Active //A note about intervals: The interval used is inclusive of both start and ends. This does //mean that a boundary such as a key beginning or end can be hit twice, but because of the //way the state flow works, this will not cause any issues, since when it is inactive //it will look for active and vice a versa. Therefore since the beginning and end cannot //lie on the same position this should not introduce any issues, but ensures that the //full track length is handled when updating //alright, run through all active effects and determine what to do CLinkListNode<FX_LINK> *pActiveNode; CLinkListNode<FX_LINK> *pNextActiveNode; //determine if this is a looping instance bool bLoopInstance = pInst->m_bLoop; pActiveNode = pInst->m_collActiveFX.GetHead(); while( pActiveNode ) { //cache the next node in case we delete this key pNextActiveNode = pActiveNode->m_pNext; //alright, first off look at the state of the key and see what to do CBaseFX *pFX = pActiveNode->m_Data.m_pFX; const FX_KEY* pKey = pActiveNode->m_Data.m_pRef; //this is our time slice beginning float fCurrStart = fStartInterval; //skip dead space on inactive effects if(!pFX->IsActive()) { //sanity check, the shutting down flag should never be set without active assert(!pFX->IsShuttingDown()); //This check is to verify that we aren't shutting down the instance and have inactive effects. //If we are shutting down, inactive effects are immediately pruned, and then active effects //should be removed as they are completed assert(!pInst->m_bShutdown); //figure out the start time of this effect float fKeyStart = pKey->m_tmStart; //this effect is not active. See if it should be (loop keys always should be) if(pKey->m_bContinualLoop || ((fKeyStart >= fCurrStart) && (fKeyStart <= fEndInterval))) { //our effect has just become active, we need to reset its elapsed time to 0, //and switch it over to an initial frame pFX->SetElapsed(0.0f); pFX->SetState(FS_ACTIVE | FS_INITIALFRAME); pFX->ClearState(FS_SHUTTINGDOWN); pFX->SetVisible(true); //handle applying a fake effect offset ApplyEffectStartingOffset(pFX, pKey); //move our timeslice forward to the beginning of the key fCurrStart = fKeyStart; } } if(pFX->IsActive()) { //we are an active effect, which means we are either in the time block or are //shutting down. //see if we are currently shutting down, and if we are going to transition into //becoming active if(pFX->IsShuttingDown()) { float fKeyStart = pKey->m_tmStart; //however, we can only bring it into the active state from here if the instance //will allow us bool bCanActivate = pInst->m_bLoop && !pInst->m_bShutdown; //this effect is not active. See if it should be if(bCanActivate && (fKeyStart >= fCurrStart) && (fKeyStart <= fEndInterval)) { //it will become active in this range, so move up to there and change state pFX->Update(fKeyStart - fCurrStart); pFX->SetElapsed(0.0f); pFX->SetState(FS_INITIALFRAME | FS_ACTIVE); pFX->ClearState(FS_SHUTTINGDOWN); //handle applying a fake effect offset ApplyEffectStartingOffset(pFX, pKey); //move our time position up to the key start fCurrStart = fKeyStart; } } //alright, we are now in a chunk of time where either the block is active //or shutting down. If it is active, we need to update until the end of the slice //or the end of the effect if(!pFX->IsShuttingDown()) { //we aren't shutting down, so we can update like normal float fTimeBlockEnd = fEndInterval; bool bCompleteKey = ((pKey->m_tmEnd <= fTimeBlockEnd) && (pKey->m_tmEnd >= fCurrStart)); //see if we hit the end of the key if(bCompleteKey) { fTimeBlockEnd = pKey->m_tmEnd; } //update based upon the interval length pFX->Update(fTimeBlockEnd - fCurrStart); //the initial update state should be cleared now pFX->ClearState(FS_INITIALFRAME); //see if we completed the key if(bCompleteKey) { //we did, so now switch to a shutting down state if it isn't continually looping, //otherwise we need to reset the elapsed time to 0 if(pKey->m_bContinualLoop && bLoopInstance) { //we have looped our key, so reset the elapsed amount. However, if we are a //continually looping effect that started with an offset, we don't want //to lose that offset and should threfore just wrap based upon the lifespan if(pKey->m_fMaxStartOffset >= 0.001f) { pFX->SetElapsed((float)fmod(pFX->GetElapsed(), pFX->GetLifespan())); } else { //no starting offset, so just to make sure that everything syncs //up correctly and no error gets introduced, reset it to 0 pFX->SetElapsed(0.0f); } } else { //we're past our key, so start shutting down pFX->SetState(FS_SHUTTINGDOWN); } } //update our time start fCurrStart = fTimeBlockEnd; } //alright, now handle shutting down, in which case we just want to update however //much time we have left in this interval, and see if the effect is completed if(pFX->IsShuttingDown()) { //allow it to update pFX->Update(fEndInterval - fCurrStart); //can this effect do a smooth shutdown? bool bSmoothShutdown = pKey->m_bSmoothShutdown && pInst->m_bSmoothShutdown; //see if this effect is done shutting down if(pFX->IsFinishedShuttingDown() || !bSmoothShutdown) { //notify of an effect that has finished shutting down HandleShutdownEffect(pInst, pActiveNode); //move onto the next node and keep processing pActiveNode = pNextActiveNode; continue; } } } //and onto the next node pActiveNode = pNextActiveNode; } }
bool CClientFXMgr::CreateFXKey( CLIENTFX_INSTANCE* pInst, FX_KEY* pKey ) { if( !pInst || !pKey ) return false; // // We need to go ahead and create this effect // assert(m_hCamera); FX_BASEDATA fxData; fxData.m_vPos = pInst->m_vPos; fxData.m_rRot = pInst->m_rRot; fxData.m_dwID = pInst->m_dwID; fxData.m_hTarget = pInst->m_hTarget; fxData.m_dwObjectFlags = pInst->m_dwObjectFlags; fxData.m_dwObjectFlags2 = pInst->m_dwObjectFlags2; fxData.m_bUseTargetData = pInst->m_bUseTargetData; fxData.m_vTargetPos = pInst->m_vTargetPos; fxData.m_vTargetNorm = pInst->m_vTargetNorm; fxData.m_hCamera = m_hCamera; // Save the parent fxData.m_hParent = pInst->m_hParent; // Is this FX supposed to be motion linked to another FX? if (pKey->m_bLinked) { CLinkListNode<FX_LINK> *pNode = pInst->m_collActiveFX.GetHead(); while (pNode) { if (pNode->m_Data.m_dwID == pKey->m_dwLinkedID) { // This is the one !!! if (pInst->ExistFX(pNode->m_Data.m_pFX)) { CBaseFX *pMotionLinkFX = pNode->m_Data.m_pFX; fxData.m_hParent = pMotionLinkFX->GetFXObject(); } // done break; } pNode = pNode->m_pNext; } } // Create the FX CBaseFX *pNewFX = CreateFX(pKey->m_pFxRef->m_sName, &fxData, pKey->m_pProps, pInst->m_hParent); if( pNewFX ) { pNewFX->SetVisible(false); pNewFX->ClearState(FS_INITIALFRAME | FS_ACTIVE | FS_SHUTTINGDOWN | FS_SUSPENDED); // Add it onto the list for link referencing FX_LINK fxLink; fxLink.m_dwID = pKey->m_dwID; fxLink.m_pFX = pNewFX; fxLink.m_pRef = pKey; pInst->m_collActiveFX.AddHead(fxLink); } else { return false; } return true; }
void CClientFXMgr::ShutdownClientFX(CLIENTFX_INSTANCE *pFxGroup) { CLinkListNode<CLIENTFX_INSTANCE *> *pActiveNode = m_collActiveGroupFX.GetHead(); //setup the callback in case any create effects as they are destroyed SetupCreateEffectCallback(); while (pActiveNode) { CLIENTFX_INSTANCE* pInst = pActiveNode->m_Data; if (pInst == pFxGroup) { // Shut it down !! pInst->m_bShutdown = true; HOBJECT hReplaceParent = NULL; //see if this effect performs a smooth shutdown, if so, we need to create a dummy //object to place everything under a dummy object if (pInst->m_bSmoothShutdown) { LTVector vPos; LTRotation rRot; if (pFxGroup->m_hParent) { m_pClientDE->GetObjectPos(pFxGroup->m_hParent, &vPos); m_pClientDE->GetObjectRotation(pFxGroup->m_hParent, &rRot); } else { vPos = pFxGroup->m_vPos; rRot = pFxGroup->m_rRot; } // Create a temporary parent object.... ObjectCreateStruct ocs; INIT_OBJECTCREATESTRUCT(ocs); ocs.m_ObjectType = OT_NORMAL; ocs.m_Pos = vPos; ocs.m_Rotation = rRot; ocs.m_Flags = 0; hReplaceParent = m_pClientDE->CreateObject(&ocs); pFxGroup->m_hAlternateParent = hReplaceParent; pFxGroup->m_hParent = hReplaceParent; } if (!hReplaceParent) { //we couldn't create or didn't need a replacement object, so just remove all fx pInst->m_hAlternateParent = NULL; pInst->m_hParent = NULL; pInst->RemoveAllEffects(); } else { // We have a parent to set, but in addition we need to notify all effects that //we are shutting down, and remove any effects that don't need a smooth shutdown (this //way it can be polled instantly after to determine if it is done) CLinkListNode<FX_LINK> *pActiveFxNode = pInst->m_collActiveFX.GetHead(); CLinkListNode<FX_LINK> *pNextActiveFxNode = NULL; while( pActiveFxNode ) { //cache the next in case this node is deleted pNextActiveFxNode = pActiveFxNode->m_pNext; CBaseFX *pFX = pActiveFxNode->m_Data.m_pFX; //we want to remove any inactive nodes if(!pFX->IsActive()) { pInst->DeleteFX(pActiveFxNode); } else { const FX_KEY* pKey = pActiveFxNode->m_Data.m_pRef; //reassign the parent of this object pFX->SetParent(hReplaceParent); //we have an active effect, set it to shutting down pFX->SetState(FS_SHUTTINGDOWN); //now that it is shutting down, see if it is complete if(pFX->IsFinishedShuttingDown() || !pInst->m_bSmoothShutdown || !pKey->m_bSmoothShutdown) { //we can just remove the effect pInst->DeleteFX(pActiveFxNode); } } pActiveFxNode = pNextActiveFxNode; } } break; } pActiveNode = pActiveNode->m_pNext; } }