//------------------------------------------------------------------ // // 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); } }
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; }
//------------------------------------------------------------------ // // 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; }