/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ ////////////////////////////////////////////////////////////////////////////// // // // plTransitionMgr - Class to handle fullscreen transitions (fades, etc) // // // // Note: eventually, I would like to drive these transitions on material // // animations (it's just a big screen-covering plate with a material, // // after all). This would allow the artists to specify their own // // transitions and do really cool effects. However, that would require // // somehow loading the materials in, and I'm not sure exactly how to do // // that.... // // // ////////////////////////////////////////////////////////////////////////////// #include "hsWindows.h" #include "hsTypes.h" #include "plTransitionMgr.h" #include "plPlates.h" #include "plGImage/plMipmap.h" #include "plSurface/plLayer.h" #include "plSurface/hsGMaterial.h" #include "plMessage/plLayRefMsg.h" #include "pnMessage/plRefMsg.h" #include "plMessage/plTransitionMsg.h" #include "pnMessage/plTimeMsg.h" #include "pnMessage/plEventCallbackMsg.h" #include "plMessage/plLinkToAgeMsg.h" #include "plgDispatch.h" #include "hsGDeviceRef.h" #include "hsResMgr.h" #include "hsTimer.h" #include "plAudio/plAudioSystem.h" #include "pnNetCommon/plNetApp.h" #include "plNetClient/plLinkEffectsMgr.h" #include "pnNetCommon/plNetApp.h" #include "plStatusLog/plStatusLog.h" //// Constructor/Destructor ////////////////////////////////////////////////// plTransitionMgr::plTransitionMgr() { fEffectPlate = nil; fCurrentEffect = kIdle; fPlaying = false; } void plTransitionMgr::Init( void ) { ICreatePlate(); plgDispatch::Dispatch()->RegisterForExactType( plTransitionMsg::Index(), GetKey() ); plgDispatch::Dispatch()->RegisterForExactType( plLinkEffectBCMsg::Index(), GetKey() ); } plTransitionMgr::~plTransitionMgr() { int i; for( i = 0; i < fCallbacks.GetCount(); i++ ) hsRefCnt_SafeUnRef( fCallbacks[ i ] ); if( fEffectPlate != nil ) plPlateManager::Instance().DestroyPlate( fEffectPlate ); if( fRegisteredForTime ) plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); plgDispatch::Dispatch()->UnRegisterForExactType( plTransitionMsg::Index(), GetKey() ); plgDispatch::Dispatch()->UnRegisterForExactType( plLinkEffectBCMsg::Index(), GetKey() ); } //// ICreatePlate //////////////////////////////////////////////////////////// void plTransitionMgr::ICreatePlate( void ) { int x, y; fEffectPlate = nil; // +0.01 to deal with the half-pixel antialiasing stuff plPlateManager::Instance().CreatePlate( &fEffectPlate, 0, 0, 2.01, 2.01 ); fEffectPlate->SetDepth(2); // hack for now--create a black layer that we will animate the opacity on plMipmap *ourMip = fEffectPlate->CreateMaterial( 16, 16, true ); for( y = 0; y < ourMip->GetHeight(); y++ ) { UInt32 *pixels = ourMip->GetAddr32( 0, y ); for( x = 0; x < ourMip->GetWidth(); x++ ) pixels[ x ] = 0xff000000; } fEffectPlate->SetVisible( false ); } //// IStartFadeOut /////////////////////////////////////////////////////////// void plTransitionMgr::IStartFadeOut( hsScalar lengthInSecs, UInt8 effect ) { fCurrentEffect = effect; // default - kFadeOut; fEffectLength = lengthInSecs; // Special case for length 0--just jump straight to fadeout if( lengthInSecs == 0.f ) { fCurrOpacity = 1.f; fLastTime = -1.f; fPlaying = false; plgAudioSys::SetGlobalFadeVolume( 0.f ); } else { fCurrOpacity = 0; fOpacDelta = 1.f / lengthInSecs; fLastTime = -1.f; fPlaying = true; // Register for time message plgDispatch::Dispatch()->RegisterForExactType( plTimeMsg::Index(), GetKey() ); fRegisteredForTime = true; } if( fEffectPlate == nil ) ICreatePlate(); fEffectPlate->SetVisible( true ); plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); if( layer != nil ) { layer->SetOpacity( fCurrOpacity ); } } //// IStartFadeIn //////////////////////////////////////////////////////////// void plTransitionMgr::IStartFadeIn( hsScalar lengthInSecs, UInt8 effect ) { fCurrentEffect = effect; // default - kFadeIn; fEffectLength = lengthInSecs; fCurrOpacity = 1.f; fOpacDelta = -1.f / lengthInSecs; fLastTime = -1.f; fPlaying = true; // Register for time message plgDispatch::Dispatch()->RegisterForExactType( plTimeMsg::Index(), GetKey() ); fRegisteredForTime = true; if( fEffectPlate == nil ) ICreatePlate(); fEffectPlate->SetVisible( true ); plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); if( layer != nil ) { layer->SetOpacity( fCurrOpacity ); } } //// IStop /////////////////////////////////////////////////////////////////// void plTransitionMgr::IStop( hsBool aboutToStartAgain /*= false*/ ) { int i; plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); fRegisteredForTime = false; if( fPlaying ) { if( !fHoldAtEnd && fEffectPlate != nil && !aboutToStartAgain ) fEffectPlate->SetVisible( false ); // finish the opacity to the end opacity if( fEffectPlate != nil ) { plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); if( layer != nil ) { layer->SetOpacity( (fCurrentEffect == kFadeIn || fCurrentEffect == kTransitionFadeIn) ? 0.f : 1.f ); } } if( !aboutToStartAgain ) { if (!fNoSoundFade) plgAudioSys::SetGlobalFadeVolume( (fCurrentEffect == kFadeIn || fCurrentEffect == kTransitionFadeIn) ? 1.f : 0.f ); } for( i = 0; i < fCallbacks.GetCount(); i++ ) { fCallbacks[ i ]->SetSender( GetKey() ); plgDispatch::MsgSend( fCallbacks[ i ] ); } fCallbacks.Reset(); fPlaying = false; } } //// MsgReceive ////////////////////////////////////////////////////////////// hsBool plTransitionMgr::MsgReceive( plMessage* msg ) { int i; plTimeMsg *time = plTimeMsg::ConvertNoRef( msg ); if( time != nil ) { if( !fPlaying ) return false; if (fLastTime < 0) { // Semi-hack. We trigger transitions after we've finished loading. // Problem is the loading all happens in one really long frame, so that // if we record the time we started, we'll instantly be done next frame, // even though we triggered just at the "end" of the last frame. // // So instead we don't start the clock until we get our first plTimeMsg. fLastTime = (hsScalar)(time->DSeconds()); return false; } fEffectLength -= (hsScalar)( time->DSeconds() - fLastTime );//*/time->DelSeconds(); if( fEffectLength < 0 ) IStop(); else { // Grab the layer so we can set the opacity fCurrOpacity += (hsScalar)(fOpacDelta * ( time->DSeconds() - fLastTime ));//*/time->DelSeconds(); if( fEffectPlate == nil ) ICreatePlate(); plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); if( layer != nil ) { layer->SetOpacity( fCurrOpacity ); } // Let the audiosystem handle fading in sounds if(!fNoSoundFade) plgAudioSys::SetGlobalFadeVolume( 1.f - fCurrOpacity ); fLastTime = (hsScalar)(time->DSeconds()); } return false; } plTransitionMsg *effect = plTransitionMsg::ConvertNoRef( msg ); if( effect != nil ) { if( fRegisteredForTime ) IStop( true ); for( i = 0; i < effect->GetNumCallbacks(); i++ ) { plEventCallbackMsg *pMsg = effect->GetEventCallback( i ); hsRefCnt_SafeRef( pMsg ); fCallbacks.Append( pMsg ); } fHoldAtEnd = effect->GetHoldState(); fNoSoundFade = false; switch(effect->GetEffect()) { case plTransitionMsg::kFadeInNoSound: fNoSoundFade = true; case plTransitionMsg::kFadeIn: IStartFadeIn( effect->GetLengthInSecs(), kTransitionFadeIn ); break; case plTransitionMsg::kFadeOutNoSound: fNoSoundFade = true; case plTransitionMsg::kFadeOut: IStartFadeOut( effect->GetLengthInSecs(), kTransitionFadeOut ); break; } return false; } plLinkEffectBCMsg *link = plLinkEffectBCMsg::ConvertNoRef( msg ); if( link != nil ) { const float kScreenFadeTime = 3.f; // seconds // Go ahead and auto-trigger based on link FX messages if( plNetClientApp::GetInstance() != nil && link->fLinkKey == plNetClientApp::GetInstance()->GetLocalPlayerKey() ) { if( fRegisteredForTime ) IStop( true ); if( link->HasLinkFlag( plLinkEffectBCMsg::kLeavingAge ) ) { plNetApp::GetInstance()->DebugMsg("Local player linking out, fading screen\n"); fHoldAtEnd = true; IStartFadeOut( kScreenFadeTime ); } else { plNetApp::GetInstance()->DebugMsg("Local player linking in, fading screen\n"); fHoldAtEnd = false; IStartFadeIn( kScreenFadeTime ); } if (link->HasLinkFlag(plLinkEffectBCMsg::kSendCallback)) { plLinkEffectsMgr *mgr; if( ( mgr = plLinkEffectsMgr::ConvertNoRef( link->GetSender()->ObjectIsLoaded() ) ) != nil ) { plEventCallbackMsg *cback = plEventCallbackMsg::ConvertNoRef( mgr->WaitForEffect( link->fLinkKey ) ); // hsRefCnt_SafeRef( cback ); // mgr has given us ownership, his ref is now ours. No need for another. -mf- fCallbacks.Append( cback ); } } } return true; } return hsKeyedObject::MsgReceive( msg ); }