2004-03-25 22:13:51 +00:00
|
|
|
/*
|
2005-07-22 08:39:07 +00:00
|
|
|
* We maintain a pool of "prepared" screens, which are screens previously loaded
|
|
|
|
|
* to be available on demand.
|
|
|
|
|
*
|
2005-07-23 01:18:48 +00:00
|
|
|
* When a screen pops off the stack that's in g_setPersistantScreens, it goes to
|
|
|
|
|
* the prepared list. If that screen is used again before it's deleted, it'll be
|
|
|
|
|
* reused.
|
2005-07-22 08:39:07 +00:00
|
|
|
*
|
|
|
|
|
* Typically, the first screen in a group will prepare all of the nearby screens it
|
2005-07-23 01:18:48 +00:00
|
|
|
* may load. When it loads one, the prepared screen will be used.
|
2005-07-22 08:39:07 +00:00
|
|
|
*
|
|
|
|
|
* A group of screens may only want to preload some screens, and not others,
|
|
|
|
|
* while still considering those screens part of the same group. For example,
|
|
|
|
|
* an attract loop typically has several very lightweight screens and a few
|
|
|
|
|
* expensive screens. Preloading the lightweight screens can improve responsiveness,
|
|
|
|
|
* but preloading the expensive screens may use too much memory and take too long
|
|
|
|
|
* to load all at once. By calling GroupScreen(), entering these screens will not
|
|
|
|
|
* trigger cleanup.
|
|
|
|
|
*
|
2005-07-23 01:18:48 +00:00
|
|
|
* Example uses:
|
|
|
|
|
* - ScreenOptions1 preloads ScreenOptions2, and persists both. Moving from Options1
|
|
|
|
|
* and Options2 and back is instant and reuses both.
|
|
|
|
|
* - ScreenMenu groups and persists itself and ScreenSubmenu. ScreenSubmenu
|
|
|
|
|
* is not preloaded, so it will be loaded on demand the first time it's used,
|
|
|
|
|
* but will remain loaded if it returns to ScreenMenu.
|
|
|
|
|
* - ScreenSelectMusic groups itself and ScreenPlayerOptions, and persists only
|
|
|
|
|
* ScreenSelectMusic. This will cause SSMusic will be loaded once, and SPO to
|
|
|
|
|
* be loaded on demand.
|
|
|
|
|
* - ScreenAttract1 preloads and persists ScreenAttract1, 3, 5 and 7, and groups 1
|
|
|
|
|
* through 7. 1, 3, 5 and 7 will remain in memory; the rest will be loaded on
|
|
|
|
|
* demand.
|
|
|
|
|
*
|
|
|
|
|
* If a screen is added to the screen stack that isn't in the current screen group
|
|
|
|
|
* (added to by GroupScreen), the screen group is reset: all prepared screens are
|
|
|
|
|
* unloaded and the persistance list is cleared.
|
|
|
|
|
*
|
|
|
|
|
* (For persistance, concurrently preparing a screen is logically equivalent
|
|
|
|
|
* to SetNewScreen, since it's going to be set after it finishes loading.)
|
|
|
|
|
*
|
2005-07-22 08:39:07 +00:00
|
|
|
* Note that not all screens yet support reuse in this way; proper use of BeginScreen
|
|
|
|
|
* is required. This will misbehave if a screen pushes another screen that's already
|
|
|
|
|
* in use lower on the stack, but that's not useful; it would allow infinite screen
|
|
|
|
|
* recursion.
|
|
|
|
|
*
|
2004-03-25 22:13:51 +00:00
|
|
|
* SM_GainFocus/SM_LoseFocus: These are sent to screens when they become the
|
|
|
|
|
* topmost screen, or stop being the topmost screen.
|
|
|
|
|
*
|
|
|
|
|
* A few subtleties:
|
|
|
|
|
*
|
|
|
|
|
* With delayed screens (eg. ScreenGameplay being pre-loaded by ScreenStage), SM_GainFocus
|
|
|
|
|
* isn't sent until the loaded screen actually is activated (put on the stack).
|
|
|
|
|
*
|
|
|
|
|
* With normal screen loads, the new screen is loaded before the old screen is destroyed.
|
|
|
|
|
* This means that the old dtor is called *after* the new ctor. If some global properties
|
|
|
|
|
* (eg. GAMESTATE) are being unset by the old screen's destructor, and set by the new
|
|
|
|
|
* screen's constructor, they'll happen in the wrong order. Use SM_GainFocus and
|
|
|
|
|
* SM_LoseFocus, instead.
|
|
|
|
|
*
|
|
|
|
|
* SM_LoseFocus is always sent after SM_GainFocus, and vice-versa: you can't gain focus
|
|
|
|
|
* if you already have it, and you can't lose focus if you don't have it.
|
|
|
|
|
*/
|
|
|
|
|
|
2004-06-08 01:24:17 +00:00
|
|
|
#include "global.h"
|
2002-05-20 08:59:37 +00:00
|
|
|
#include "ScreenManager.h"
|
2006-02-01 06:33:22 +00:00
|
|
|
#include "Preference.h"
|
2002-05-20 08:59:37 +00:00
|
|
|
#include "RageLog.h"
|
2004-11-25 19:16:46 +00:00
|
|
|
#include "RageUtil.h"
|
2004-07-08 00:10:34 +00:00
|
|
|
#include "GameSoundManager.h"
|
2002-12-30 00:13:47 +00:00
|
|
|
#include "RageDisplay.h"
|
2003-03-03 10:03:02 +00:00
|
|
|
#include "SongManager.h"
|
2003-04-18 23:55:20 +00:00
|
|
|
#include "RageTextureManager.h"
|
2003-11-05 04:56:41 +00:00
|
|
|
#include "ThemeManager.h"
|
2005-02-28 05:22:53 +00:00
|
|
|
#include "Screen.h"
|
2006-01-01 03:47:02 +00:00
|
|
|
#include "ScreenDimensions.h"
|
2004-12-09 09:41:06 +00:00
|
|
|
#include "Foreach.h"
|
2005-02-22 22:13:24 +00:00
|
|
|
#include "ActorUtil.h"
|
2005-07-13 06:44:44 +00:00
|
|
|
#include "GameLoop.h"
|
2002-05-20 08:59:37 +00:00
|
|
|
|
|
|
|
|
ScreenManager* SCREENMAN = NULL; // global and accessable from anywhere in our program
|
|
|
|
|
|
2006-01-31 05:33:58 +00:00
|
|
|
static Preference<bool> g_bConcurrentLoading( "ConcurrentLoading", false );
|
|
|
|
|
static Preference<bool> g_bDelayedScreenLoad( "DelayedScreenLoad", false );
|
2004-11-26 17:28:47 +00:00
|
|
|
|
|
|
|
|
// Screen registration
|
2006-01-22 01:00:06 +00:00
|
|
|
static map<RString,CreateScreenFn> *g_pmapRegistrees = NULL;
|
2004-11-26 17:28:47 +00:00
|
|
|
|
2005-07-22 05:38:56 +00:00
|
|
|
namespace
|
|
|
|
|
{
|
2005-07-22 05:39:52 +00:00
|
|
|
//
|
|
|
|
|
// in draw order first to last
|
|
|
|
|
//
|
|
|
|
|
struct LoadedScreen
|
|
|
|
|
{
|
|
|
|
|
Screen *m_pScreen;
|
2005-07-22 07:15:57 +00:00
|
|
|
|
|
|
|
|
/* Normally true. If false, the screen is owned by another screen
|
|
|
|
|
* and was given to us for use, and it's not ours to free. */
|
2005-07-22 05:39:52 +00:00
|
|
|
bool m_bDeleteWhenDone;
|
2005-07-22 07:15:57 +00:00
|
|
|
|
2005-07-22 05:39:52 +00:00
|
|
|
ScreenMessage m_SendOnPop;
|
2005-07-22 07:15:57 +00:00
|
|
|
|
|
|
|
|
LoadedScreen()
|
|
|
|
|
{
|
|
|
|
|
m_pScreen = NULL;
|
|
|
|
|
m_bDeleteWhenDone = true;
|
|
|
|
|
m_SendOnPop = SM_None;
|
|
|
|
|
}
|
2005-07-22 05:39:52 +00:00
|
|
|
};
|
2005-07-22 07:15:57 +00:00
|
|
|
|
2005-07-22 05:45:46 +00:00
|
|
|
Actor *g_pSharedBGA; // BGA object that's persistent between screens
|
2005-07-22 05:39:52 +00:00
|
|
|
vector<LoadedScreen> g_ScreenStack; // bottommost to topmost
|
2005-07-22 05:44:54 +00:00
|
|
|
vector<Screen*> g_OverlayScreens;
|
2006-01-22 01:00:06 +00:00
|
|
|
set<RString> g_setGroupedScreens;
|
|
|
|
|
set<RString> g_setPersistantScreens;
|
2005-07-22 05:44:54 +00:00
|
|
|
|
2005-07-22 08:39:07 +00:00
|
|
|
vector<LoadedScreen> g_vPreparedScreens;
|
2005-07-22 05:44:54 +00:00
|
|
|
vector<Actor*> g_vPreparedBackgrounds;
|
2005-07-22 08:39:07 +00:00
|
|
|
|
|
|
|
|
/* Add a screen to g_ScreenStack. This is the only function that adds to g_ScreenStack. */
|
|
|
|
|
void PushLoadedScreen( const LoadedScreen &ls )
|
|
|
|
|
{
|
2005-08-29 00:47:45 +00:00
|
|
|
// Be sure to push the screen first, so GetTopScreen returns the screen
|
|
|
|
|
// during BeginScreen.
|
2005-07-22 08:39:07 +00:00
|
|
|
g_ScreenStack.push_back( ls );
|
2006-01-14 05:32:54 +00:00
|
|
|
|
|
|
|
|
/* Set the name of the loading screen. */
|
|
|
|
|
ActorUtil::ActorParam LoadingScreen( "LoadingScreen", ls.m_pScreen->GetName() );
|
2005-07-22 08:39:07 +00:00
|
|
|
ls.m_pScreen->BeginScreen();
|
|
|
|
|
|
2006-01-14 05:32:54 +00:00
|
|
|
LoadingScreen.Release();
|
|
|
|
|
|
2005-07-22 08:39:07 +00:00
|
|
|
SCREENMAN->RefreshCreditsMessages();
|
|
|
|
|
|
|
|
|
|
SCREENMAN->PostMessageToTopScreen( SM_GainFocus, 0 );
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
bool ScreenIsPrepped( const RString &sScreenName )
|
2005-07-23 03:31:43 +00:00
|
|
|
{
|
|
|
|
|
FOREACH( LoadedScreen, g_vPreparedScreens, s )
|
|
|
|
|
{
|
|
|
|
|
if( s->m_pScreen->GetName() == sScreenName )
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-22 08:39:07 +00:00
|
|
|
/* If the named screen is loaded, remove it from the prepared list and
|
|
|
|
|
* return it in ls. */
|
2006-01-22 01:00:06 +00:00
|
|
|
bool GetPreppedScreen( const RString &sScreenName, LoadedScreen &ls )
|
2005-07-22 08:39:07 +00:00
|
|
|
{
|
|
|
|
|
FOREACH( LoadedScreen, g_vPreparedScreens, s )
|
|
|
|
|
{
|
|
|
|
|
if( s->m_pScreen->GetName() == sScreenName )
|
|
|
|
|
{
|
|
|
|
|
ls = *s;
|
|
|
|
|
g_vPreparedScreens.erase( s );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2005-07-23 03:09:55 +00:00
|
|
|
|
2005-07-23 06:36:11 +00:00
|
|
|
void BeforeDeleteScreen()
|
|
|
|
|
{
|
|
|
|
|
/* Deleting a screen can take enough time to cause a frame skip. */
|
|
|
|
|
SCREENMAN->ZeroNextUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-23 03:09:55 +00:00
|
|
|
/* If we're deleting a screen, it's probably releasing texture and other
|
|
|
|
|
* resources, so trigger cleanups. */
|
2005-07-23 06:36:11 +00:00
|
|
|
void AfterDeleteScreen()
|
2005-07-23 03:09:55 +00:00
|
|
|
{
|
|
|
|
|
/* Now that we've actually deleted a screen, it makes sense to clear out
|
|
|
|
|
* cached textures. */
|
|
|
|
|
TEXTUREMAN->DeleteCachedTextures();
|
|
|
|
|
|
|
|
|
|
/* Cleanup song data. This can free up a fair bit of memory, so do it after
|
|
|
|
|
* deleting screens. */
|
|
|
|
|
SONGMAN->Cleanup();
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-11 00:54:45 +00:00
|
|
|
/* Take ownership of all screens and backgrounds that are owned by
|
|
|
|
|
* us (this excludes screens where m_bDeleteWhenDone is false).
|
|
|
|
|
* Clear the prepared lists. The contents of apOut must be
|
|
|
|
|
* freed by the caller. */
|
|
|
|
|
void GrabPreparedActors( vector<Actor*> &apOut )
|
2005-07-23 03:09:55 +00:00
|
|
|
{
|
|
|
|
|
FOREACH( LoadedScreen, g_vPreparedScreens, s )
|
|
|
|
|
if( s->m_bDeleteWhenDone )
|
2006-01-11 00:54:45 +00:00
|
|
|
apOut.push_back( s->m_pScreen );
|
2005-07-23 03:09:55 +00:00
|
|
|
g_vPreparedScreens.clear();
|
|
|
|
|
FOREACH( Actor*, g_vPreparedBackgrounds, a )
|
2006-01-11 00:54:45 +00:00
|
|
|
apOut.push_back( *a );
|
2005-07-23 03:09:55 +00:00
|
|
|
g_vPreparedBackgrounds.clear();
|
|
|
|
|
|
|
|
|
|
g_setGroupedScreens.clear();
|
|
|
|
|
g_setPersistantScreens.clear();
|
2006-01-11 00:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called when changing screen groups. Delete all prepared screens,
|
|
|
|
|
* reset the screen group and list of persistant screens. */
|
|
|
|
|
void DeletePreparedScreens()
|
|
|
|
|
{
|
|
|
|
|
vector<Actor*> apActorsToDelete;
|
|
|
|
|
GrabPreparedActors( apActorsToDelete );
|
2005-07-23 03:09:55 +00:00
|
|
|
|
2006-01-11 00:54:45 +00:00
|
|
|
BeforeDeleteScreen();
|
|
|
|
|
FOREACH( Actor*, apActorsToDelete, a )
|
|
|
|
|
SAFE_DELETE( *a );
|
2005-07-23 06:36:11 +00:00
|
|
|
AfterDeleteScreen();
|
2005-07-23 03:09:55 +00:00
|
|
|
}
|
2005-07-22 05:38:56 +00:00
|
|
|
};
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void RegisterScreenClass( const RString& sClassName, CreateScreenFn pfn )
|
2004-11-26 17:28:47 +00:00
|
|
|
{
|
|
|
|
|
if( g_pmapRegistrees == NULL )
|
2006-01-22 01:00:06 +00:00
|
|
|
g_pmapRegistrees = new map<RString,CreateScreenFn>;
|
2004-11-26 17:28:47 +00:00
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
map<RString,CreateScreenFn>::iterator iter = g_pmapRegistrees->find( sClassName );
|
2004-11-26 19:45:03 +00:00
|
|
|
ASSERT_M( iter == g_pmapRegistrees->end(), ssprintf("Screen class '%s' already registered.", sClassName.c_str()) );
|
2004-11-26 17:28:47 +00:00
|
|
|
|
|
|
|
|
(*g_pmapRegistrees)[sClassName] = pfn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-03-15 00:02:54 +00:00
|
|
|
ScreenManager::ScreenManager()
|
|
|
|
|
{
|
2005-07-22 05:44:54 +00:00
|
|
|
g_pSharedBGA = new Actor;
|
2004-09-16 22:45:55 +00:00
|
|
|
|
2005-02-05 22:11:15 +00:00
|
|
|
m_bZeroNextUpdate = false;
|
2005-07-07 08:14:01 +00:00
|
|
|
m_PopTopScreen = SM_Invalid;
|
2005-07-13 06:44:44 +00:00
|
|
|
m_OnDonePreparingScreen = SM_Invalid;
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ScreenManager::~ScreenManager()
|
|
|
|
|
{
|
2002-07-31 19:40:40 +00:00
|
|
|
LOG->Trace( "ScreenManager::~ScreenManager()" );
|
2002-05-20 08:59:37 +00:00
|
|
|
|
2005-07-22 05:44:54 +00:00
|
|
|
SAFE_DELETE( g_pSharedBGA );
|
2005-07-22 05:38:56 +00:00
|
|
|
for( unsigned i=0; i<g_ScreenStack.size(); i++ )
|
2005-07-14 21:42:52 +00:00
|
|
|
{
|
2005-07-22 05:38:56 +00:00
|
|
|
if( g_ScreenStack[i].m_bDeleteWhenDone )
|
|
|
|
|
SAFE_DELETE( g_ScreenStack[i].m_pScreen );
|
2005-07-14 21:42:52 +00:00
|
|
|
}
|
2004-12-09 09:41:06 +00:00
|
|
|
DeletePreparedScreens();
|
2005-07-22 05:44:54 +00:00
|
|
|
for( unsigned i=0; i<g_OverlayScreens.size(); i++ )
|
|
|
|
|
SAFE_DELETE( g_OverlayScreens[i] );
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-16 22:45:55 +00:00
|
|
|
/* This is called when we start up, and when the theme changes or is reloaded. */
|
2004-09-16 22:05:24 +00:00
|
|
|
void ScreenManager::ThemeChanged()
|
2004-08-21 17:20:30 +00:00
|
|
|
{
|
2004-09-16 22:45:55 +00:00
|
|
|
LOG->Trace( "ScreenManager::ThemeChanged" );
|
|
|
|
|
|
2004-08-21 17:20:30 +00:00
|
|
|
// reload common sounds
|
|
|
|
|
m_soundStart.Load( THEME->GetPathS("Common","start") );
|
2005-03-20 08:23:08 +00:00
|
|
|
m_soundCoin.Load( THEME->GetPathS("Common","coin"), true );
|
2006-03-08 23:58:37 +00:00
|
|
|
m_soundCancel.Load( THEME->GetPathS("Common","cancel"), true );
|
2004-08-21 17:20:30 +00:00
|
|
|
m_soundInvalid.Load( THEME->GetPathS("Common","invalid") );
|
|
|
|
|
m_soundScreenshot.Load( THEME->GetPathS("Common","screenshot") );
|
2004-09-16 22:45:55 +00:00
|
|
|
|
2005-02-28 05:22:53 +00:00
|
|
|
// reload overlay screens
|
2005-07-22 05:44:54 +00:00
|
|
|
for( unsigned i=0; i<g_OverlayScreens.size(); i++ )
|
|
|
|
|
SAFE_DELETE( g_OverlayScreens[i] );
|
|
|
|
|
g_OverlayScreens.clear();
|
2005-02-28 05:22:53 +00:00
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
RString sOverlays = THEME->GetMetric( "Common","OverlayScreens" );
|
|
|
|
|
vector<RString> asOverlays;
|
2005-02-28 05:22:53 +00:00
|
|
|
split( sOverlays, ",", asOverlays );
|
|
|
|
|
for( unsigned i=0; i<asOverlays.size(); i++ )
|
|
|
|
|
{
|
2005-07-19 23:49:39 +00:00
|
|
|
Screen *pScreen = MakeNewScreen( asOverlays[i] );
|
2005-07-22 05:44:54 +00:00
|
|
|
g_OverlayScreens.push_back( pScreen );
|
2005-02-28 05:22:53 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-06 07:44:12 +00:00
|
|
|
// force recreate of new BGA
|
|
|
|
|
SAFE_DELETE( g_pSharedBGA );
|
|
|
|
|
g_pSharedBGA = new Actor;
|
|
|
|
|
|
2005-02-28 05:01:50 +00:00
|
|
|
this->RefreshCreditsMessages();
|
2004-08-21 17:20:30 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-16 05:30:31 +00:00
|
|
|
Screen *ScreenManager::GetTopScreen()
|
|
|
|
|
{
|
2005-07-22 05:38:56 +00:00
|
|
|
if( g_ScreenStack.empty() )
|
2004-02-16 05:30:31 +00:00
|
|
|
return NULL;
|
2005-07-22 05:38:56 +00:00
|
|
|
return g_ScreenStack[g_ScreenStack.size()-1].m_pScreen;
|
2005-03-08 03:52:18 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-01 06:54:15 +00:00
|
|
|
bool ScreenManager::AllowOperatorMenuButton() const
|
|
|
|
|
{
|
|
|
|
|
FOREACH( LoadedScreen, g_ScreenStack, s )
|
|
|
|
|
{
|
|
|
|
|
if( !s->m_pScreen->AllowOperatorMenuButton() )
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-08 03:52:18 +00:00
|
|
|
bool ScreenManager::IsStackedScreen( const Screen *pScreen ) const
|
|
|
|
|
{
|
|
|
|
|
/* True if the screen is in the screen stack, but not the first. */
|
2005-07-22 05:38:56 +00:00
|
|
|
for( unsigned i = 1; i < g_ScreenStack.size(); ++i )
|
|
|
|
|
if( g_ScreenStack[i].m_pScreen == pScreen )
|
2005-03-08 03:52:18 +00:00
|
|
|
return true;
|
|
|
|
|
return false;
|
2004-02-16 05:30:31 +00:00
|
|
|
}
|
|
|
|
|
|
2005-07-13 06:44:44 +00:00
|
|
|
static bool g_bIsConcurrentlyLoading = false;
|
|
|
|
|
|
2005-07-22 08:39:07 +00:00
|
|
|
/* Pop the top screen off the stack, sending SM_LoseFocus messages and
|
|
|
|
|
* returning the message the popped screen wants sent to the new top
|
|
|
|
|
* screen. Does not send SM_GainFocus. */
|
2005-07-26 03:08:03 +00:00
|
|
|
ScreenMessage ScreenManager::PopTopScreenInternal( bool bSendLoseFocus )
|
2005-07-22 08:39:07 +00:00
|
|
|
{
|
|
|
|
|
if( g_ScreenStack.empty() )
|
|
|
|
|
return SM_None;
|
|
|
|
|
|
|
|
|
|
LoadedScreen ls = g_ScreenStack.back();
|
|
|
|
|
g_ScreenStack.erase( g_ScreenStack.end()-1, g_ScreenStack.end() );
|
|
|
|
|
|
2005-07-26 03:08:03 +00:00
|
|
|
if( bSendLoseFocus )
|
|
|
|
|
ls.m_pScreen->HandleScreenMessage( SM_LoseFocus );
|
2006-03-16 05:38:47 +00:00
|
|
|
ls.m_pScreen->EndScreen();
|
2005-07-22 08:39:07 +00:00
|
|
|
|
2005-07-23 01:18:48 +00:00
|
|
|
if( g_setPersistantScreens.find(ls.m_pScreen->GetName()) != g_setPersistantScreens.end() )
|
2005-07-22 08:39:07 +00:00
|
|
|
{
|
2005-07-23 01:18:48 +00:00
|
|
|
/* Move the screen back to the prepared list. */
|
|
|
|
|
g_vPreparedScreens.push_back( ls );
|
2005-07-22 08:39:07 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-07-23 01:18:48 +00:00
|
|
|
if( ls.m_bDeleteWhenDone )
|
2005-07-23 03:09:55 +00:00
|
|
|
{
|
2005-07-23 06:36:11 +00:00
|
|
|
BeforeDeleteScreen();
|
2005-07-23 01:18:48 +00:00
|
|
|
SAFE_DELETE( ls.m_pScreen );
|
2005-07-23 06:36:11 +00:00
|
|
|
AfterDeleteScreen();
|
2005-07-23 03:09:55 +00:00
|
|
|
}
|
2005-07-22 08:39:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ls.m_SendOnPop;
|
|
|
|
|
}
|
|
|
|
|
|
2002-05-20 08:59:37 +00:00
|
|
|
void ScreenManager::Update( float fDeltaTime )
|
|
|
|
|
{
|
2005-07-07 08:14:01 +00:00
|
|
|
//
|
|
|
|
|
// Pop the top screen, if PopTopScreen was called.
|
|
|
|
|
//
|
|
|
|
|
if( m_PopTopScreen != SM_Invalid )
|
|
|
|
|
{
|
|
|
|
|
ScreenMessage SM = m_PopTopScreen;
|
|
|
|
|
m_PopTopScreen = SM_Invalid;
|
|
|
|
|
|
2005-07-22 08:39:07 +00:00
|
|
|
ScreenMessage SM2 = PopTopScreenInternal();
|
2005-07-07 08:14:01 +00:00
|
|
|
|
|
|
|
|
SendMessageToTopScreen( SM_GainFocus );
|
2005-07-22 08:39:07 +00:00
|
|
|
SendMessageToTopScreen( SM );
|
|
|
|
|
SendMessageToTopScreen( SM2 );
|
2005-07-07 08:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
2005-06-19 08:56:45 +00:00
|
|
|
/*
|
|
|
|
|
* Screens take some time to load. If we don't do this, then screens
|
2003-01-19 21:51:46 +00:00
|
|
|
* receive an initial update that includes all of the time they spent
|
|
|
|
|
* loading, which will chop off their tweens.
|
|
|
|
|
*
|
|
|
|
|
* We don't want to simply cap update times; for example, the stage
|
|
|
|
|
* screen sets a 4 second timer, preps the gameplay screen, and then
|
|
|
|
|
* displays the prepped screen after the timer runs out; this lets the
|
|
|
|
|
* load time be masked (as long as the load takes less than 4 seconds).
|
|
|
|
|
* If we cap that large update delta from the screen load, the update
|
|
|
|
|
* to load the new screen will come after 4 seconds plus the load time.
|
|
|
|
|
*
|
2003-04-25 03:41:38 +00:00
|
|
|
* So, let's just zero the first update for every screen.
|
2003-01-19 21:51:46 +00:00
|
|
|
*/
|
2005-07-22 05:38:56 +00:00
|
|
|
ASSERT( !g_ScreenStack.empty() || m_sDelayedScreen != "" ); // Why play the game if there is nothing showing?
|
2003-04-25 03:41:38 +00:00
|
|
|
|
2005-07-22 05:38:56 +00:00
|
|
|
Screen* pScreen = g_ScreenStack.empty() ? NULL : GetTopScreen();
|
2004-12-09 08:16:18 +00:00
|
|
|
|
2005-02-07 07:41:31 +00:00
|
|
|
bool bFirstUpdate = pScreen && pScreen->IsFirstUpdate();
|
|
|
|
|
|
2004-12-23 09:48:16 +00:00
|
|
|
/* Loading a new screen can take seconds and cause a big jump on the new
|
|
|
|
|
* Screen's first update. Clamp the first update delta so that the
|
|
|
|
|
* animations don't jump. */
|
2005-02-05 22:11:15 +00:00
|
|
|
if( pScreen && m_bZeroNextUpdate )
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace( "Zeroing this update. Was %f", fDeltaTime );
|
2004-12-23 09:48:16 +00:00
|
|
|
fDeltaTime = 0;
|
2005-02-05 22:11:15 +00:00
|
|
|
m_bZeroNextUpdate = false;
|
|
|
|
|
}
|
2004-12-23 09:48:16 +00:00
|
|
|
|
2005-07-12 03:12:18 +00:00
|
|
|
//
|
|
|
|
|
// Update screens.
|
|
|
|
|
//
|
|
|
|
|
{
|
2005-07-22 08:39:07 +00:00
|
|
|
for( unsigned i=0; i<g_ScreenStack.size(); i++ )
|
|
|
|
|
g_ScreenStack[i].m_pScreen->Update( fDeltaTime );
|
2004-12-23 09:48:16 +00:00
|
|
|
|
2005-07-22 05:44:54 +00:00
|
|
|
g_pSharedBGA->Update( fDeltaTime );
|
2005-07-12 03:12:18 +00:00
|
|
|
|
2005-07-22 05:44:54 +00:00
|
|
|
for( unsigned i=0; i<g_OverlayScreens.size(); i++ )
|
|
|
|
|
g_OverlayScreens[i]->Update( fDeltaTime );
|
2005-07-12 03:12:18 +00:00
|
|
|
}
|
|
|
|
|
//
|
|
|
|
|
// Handle messages after updating.
|
|
|
|
|
//
|
|
|
|
|
{
|
2005-07-22 05:38:56 +00:00
|
|
|
for( unsigned i=0; i<g_ScreenStack.size(); i++ )
|
|
|
|
|
g_ScreenStack[i].m_pScreen->ProcessMessages( fDeltaTime );
|
2005-07-12 03:12:18 +00:00
|
|
|
|
2005-07-22 05:44:54 +00:00
|
|
|
g_pSharedBGA->ProcessMessages( fDeltaTime );
|
2005-07-12 03:12:18 +00:00
|
|
|
|
2005-07-22 05:44:54 +00:00
|
|
|
for( unsigned i=0; i<g_OverlayScreens.size(); i++ )
|
|
|
|
|
g_OverlayScreens[i]->ProcessMessages( fDeltaTime );
|
2005-07-12 03:12:18 +00:00
|
|
|
}
|
2003-03-15 00:02:54 +00:00
|
|
|
|
2005-02-07 07:41:31 +00:00
|
|
|
/* The music may be started on the first update. If we're reading from a CD,
|
|
|
|
|
* it might not start immediately. Make sure we start playing the sound before
|
|
|
|
|
* continuing, since it's strange to start rendering before the music starts. */
|
|
|
|
|
if( bFirstUpdate )
|
2005-06-19 08:56:45 +00:00
|
|
|
SOUND->Flush();
|
2005-02-05 22:11:15 +00:00
|
|
|
|
2005-07-13 06:44:44 +00:00
|
|
|
/* If we're currently inside a background screen load, and m_sDelayedScreen
|
|
|
|
|
* is set, then the screen called SetNewScreen before we finished preparing.
|
|
|
|
|
* Postpone it until we're finished loading. */
|
|
|
|
|
if( !IsConcurrentlyLoading() && m_sDelayedScreen.size() != 0 )
|
2003-04-25 03:41:38 +00:00
|
|
|
{
|
|
|
|
|
LoadDelayedScreen();
|
|
|
|
|
}
|
2005-07-13 06:44:44 +00:00
|
|
|
|
|
|
|
|
if( !m_sDelayedConcurrentPrepare.empty() )
|
|
|
|
|
{
|
2005-08-15 15:19:34 +00:00
|
|
|
RunConcurrentlyPrepareScreen();
|
2005-07-13 06:44:44 +00:00
|
|
|
}
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
2005-07-13 06:44:44 +00:00
|
|
|
bool ScreenManager::IsConcurrentlyLoading() const
|
|
|
|
|
{
|
|
|
|
|
return g_bIsConcurrentlyLoading;
|
|
|
|
|
}
|
2002-05-20 08:59:37 +00:00
|
|
|
|
|
|
|
|
void ScreenManager::Draw()
|
|
|
|
|
{
|
2004-01-30 05:07:26 +00:00
|
|
|
/* If it hasn't been updated yet, skip the render. We can't call Update(0), since
|
|
|
|
|
* that'll confuse the "zero out the next update after loading a screen logic.
|
|
|
|
|
* If we don't render, don't call BeginFrame or EndFrame. That way, we won't
|
|
|
|
|
* clear the buffer, and we won't wait for vsync. */
|
2005-07-22 05:38:56 +00:00
|
|
|
if( g_ScreenStack.size() && g_ScreenStack.back().m_pScreen->IsFirstUpdate() )
|
2003-04-25 03:41:38 +00:00
|
|
|
return;
|
2004-01-30 05:07:26 +00:00
|
|
|
|
2004-09-19 01:38:12 +00:00
|
|
|
if( !DISPLAY->BeginFrame() )
|
|
|
|
|
return;
|
2003-03-17 01:12:58 +00:00
|
|
|
|
2006-01-01 03:47:02 +00:00
|
|
|
DISPLAY->CameraPushMatrix();
|
|
|
|
|
DISPLAY->LoadMenuPerspective( 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_CENTER_X, SCREEN_CENTER_Y );
|
2005-07-22 05:44:54 +00:00
|
|
|
g_pSharedBGA->Draw();
|
2006-01-01 03:47:02 +00:00
|
|
|
DISPLAY->CameraPopMatrix();
|
2004-12-09 08:16:18 +00:00
|
|
|
|
2005-07-22 05:38:56 +00:00
|
|
|
for( unsigned i=0; i<g_ScreenStack.size(); i++ ) // Draw all screens bottom to top
|
|
|
|
|
g_ScreenStack[i].m_pScreen->Draw();
|
2004-12-09 08:16:18 +00:00
|
|
|
|
2005-07-22 05:44:54 +00:00
|
|
|
for( unsigned i=0; i<g_OverlayScreens.size(); i++ )
|
|
|
|
|
g_OverlayScreens[i]->Draw();
|
2003-04-25 03:41:38 +00:00
|
|
|
|
2004-12-09 08:16:18 +00:00
|
|
|
|
2003-05-24 05:44:31 +00:00
|
|
|
DISPLAY->EndFrame();
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-09-05 02:26:50 +00:00
|
|
|
void ScreenManager::Input( const InputEventPlus &input )
|
2002-05-20 08:59:37 +00:00
|
|
|
{
|
2002-09-06 00:31:25 +00:00
|
|
|
// LOG->Trace( "ScreenManager::Input( %d-%d, %d-%d, %d-%d, %d-%d )",
|
|
|
|
|
// DeviceI.device, DeviceI.button, GameI.controller, GameI.button, MenuI.player, MenuI.button, StyleI.player, StyleI.col );
|
2002-05-20 08:59:37 +00:00
|
|
|
|
2005-03-09 02:45:33 +00:00
|
|
|
// First, give overlay screens a shot at the input. If OverlayInput returns
|
|
|
|
|
// true, it handled the input, so don't pass it further. OverlayInput could
|
|
|
|
|
// probably be merged with Input, but that would require changing all Input
|
|
|
|
|
// overloads, as well as all MenuLeft, etc. overloads.
|
2005-07-22 05:44:54 +00:00
|
|
|
for( unsigned i = 0; i < g_OverlayScreens.size(); ++i )
|
2005-03-09 02:45:33 +00:00
|
|
|
{
|
2005-07-22 05:44:54 +00:00
|
|
|
Screen *pScreen = g_OverlayScreens[i];
|
2005-09-05 02:26:50 +00:00
|
|
|
if( pScreen->OverlayInput(input) )
|
2005-03-09 02:45:33 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2005-02-28 05:33:24 +00:00
|
|
|
|
2005-03-21 04:26:08 +00:00
|
|
|
// Pass input to the topmost screen. If we have a new top screen pending, don't
|
|
|
|
|
// send to the old screen, but do send to overlay screens.
|
|
|
|
|
if( m_sDelayedScreen != "" )
|
|
|
|
|
return;
|
|
|
|
|
|
2005-07-22 05:38:56 +00:00
|
|
|
if( !g_ScreenStack.empty() )
|
2005-09-05 02:26:50 +00:00
|
|
|
g_ScreenStack.back().m_pScreen->Input( input );
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
2005-02-28 05:22:53 +00:00
|
|
|
/* Just create a new screen; don't do any associated cleanup. */
|
2006-01-22 01:00:06 +00:00
|
|
|
Screen* ScreenManager::MakeNewScreen( const RString &sScreenName )
|
2005-02-28 05:22:53 +00:00
|
|
|
{
|
|
|
|
|
RageTimer t;
|
|
|
|
|
LOG->Trace( "Loading screen name '%s'", sScreenName.c_str() );
|
|
|
|
|
|
2006-02-18 23:51:21 +00:00
|
|
|
RString sClassName = THEME->GetMetric( sScreenName,"Class" );
|
2005-02-28 05:22:53 +00:00
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
map<RString,CreateScreenFn>::iterator iter = g_pmapRegistrees->find( sClassName );
|
2006-02-18 23:51:21 +00:00
|
|
|
if( iter == g_pmapRegistrees->end() )
|
|
|
|
|
RageException::Throw( "Screen '%s' has an invalid class '%s'", sScreenName.c_str(), sClassName.c_str() );
|
2005-02-28 05:22:53 +00:00
|
|
|
|
2005-07-23 06:30:22 +00:00
|
|
|
this->ZeroNextUpdate();
|
|
|
|
|
|
2005-02-28 05:22:53 +00:00
|
|
|
CreateScreenFn pfn = iter->second;
|
2006-02-18 23:51:21 +00:00
|
|
|
Screen *ret = pfn( sScreenName );
|
2005-02-28 05:22:53 +00:00
|
|
|
|
2006-02-18 23:51:21 +00:00
|
|
|
LOG->Trace( "Loaded '%s' ('%s') in %f", sScreenName.c_str(), sClassName.c_str(), t.GetDeltaTime() );
|
2005-02-28 05:22:53 +00:00
|
|
|
|
2006-06-24 00:50:08 +00:00
|
|
|
// Screens may not call SetNewScreen from the ctor or Init().
|
|
|
|
|
ASSERT_M( m_sDelayedScreen.empty(), m_sDelayedScreen );
|
|
|
|
|
|
2005-02-28 05:22:53 +00:00
|
|
|
return ret;
|
|
|
|
|
}
|
2002-08-27 03:59:22 +00:00
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void ScreenManager::PrepareScreen( const RString &sScreenName )
|
2002-09-04 00:25:30 +00:00
|
|
|
{
|
2005-07-07 01:32:15 +00:00
|
|
|
// If the screen is already prepared, stop.
|
2005-07-23 03:31:43 +00:00
|
|
|
if( ScreenIsPrepped(sScreenName) )
|
|
|
|
|
return;
|
2005-07-07 01:32:15 +00:00
|
|
|
|
|
|
|
|
Screen* pNewScreen = MakeNewScreen(sScreenName);
|
2005-07-22 08:39:07 +00:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
LoadedScreen ls;
|
|
|
|
|
ls.m_pScreen = pNewScreen;
|
|
|
|
|
|
|
|
|
|
g_vPreparedScreens.push_back( ls );
|
|
|
|
|
}
|
2005-07-07 01:32:15 +00:00
|
|
|
|
|
|
|
|
/* Don't delete previously prepared versions of the screen's background,
|
|
|
|
|
* and only prepare it if it's different than the current background
|
|
|
|
|
* and not already loaded. */
|
2006-02-05 05:50:38 +00:00
|
|
|
RString sNewBGA = THEME->GetPathB(sScreenName,"background");
|
2005-07-07 01:32:15 +00:00
|
|
|
|
2005-07-22 05:44:54 +00:00
|
|
|
if( !sNewBGA.empty() && sNewBGA != g_pSharedBGA->GetName() )
|
2005-07-07 01:32:15 +00:00
|
|
|
{
|
|
|
|
|
Actor *pNewBGA = NULL;
|
2005-07-22 05:44:54 +00:00
|
|
|
FOREACH( Actor*, g_vPreparedBackgrounds, a )
|
2004-12-09 09:41:06 +00:00
|
|
|
{
|
2005-07-19 06:20:18 +00:00
|
|
|
if( (*a)->GetName() == sNewBGA )
|
2005-07-07 01:32:15 +00:00
|
|
|
{
|
|
|
|
|
pNewBGA = *a;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2005-07-03 06:13:30 +00:00
|
|
|
}
|
2005-07-06 23:55:48 +00:00
|
|
|
|
2005-07-07 01:32:15 +00:00
|
|
|
// Create the new background before deleting the previous so that we keep
|
|
|
|
|
// any common textures loaded.
|
|
|
|
|
if( pNewBGA == NULL )
|
|
|
|
|
{
|
2005-07-29 04:08:33 +00:00
|
|
|
LOG->Trace( "Loading screen background \"%s\"", sNewBGA.c_str() );
|
2005-07-22 08:39:07 +00:00
|
|
|
Actor *pActor = ActorUtil::MakeActor( sNewBGA );
|
|
|
|
|
pActor->SetName( sNewBGA );
|
|
|
|
|
g_vPreparedBackgrounds.push_back( pActor );
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-08-25 18:12:23 +00:00
|
|
|
|
|
|
|
|
TEXTUREMAN->DiagnosticOutput();
|
2005-07-22 08:39:07 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void ScreenManager::GroupScreen( const RString &sScreenName )
|
2005-07-22 08:39:07 +00:00
|
|
|
{
|
|
|
|
|
g_setGroupedScreens.insert( sScreenName );
|
2002-09-04 00:25:30 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void ScreenManager::PersistantScreen( const RString &sScreenName )
|
2005-07-23 01:18:48 +00:00
|
|
|
{
|
|
|
|
|
g_setPersistantScreens.insert( sScreenName );
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
bool ScreenManager::ConcurrentlyPrepareScreen( const RString &sScreenName, ScreenMessage SM )
|
2005-07-13 06:44:44 +00:00
|
|
|
{
|
|
|
|
|
ASSERT_M( m_sDelayedConcurrentPrepare == "", m_sDelayedConcurrentPrepare );
|
|
|
|
|
|
|
|
|
|
if( !g_bConcurrentLoading || !DISPLAY->SupportsThreadedRendering() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
LOG->Trace( "ConcurrentlyPrepareScreen(%s)", sScreenName.c_str() );
|
|
|
|
|
ASSERT( !IsConcurrentlyLoading() );
|
|
|
|
|
|
|
|
|
|
m_sDelayedConcurrentPrepare = sScreenName;
|
|
|
|
|
m_OnDonePreparingScreen = SM;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-15 15:19:34 +00:00
|
|
|
void ScreenManager::RunConcurrentlyPrepareScreen()
|
|
|
|
|
{
|
|
|
|
|
/* Don't call BackgroundPrepareScreen() from within another background load. */
|
|
|
|
|
ASSERT( !IsConcurrentlyLoading() );
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
RString sScreenName = m_sDelayedConcurrentPrepare;
|
2005-08-15 15:19:34 +00:00
|
|
|
m_sDelayedConcurrentPrepare = "";
|
|
|
|
|
|
|
|
|
|
ScreenMessage SM = m_OnDonePreparingScreen;
|
|
|
|
|
m_OnDonePreparingScreen = SM_None;
|
|
|
|
|
|
2005-08-15 15:44:29 +00:00
|
|
|
/* If the screen is already prepared, we're all set. */
|
2006-01-17 01:49:18 +00:00
|
|
|
if( !ScreenIsPrepped(sScreenName) )
|
2005-08-15 15:44:29 +00:00
|
|
|
{
|
2006-01-17 01:49:18 +00:00
|
|
|
g_bIsConcurrentlyLoading = true;
|
2006-01-24 03:53:17 +00:00
|
|
|
GameLoop::StartConcurrentRendering();
|
2005-08-15 15:19:34 +00:00
|
|
|
|
2006-01-17 01:49:18 +00:00
|
|
|
if( g_setGroupedScreens.find(sScreenName) == g_setGroupedScreens.end() )
|
|
|
|
|
DeletePreparedScreens();
|
2006-01-22 00:13:27 +00:00
|
|
|
|
2006-01-17 01:49:18 +00:00
|
|
|
PrepareScreen( sScreenName );
|
2006-01-22 00:13:27 +00:00
|
|
|
|
2006-01-24 03:53:17 +00:00
|
|
|
GameLoop::FinishConcurrentRendering();
|
2006-01-17 01:49:18 +00:00
|
|
|
g_bIsConcurrentlyLoading = false;
|
2005-08-15 15:19:34 +00:00
|
|
|
|
2006-01-17 01:49:18 +00:00
|
|
|
LOG->Trace( "Concurrent prepare of %s finished", sScreenName.c_str() );
|
|
|
|
|
}
|
2005-08-15 15:19:34 +00:00
|
|
|
|
|
|
|
|
/* We're done. Send the message. The screen is allowed to start
|
|
|
|
|
* another concurrent prepare from this message. */
|
|
|
|
|
SendMessageToTopScreen( SM );
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void ScreenManager::SetNewScreen( const RString &sScreenName )
|
2002-05-20 08:59:37 +00:00
|
|
|
{
|
2005-01-07 04:50:08 +00:00
|
|
|
ASSERT( sScreenName != "" );
|
2004-12-09 09:41:06 +00:00
|
|
|
m_sDelayedScreen = sScreenName;
|
2003-04-25 03:41:38 +00:00
|
|
|
}
|
|
|
|
|
|
2006-06-24 01:27:01 +00:00
|
|
|
/* Activate the screen and/or its background, if either are loaded. Return true if both were
|
|
|
|
|
* activated. */
|
2006-06-24 00:59:23 +00:00
|
|
|
bool ScreenManager::ActivatePreparedScreenAndBackground( const RString &sScreenName )
|
2003-04-25 03:41:38 +00:00
|
|
|
{
|
2006-06-24 05:07:04 +00:00
|
|
|
bool bLoadedBoth = true;
|
|
|
|
|
|
2004-12-09 09:41:06 +00:00
|
|
|
//
|
2005-07-07 01:32:15 +00:00
|
|
|
// Find the prepped screen.
|
2004-12-09 09:41:06 +00:00
|
|
|
//
|
2005-07-22 08:39:07 +00:00
|
|
|
LoadedScreen ls;
|
2006-01-25 05:49:49 +00:00
|
|
|
if( !GetPreppedScreen(sScreenName, ls) )
|
2006-06-24 05:07:04 +00:00
|
|
|
{
|
|
|
|
|
bLoadedBoth = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace("... PushScreen");
|
|
|
|
|
PushLoadedScreen( ls );
|
|
|
|
|
}
|
2004-12-09 09:41:06 +00:00
|
|
|
|
2005-07-07 01:32:15 +00:00
|
|
|
// Find the prepared shared background (if any), and activate it.
|
2006-02-05 05:50:38 +00:00
|
|
|
RString sNewBGA = THEME->GetPathB(sScreenName,"background");
|
2005-07-22 05:44:54 +00:00
|
|
|
if( sNewBGA != g_pSharedBGA->GetName() )
|
2004-12-11 10:22:42 +00:00
|
|
|
{
|
2005-07-07 01:32:15 +00:00
|
|
|
Actor *pNewBGA = NULL;
|
2005-07-02 22:44:56 +00:00
|
|
|
if( sNewBGA.empty() )
|
2006-02-24 19:37:28 +00:00
|
|
|
{
|
2005-07-02 22:44:56 +00:00
|
|
|
pNewBGA = new Actor;
|
2006-02-24 19:37:28 +00:00
|
|
|
}
|
2005-07-02 22:44:56 +00:00
|
|
|
else
|
2005-07-07 01:32:15 +00:00
|
|
|
{
|
2005-07-22 05:44:54 +00:00
|
|
|
FOREACH( Actor*, g_vPreparedBackgrounds, a )
|
2005-07-07 01:32:15 +00:00
|
|
|
{
|
2005-07-19 06:20:18 +00:00
|
|
|
if( (*a)->GetName() == sNewBGA )
|
2005-07-07 01:32:15 +00:00
|
|
|
{
|
|
|
|
|
pNewBGA = *a;
|
2005-07-22 05:44:54 +00:00
|
|
|
g_vPreparedBackgrounds.erase( a );
|
2005-07-07 01:32:15 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-06-24 05:07:04 +00:00
|
|
|
|
|
|
|
|
/* If the BGA isn't loaded yet, load a dummy actor. If we're not going to use the same
|
|
|
|
|
* BGA for the new screen, always move the old BGA back to g_vPreparedBackgrounds now. */
|
|
|
|
|
if( pNewBGA == NULL )
|
|
|
|
|
{
|
|
|
|
|
bLoadedBoth = false;
|
|
|
|
|
pNewBGA = new Actor;
|
|
|
|
|
}
|
2005-07-02 22:44:56 +00:00
|
|
|
|
2005-10-25 12:42:32 +00:00
|
|
|
/* Move the old background back to the prepared list, or delete it if
|
|
|
|
|
* it's a blank actor. */
|
|
|
|
|
if( g_pSharedBGA->GetName() == "" )
|
|
|
|
|
delete g_pSharedBGA;
|
|
|
|
|
else
|
|
|
|
|
g_vPreparedBackgrounds.push_back( g_pSharedBGA );
|
2005-07-22 05:44:54 +00:00
|
|
|
g_pSharedBGA = pNewBGA;
|
2006-06-24 01:27:01 +00:00
|
|
|
g_pSharedBGA->PlayCommand( "On" );
|
2004-12-11 10:22:42 +00:00
|
|
|
}
|
2004-12-11 09:54:08 +00:00
|
|
|
|
2006-06-24 05:07:04 +00:00
|
|
|
return bLoadedBoth;
|
2006-01-25 05:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ScreenManager::LoadDelayedScreen()
|
|
|
|
|
{
|
|
|
|
|
RString sScreenName = m_sDelayedScreen;
|
|
|
|
|
m_sDelayedScreen = "";
|
|
|
|
|
|
|
|
|
|
// Pop the top screen, if any.
|
|
|
|
|
ScreenMessage SM = PopTopScreenInternal();
|
|
|
|
|
|
2006-01-25 06:24:22 +00:00
|
|
|
/* If the screen is already prepared, activate it before performing any cleanup, so
|
|
|
|
|
* it doesn't get deleted by cleanup. */
|
2006-06-24 00:59:23 +00:00
|
|
|
bool bLoaded = ActivatePreparedScreenAndBackground( sScreenName );
|
2006-01-25 06:24:22 +00:00
|
|
|
|
2006-01-25 05:49:49 +00:00
|
|
|
vector<Actor*> apActorsToDelete;
|
2006-01-25 06:25:11 +00:00
|
|
|
if( g_setGroupedScreens.find(sScreenName) == g_setGroupedScreens.end() )
|
2006-01-25 05:49:49 +00:00
|
|
|
{
|
2006-01-25 06:19:29 +00:00
|
|
|
/* It's time to delete all old prepared screens. Depending on DelayedScreenLoad,
|
|
|
|
|
* we can either delete the screens before or after we load the new screen. Either
|
2006-01-25 06:23:06 +00:00
|
|
|
* way, we must remove them from the prepared list before we prepare new screens.
|
|
|
|
|
*
|
|
|
|
|
* If DelayedScreenLoad is true, delete them now; this lowers memory requirements,
|
|
|
|
|
* but results in redundant loads as we unload common data. */
|
2006-01-31 05:33:58 +00:00
|
|
|
if( g_bDelayedScreenLoad )
|
2006-01-25 05:49:49 +00:00
|
|
|
DeletePreparedScreens();
|
|
|
|
|
else
|
|
|
|
|
GrabPreparedActors( apActorsToDelete );
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-25 06:24:22 +00:00
|
|
|
/* If the screen wasn't already prepared, load it. */
|
2006-06-24 00:59:23 +00:00
|
|
|
if( !bLoaded )
|
2006-01-25 06:24:22 +00:00
|
|
|
{
|
|
|
|
|
PrepareScreen( sScreenName );
|
2006-06-24 00:59:23 +00:00
|
|
|
bLoaded = ActivatePreparedScreenAndBackground( sScreenName );
|
|
|
|
|
ASSERT( bLoaded );
|
2006-01-25 06:24:22 +00:00
|
|
|
}
|
2006-01-25 05:49:49 +00:00
|
|
|
|
2006-01-11 00:59:38 +00:00
|
|
|
if( !apActorsToDelete.empty() )
|
|
|
|
|
{
|
|
|
|
|
BeforeDeleteScreen();
|
|
|
|
|
FOREACH( Actor*, apActorsToDelete, a )
|
|
|
|
|
SAFE_DELETE( *a );
|
|
|
|
|
AfterDeleteScreen();
|
|
|
|
|
}
|
2005-06-19 08:51:09 +00:00
|
|
|
|
2006-06-24 01:21:21 +00:00
|
|
|
MESSAGEMAN->Broadcast( Message_ScreenChanged );
|
|
|
|
|
|
2005-07-22 08:39:07 +00:00
|
|
|
SendMessageToTopScreen( SM );
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void ScreenManager::AddNewScreenToTop( const RString &sScreenName, ScreenMessage SendOnPop )
|
2005-02-05 22:11:15 +00:00
|
|
|
{
|
2005-07-22 08:39:07 +00:00
|
|
|
// Load the screen, if it's not already prepared.
|
2005-07-23 01:18:48 +00:00
|
|
|
PrepareScreen( sScreenName );
|
2005-07-22 08:39:07 +00:00
|
|
|
|
|
|
|
|
// Find the prepped screen.
|
|
|
|
|
LoadedScreen ls;
|
|
|
|
|
bool b = GetPreppedScreen( sScreenName, ls );
|
|
|
|
|
ASSERT( b );
|
|
|
|
|
|
|
|
|
|
ls.m_SendOnPop = SendOnPop;
|
|
|
|
|
|
|
|
|
|
if( g_ScreenStack.size() )
|
|
|
|
|
g_ScreenStack.back().m_pScreen->HandleScreenMessage( SM_LoseFocus );
|
|
|
|
|
PushLoadedScreen( ls );
|
2003-02-19 05:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
2002-07-27 19:29:51 +00:00
|
|
|
void ScreenManager::PopTopScreen( ScreenMessage SM )
|
2002-05-20 08:59:37 +00:00
|
|
|
{
|
2005-07-22 05:38:56 +00:00
|
|
|
ASSERT( g_ScreenStack.size() > 0 );
|
2005-03-10 21:18:04 +00:00
|
|
|
|
2005-07-07 08:14:01 +00:00
|
|
|
m_PopTopScreen = SM;
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
2005-07-26 03:08:03 +00:00
|
|
|
/* Clear the screen stack; only used before major, unusual state changes,
|
|
|
|
|
* such as resetting the game or jumping to the service menu. Don't call
|
|
|
|
|
* from inside a screen. */
|
|
|
|
|
void ScreenManager::PopAllScreens()
|
|
|
|
|
{
|
|
|
|
|
if( g_ScreenStack.empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Make sure only the top screen receives LoseFocus. */
|
|
|
|
|
bool bFirst = true;
|
|
|
|
|
while( !g_ScreenStack.empty() )
|
|
|
|
|
{
|
|
|
|
|
PopTopScreenInternal( bFirst );
|
|
|
|
|
bFirst = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-25 21:17:29 +00:00
|
|
|
void ScreenManager::PostMessageToTopScreen( ScreenMessage SM, float fDelay )
|
2002-05-20 08:59:37 +00:00
|
|
|
{
|
2005-07-14 21:42:52 +00:00
|
|
|
Screen* pTopScreen = GetTopScreen();
|
|
|
|
|
if( pTopScreen != NULL )
|
2005-03-10 21:18:04 +00:00
|
|
|
pTopScreen->PostScreenMessage( SM, fDelay );
|
2003-03-25 21:17:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ScreenManager::SendMessageToTopScreen( ScreenMessage SM )
|
|
|
|
|
{
|
2005-07-14 21:42:52 +00:00
|
|
|
Screen* pTopScreen = GetTopScreen();
|
|
|
|
|
if( pTopScreen != NULL )
|
2005-03-10 21:18:04 +00:00
|
|
|
pTopScreen->HandleScreenMessage( SM );
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void ScreenManager::SystemMessage( const RString &sMessage )
|
2002-05-20 08:59:37 +00:00
|
|
|
{
|
2005-02-28 05:10:08 +00:00
|
|
|
m_sSystemMessage = sMessage;
|
2003-09-15 20:44:25 +00:00
|
|
|
LOG->Trace( "%s", sMessage.c_str() );
|
2005-02-28 05:10:08 +00:00
|
|
|
MESSAGEMAN->Broadcast( "SystemMessage" );
|
2002-05-20 08:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void ScreenManager::SystemMessageNoAnimate( const RString &sMessage )
|
2003-11-20 11:55:45 +00:00
|
|
|
{
|
2004-04-23 00:26:51 +00:00
|
|
|
// LOG->Trace( "%s", sMessage.c_str() ); // don't log because the caller is likely calling us every frame
|
2005-02-28 05:10:08 +00:00
|
|
|
m_sSystemMessage = sMessage;
|
|
|
|
|
MESSAGEMAN->Broadcast( "SystemMessageNoAnimate" );
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-15 10:01:08 +00:00
|
|
|
void ScreenManager::HideSystemMessage()
|
|
|
|
|
{
|
|
|
|
|
MESSAGEMAN->Broadcast( "HideSystemMessage" );
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-28 05:33:24 +00:00
|
|
|
|
2002-08-13 23:26:46 +00:00
|
|
|
void ScreenManager::RefreshCreditsMessages()
|
|
|
|
|
{
|
2005-02-28 05:01:50 +00:00
|
|
|
MESSAGEMAN->Broadcast( "RefreshCreditText" );
|
2004-03-13 10:26:51 +00:00
|
|
|
|
|
|
|
|
/* This is called when GAMESTATE->m_bSideIsJoined changes. */
|
2006-01-22 01:00:06 +00:00
|
|
|
RString joined;
|
2005-02-26 07:56:04 +00:00
|
|
|
FOREACH_HumanPlayer( pn )
|
2004-03-13 10:26:51 +00:00
|
|
|
{
|
2005-02-26 07:56:04 +00:00
|
|
|
if( joined != "" )
|
|
|
|
|
joined += ", ";
|
|
|
|
|
joined += ssprintf( "P%i", pn+1 );
|
2004-03-13 10:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( joined == "" )
|
|
|
|
|
joined = "none";
|
|
|
|
|
|
|
|
|
|
LOG->MapLog( "JOINED", "Players joined: %s", joined.c_str() );
|
2003-02-12 07:52:20 +00:00
|
|
|
}
|
|
|
|
|
|
2005-07-07 04:48:07 +00:00
|
|
|
void ScreenManager::ZeroNextUpdate()
|
|
|
|
|
{
|
2005-07-13 06:44:44 +00:00
|
|
|
if( !IsConcurrentlyLoading() )
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace("ScreenManager::ZeroNextUpdate");
|
|
|
|
|
m_bZeroNextUpdate = true;
|
2005-07-19 23:22:50 +00:00
|
|
|
|
|
|
|
|
/* Loading probably took a little while. Let's reset stats. This prevents us
|
|
|
|
|
* from displaying an unnaturally low FPS value, and the next FPS value we
|
|
|
|
|
* display will be accurate, which makes skips in the initial tween-ins more
|
|
|
|
|
* apparent. */
|
|
|
|
|
DISPLAY->ResetStats();
|
2005-07-13 06:44:44 +00:00
|
|
|
}
|
2005-07-07 04:48:07 +00:00
|
|
|
}
|
|
|
|
|
|
2005-10-04 07:47:15 +00:00
|
|
|
void ScreenManager::PlayInvalidSound()
|
|
|
|
|
{
|
2005-10-13 22:07:40 +00:00
|
|
|
RageSoundParams p;
|
|
|
|
|
p.m_bIsCriticalSound = true;
|
|
|
|
|
m_soundInvalid.Play( &p );
|
2005-10-04 07:47:15 +00:00
|
|
|
}
|
|
|
|
|
|
2004-03-25 21:41:11 +00:00
|
|
|
/* Always play these sounds, even if we're in a silent attract loop. */
|
|
|
|
|
void ScreenManager::PlayStartSound()
|
|
|
|
|
{
|
|
|
|
|
RageSoundParams p;
|
2005-10-04 07:47:15 +00:00
|
|
|
p.m_bIsCriticalSound = true;
|
2004-03-25 21:41:11 +00:00
|
|
|
m_soundStart.Play( &p );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ScreenManager::PlayCoinSound()
|
|
|
|
|
{
|
|
|
|
|
RageSoundParams p;
|
2005-10-04 06:51:06 +00:00
|
|
|
p.m_bIsCriticalSound = true;
|
2004-03-25 21:41:11 +00:00
|
|
|
m_soundCoin.Play( &p );
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-08 23:58:37 +00:00
|
|
|
void ScreenManager::PlayCancelSound()
|
|
|
|
|
{
|
|
|
|
|
RageSoundParams p;
|
|
|
|
|
p.m_bIsCriticalSound = true;
|
|
|
|
|
m_soundCancel.Play( &p );
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-25 21:41:11 +00:00
|
|
|
void ScreenManager::PlayScreenshotSound()
|
|
|
|
|
{
|
|
|
|
|
RageSoundParams p;
|
2005-10-04 07:47:15 +00:00
|
|
|
p.m_bIsCriticalSound = true;
|
2004-03-25 21:41:11 +00:00
|
|
|
m_soundScreenshot.Play( &p );
|
|
|
|
|
}
|
2004-05-01 23:19:33 +00:00
|
|
|
|
2004-12-09 08:16:18 +00:00
|
|
|
void ScreenManager::PlaySharedBackgroundOffCommand()
|
|
|
|
|
{
|
2005-07-22 05:44:54 +00:00
|
|
|
g_pSharedBGA->PlayCommand("Off");
|
2004-12-09 08:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
2005-02-28 07:06:29 +00:00
|
|
|
// lua start
|
|
|
|
|
#include "LuaBinding.h"
|
|
|
|
|
|
2005-06-20 09:49:07 +00:00
|
|
|
class LunaScreenManager: public Luna<ScreenManager>
|
2005-02-28 07:06:29 +00:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
LunaScreenManager() { LUA->Register( Register ); }
|
|
|
|
|
|
2005-07-23 01:18:48 +00:00
|
|
|
// Note: PrepareScreen binding is not allowed; loading data inside
|
|
|
|
|
// Lua causes the Lua lock to be held for the duration of the load,
|
|
|
|
|
// which blocks concurrent rendering
|
2005-02-28 07:06:29 +00:00
|
|
|
static int SetNewScreen( T* p, lua_State *L ) { p->SetNewScreen( SArg(1) ); return 0; }
|
2005-04-15 07:00:21 +00:00
|
|
|
static int GetTopScreen( T* p, lua_State *L )
|
|
|
|
|
{
|
|
|
|
|
Actor *pScreen = p->GetTopScreen();
|
|
|
|
|
if( pScreen != NULL )
|
|
|
|
|
pScreen->PushSelf(L);
|
|
|
|
|
else
|
|
|
|
|
lua_pushnil( L );
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2005-05-18 04:44:15 +00:00
|
|
|
static int SystemMessage( T* p, lua_State *L ) { p->SystemMessage( SArg(1) ); return 0; }
|
2006-01-25 05:10:40 +00:00
|
|
|
static int ConcurrentlyPrepareScreen( T* p, lua_State *L ) { p->ConcurrentlyPrepareScreen( SArg(1) ); return 0; }
|
2005-02-28 07:06:29 +00:00
|
|
|
|
|
|
|
|
static void Register(lua_State *L)
|
|
|
|
|
{
|
2005-09-10 02:47:04 +00:00
|
|
|
ADD_METHOD( SetNewScreen );
|
|
|
|
|
ADD_METHOD( GetTopScreen );
|
|
|
|
|
ADD_METHOD( SystemMessage );
|
2006-01-25 05:10:40 +00:00
|
|
|
ADD_METHOD( ConcurrentlyPrepareScreen );
|
2005-05-18 04:44:15 +00:00
|
|
|
|
2005-02-28 07:06:29 +00:00
|
|
|
Luna<T>::Register( L );
|
|
|
|
|
|
|
|
|
|
// Add global singleton if constructed already. If it's not constructed yet,
|
|
|
|
|
// then we'll register it later when we reinit Lua just before
|
|
|
|
|
// initializing the display.
|
|
|
|
|
if( SCREENMAN )
|
|
|
|
|
{
|
|
|
|
|
lua_pushstring(L, "SCREENMAN");
|
2005-06-15 02:21:24 +00:00
|
|
|
SCREENMAN->PushSelf( L );
|
2005-02-28 07:06:29 +00:00
|
|
|
lua_settable(L, LUA_GLOBALSINDEX);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LUA_REGISTER_CLASS( ScreenManager )
|
|
|
|
|
// lua end
|
|
|
|
|
|
2004-06-08 01:24:17 +00:00
|
|
|
/*
|
|
|
|
|
* (c) 2001-2003 Chris Danford, Glenn Maynard
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|