You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

359 lines
10 KiB

/*==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 <http://www.gnu.org/licenses/>.
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 );
}