Files
itgmania212121/stepmania/src/ScreenManager.cpp
T

719 lines
21 KiB
C++
Raw Normal View History

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"
2002-07-23 01:41:40 +00:00
#include "PrefsManager.h"
2002-05-20 08:59:37 +00:00
#include "RageLog.h"
2004-11-25 19:16:46 +00:00
#include "RageUtil.h"
#include "GameState.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"
#include "ThemeManager.h"
#include "Screen.h"
#include "Foreach.h"
2005-02-22 22:13:24 +00:00
#include "ActorUtil.h"
#include "GameLoop.h"
2002-05-20 08:59:37 +00:00
ScreenManager* SCREENMAN = NULL; // global and accessable from anywhere in our program
static Preference<bool> g_bConcurrentLoading( "ConcurrentLoading", false );
2004-11-26 17:28:47 +00:00
// Screen registration
static map<CString,CreateScreenFn> *g_pmapRegistrees = NULL;
void ScreenManager::Register( const CString& sClassName, CreateScreenFn pfn )
{
if( g_pmapRegistrees == NULL )
g_pmapRegistrees = new map<CString,CreateScreenFn>;
map<CString,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;
}
ScreenManager::ScreenManager()
{
2005-02-22 22:13:24 +00:00
m_pSharedBGA = new Actor;
m_bZeroNextUpdate = false;
2005-07-07 08:14:01 +00:00
m_PopTopScreen = SM_Invalid;
m_OnDonePreparingScreen = SM_Invalid;
2002-05-20 08:59:37 +00:00
}
ScreenManager::~ScreenManager()
{
LOG->Trace( "ScreenManager::~ScreenManager()" );
2002-05-20 08:59:37 +00:00
SAFE_DELETE( m_pSharedBGA );
for( unsigned i=0; i<m_ScreenStack.size(); i++ )
{
if( m_ScreenStack[i].m_bDeleteWhenDone )
SAFE_DELETE( m_ScreenStack[i].m_pScreen );
}
DeletePreparedScreens();
for( unsigned i=0; i<m_OverlayScreens.size(); i++ )
SAFE_DELETE( m_OverlayScreens[i] );
2002-05-20 08:59:37 +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()
{
LOG->Trace( "ScreenManager::ThemeChanged" );
// reload common sounds
m_soundStart.Load( THEME->GetPathS("Common","start") );
m_soundCoin.Load( THEME->GetPathS("Common","coin"), true );
m_soundInvalid.Load( THEME->GetPathS("Common","invalid") );
m_soundScreenshot.Load( THEME->GetPathS("Common","screenshot") );
// reload overlay screens
for( unsigned i=0; i<m_OverlayScreens.size(); i++ )
SAFE_DELETE( m_OverlayScreens[i] );
m_OverlayScreens.clear();
CString sOverlays = THEME->GetMetric( "Common","OverlayScreens" );
vector<CString> asOverlays;
split( sOverlays, ",", asOverlays );
for( unsigned i=0; i<asOverlays.size(); i++ )
{
Screen *pScreen = MakeNewScreenInternal( asOverlays[i] );
m_OverlayScreens.push_back( pScreen );
}
this->RefreshCreditsMessages();
}
Screen *ScreenManager::GetTopScreen()
{
if( m_ScreenStack.empty() )
return NULL;
return m_ScreenStack[m_ScreenStack.size()-1].m_pScreen;
}
bool ScreenManager::IsStackedScreen( const Screen *pScreen ) const
{
/* True if the screen is in the screen stack, but not the first. */
for( unsigned i = 1; i < m_ScreenStack.size(); ++i )
if( m_ScreenStack[i].m_pScreen == pScreen )
return true;
return false;
}
static bool g_bIsConcurrentlyLoading = false;
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;
LoadedScreen ls = m_ScreenStack.back(); // top menu
2005-07-07 08:14:01 +00:00
m_ScreenStack.erase( m_ScreenStack.end()-1, m_ScreenStack.end() );
ls.m_pScreen->HandleScreenMessage( SM_LoseFocus );
2005-07-07 08:14:01 +00:00
SendMessageToTopScreen( SM );
SendMessageToTopScreen( ls.m_SendOnPop );
2005-07-07 08:14:01 +00:00
SendMessageToTopScreen( SM_GainFocus );
if( ls.m_bDeleteWhenDone )
delete ls.m_pScreen;
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
* 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.
*
* So, let's just zero the first update for every screen.
*/
2004-12-15 00:23:50 +00:00
ASSERT( !m_ScreenStack.empty() || m_sDelayedScreen != "" ); // Why play the game if there is nothing showing?
2004-12-23 09:48:16 +00:00
Screen* pScreen = m_ScreenStack.empty() ? NULL : GetTopScreen();
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. */
if( pScreen && m_bZeroNextUpdate )
{
LOG->Trace( "Zeroing this update. Was %f", fDeltaTime );
2004-12-23 09:48:16 +00:00
fDeltaTime = 0;
m_bZeroNextUpdate = false;
}
2004-12-23 09:48:16 +00:00
2005-07-12 03:12:18 +00:00
//
// Update screens.
//
{
// Only update the topmost screen on the stack.
if( pScreen )
pScreen->Update( fDeltaTime );
2004-12-23 09:48:16 +00:00
2005-07-12 03:12:18 +00:00
m_pSharedBGA->Update( fDeltaTime );
for( unsigned i=0; i<m_OverlayScreens.size(); i++ )
m_OverlayScreens[i]->Update( fDeltaTime );
}
//
// Handle messages after updating.
//
{
for( unsigned i=0; i<m_ScreenStack.size(); i++ )
m_ScreenStack[i].m_pScreen->ProcessMessages( fDeltaTime );
2005-07-12 03:12:18 +00:00
m_pSharedBGA->ProcessMessages( fDeltaTime );
for( unsigned i=0; i<m_OverlayScreens.size(); i++ )
m_OverlayScreens[i]->ProcessMessages( fDeltaTime );
}
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();
/* 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 )
{
LoadDelayedScreen();
}
if( !m_sDelayedConcurrentPrepare.empty() )
{
/* Don't call BackgroundPrepareScreen() from within another background load. */
ASSERT( !IsConcurrentlyLoading() );
2005-07-13 06:49:30 +00:00
CString sScreenName = m_sDelayedConcurrentPrepare;
m_sDelayedConcurrentPrepare = "";
ScreenMessage SM = m_OnDonePreparingScreen;
m_OnDonePreparingScreen = SM_None;
g_bIsConcurrentlyLoading = true;
StartConcurrentRendering();
2005-07-13 06:49:30 +00:00
PrepareScreen( sScreenName );
FinishConcurrentRendering();
g_bIsConcurrentlyLoading = false;
LOG->Trace( "Concurrent prepare of %s finished", m_sDelayedConcurrentPrepare.c_str() );
/* We're done. Send the message. The screen is allowed to start
* another concurrent prepare from this message. */
SendMessageToTopScreen( SM );
}
2002-05-20 08:59:37 +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. */
if( m_ScreenStack.size() && m_ScreenStack.back().m_pScreen->IsFirstUpdate() )
return;
2004-01-30 05:07:26 +00:00
if( !DISPLAY->BeginFrame() )
return;
m_pSharedBGA->Draw();
for( unsigned i=0; i<m_ScreenStack.size(); i++ ) // Draw all screens bottom to top
m_ScreenStack[i].m_pScreen->Draw();
for( unsigned i=0; i<m_OverlayScreens.size(); i++ )
m_OverlayScreens[i]->Draw();
DISPLAY->EndFrame();
2002-05-20 08:59:37 +00:00
}
void ScreenManager::Input( const DeviceInput& DeviceI, const InputEventType type, const GameInput &GameI, const MenuInput &MenuI, const StyleInput &StyleI )
{
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
// 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.
for( unsigned i = 0; i < m_OverlayScreens.size(); ++i )
{
Screen *pScreen = m_OverlayScreens[i];
if( pScreen->OverlayInput(DeviceI, type, GameI, MenuI, StyleI) )
return;
}
2005-02-28 05:33:24 +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;
if( !m_ScreenStack.empty() )
m_ScreenStack.back().m_pScreen->Input( DeviceI, type, GameI, MenuI, StyleI );
2002-05-20 08:59:37 +00:00
}
/* Just create a new screen; don't do any associated cleanup. */
Screen* ScreenManager::MakeNewScreenInternal( const CString &sScreenName )
{
RageTimer t;
LOG->Trace( "Loading screen name '%s'", sScreenName.c_str() );
CString sClassName = THEME->GetMetric(sScreenName,"Class");
map<CString,CreateScreenFn>::iterator iter = g_pmapRegistrees->find( sClassName );
ASSERT_M( iter != g_pmapRegistrees->end(), ssprintf("Screen '%s' has an invalid class '%s'",sScreenName.c_str(),sClassName.c_str()) )
CreateScreenFn pfn = iter->second;
Screen* ret = pfn( sScreenName );
LOG->Trace( "Loaded '%s' ('%s') in %f", sScreenName.c_str(), sClassName.c_str(), t.GetDeltaTime());
2005-03-20 06:41:56 +00:00
this->ZeroNextUpdate();
return ret;
}
Screen* ScreenManager::MakeNewScreen( const CString &sScreenName )
{
Screen* ret = MakeNewScreenInternal( sScreenName );
2002-12-21 07:54:24 +00:00
return ret;
}
void ScreenManager::PrepareScreen( const CString &sScreenName )
{
// If the screen is already prepared, stop.
2005-03-02 11:09:05 +00:00
for( int i = (int)m_vPreparedScreens.size()-1; i>=0; i-- )
{
2005-03-02 11:09:05 +00:00
Screen *&pScreen = m_vPreparedScreens[i];
2005-07-19 06:20:18 +00:00
if( pScreen->GetName() == sScreenName )
return;
}
/* Cleanup song data. This can free up a fair bit of memory, so do it before
* creating the new screen, to lower peak memory usage slightly. */
SONGMAN->Cleanup();
Screen* pNewScreen = MakeNewScreen(sScreenName);
m_vPreparedScreens.push_back( pNewScreen );
/* 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. */
CString sNewBGA;
if( pNewScreen->UsesBackground() )
sNewBGA = THEME->GetPathB(sScreenName,"background");
if( !sNewBGA.empty() && sNewBGA != m_pSharedBGA->GetName() )
{
Actor *pNewBGA = NULL;
FOREACH( Actor*, m_vPreparedBackgrounds, a )
{
2005-07-19 06:20:18 +00:00
if( (*a)->GetName() == sNewBGA )
{
pNewBGA = *a;
break;
}
}
// Create the new background before deleting the previous so that we keep
// any common textures loaded.
if( pNewBGA == NULL )
{
pNewBGA = ActorUtil::MakeActor( sNewBGA );
pNewBGA->SetName( sNewBGA );
m_vPreparedBackgrounds.push_back( pNewBGA );
}
}
}
bool ScreenManager::ConcurrentlyPrepareScreen( const CString &sScreenName, ScreenMessage SM )
{
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;
}
void ScreenManager::DeletePreparedScreens()
{
FOREACH( Screen*, m_vPreparedScreens, s )
SAFE_DELETE( *s );
m_vPreparedScreens.clear();
FOREACH( Actor*, m_vPreparedBackgrounds, a )
SAFE_DELETE( *a );
m_vPreparedBackgrounds.clear();
TEXTUREMAN->DeleteCachedTextures();
}
2004-03-25 22:13:51 +00:00
/* Remove all screens from the stack, sending a SM_LoseFocus message to the top.
* (There's no need to send them to any lower screens; they don't have focus anyway,
* and received the message when they actually lost it. */
void ScreenManager::ClearScreenStack()
{
if( m_ScreenStack.size() )
m_ScreenStack.back().m_pScreen->HandleScreenMessage( SM_LoseFocus );
2004-03-25 22:13:51 +00:00
for( unsigned i=0; i<m_ScreenStack.size(); i++ )
{
if( m_ScreenStack[i].m_bDeleteWhenDone )
SAFE_DELETE( m_ScreenStack[i].m_pScreen );
}
2004-03-25 22:13:51 +00:00
m_ScreenStack.clear();
/* Now that we've actually deleted a screen, it makes sense to clear out
* cached textures. */
TEXTUREMAN->DeleteCachedTextures();
2004-03-25 22:13:51 +00:00
}
2005-06-19 08:51:09 +00:00
/* Add a screen to m_ScreenStack. This is the only function that adds to m_ScreenStack. */
void ScreenManager::PushScreen( Screen *pNewScreen, bool bDeleteWhenDone, ScreenMessage SendOnPop )
{
if( m_ScreenStack.size() )
m_ScreenStack.back().m_pScreen->HandleScreenMessage( SM_LoseFocus );
LoadedScreen ls;
ls.m_pScreen = pNewScreen;
ls.m_bDeleteWhenDone = bDeleteWhenDone;
ls.m_SendOnPop = SendOnPop;
m_ScreenStack.push_back( ls );
2005-07-15 01:17:34 +00:00
pNewScreen->BeginScreen();
2003-11-25 21:57:00 +00:00
2005-05-21 07:15:41 +00:00
RefreshCreditsMessages();
2003-11-25 21:57:00 +00:00
PostMessageToTopScreen( SM_GainFocus, 0 );
}
void ScreenManager::SetNewScreen( const CString &sScreenName )
2002-05-20 08:59:37 +00:00
{
2005-01-07 04:50:08 +00:00
ASSERT( sScreenName != "" );
m_sDelayedScreen = sScreenName;
}
void ScreenManager::LoadDelayedScreen()
{
const bool bWasOnSystemMenu = !m_ScreenStack.empty() && m_ScreenStack.back().m_pScreen->GetScreenType() == system_menu;
2005-06-19 09:20:30 +00:00
/*
* We have a screen to display. Delete the current screens and load it.
* If DelayedScreenLoad is true, clear the old screen first; this lowers
* memory requirements, but results in redundant loads as we unload common
* data. If we don't unload the old screen here, it'll be deleted below.
*/
if( PREFSMAN->m_bDelayedScreenLoad )
ClearScreenStack();
CString sScreenName = m_sDelayedScreen;
m_sDelayedScreen = "";
// Load the screen, if it's not already prepared.
PrepareScreen( sScreenName );
//
// Find the prepped screen.
//
Screen* pNewScreen = NULL;
FOREACH( Screen*, m_vPreparedScreens, s )
{
2005-07-19 06:20:18 +00:00
if( (*s)->GetName() == sScreenName )
{
pNewScreen = *s;
m_vPreparedScreens.erase( s );
break;
}
}
ASSERT( pNewScreen != NULL );
if( m_sDelayedScreen != "" )
2003-03-09 00:55:49 +00:00
{
// While constructing this Screen, its constructor called
2003-03-09 00:55:49 +00:00
// SetNewScreen again! That SetNewScreen Command should
// override this older one.
// This is no longer allowed. Instead, figure out which screen
// you really wanted in the first place with Lua, and don't waste
// time constructing an extra screen.
FAIL_M( ssprintf("%s, %s", sScreenName.c_str(), m_sDelayedScreen.c_str()) );
}
// Find the prepared shared background (if any), and activate it.
CString sNewBGA;
2004-12-11 10:03:39 +00:00
if( pNewScreen->UsesBackground() )
2005-02-05 23:09:26 +00:00
sNewBGA = THEME->GetPathB(sScreenName,"background");
if( sNewBGA != m_pSharedBGA->GetName() )
{
Actor *pNewBGA = NULL;
if( sNewBGA.empty() )
pNewBGA = new Actor;
else
{
FOREACH( Actor*, m_vPreparedBackgrounds, a )
{
2005-07-19 06:20:18 +00:00
if( (*a)->GetName() == sNewBGA )
{
pNewBGA = *a;
m_vPreparedBackgrounds.erase( a );
break;
}
}
}
ASSERT( pNewBGA != NULL );
SAFE_DELETE( m_pSharedBGA );
m_pSharedBGA = pNewBGA;
2005-01-26 10:45:47 +00:00
m_pSharedBGA->PlayCommand( "On" );
}
2005-05-19 23:29:39 +00:00
bool bIsOnSystemMenu = pNewScreen->GetScreenType() == system_menu;
// If we're exiting a system menu, persist settings in case we don't exit normally
2005-05-19 23:29:39 +00:00
if( bWasOnSystemMenu && !bIsOnSystemMenu )
PREFSMAN->SaveGlobalPrefsToDisk();
2005-06-19 09:20:30 +00:00
if( !PREFSMAN->m_bDelayedScreenLoad )
ClearScreenStack();
2005-06-19 08:51:09 +00:00
2005-07-07 08:20:18 +00:00
TEXTUREMAN->DiagnosticOutput();
LOG->Trace("... PushScreen");
PushScreen( pNewScreen, true );
2002-05-20 08:59:37 +00:00
}
2005-07-18 19:30:36 +00:00
void ScreenManager::AddNewScreenToTop( const CString &sScreenName, ScreenMessage SendOnPop )
{
Screen* pNewScreen = MakeNewScreen(sScreenName);
2005-07-18 19:30:36 +00:00
PushScreen( pNewScreen, true, SendOnPop );
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
{
ASSERT( m_ScreenStack.size() > 0 );
2005-07-07 08:14:01 +00:00
m_PopTopScreen = SM;
2002-05-20 08:59:37 +00:00
}
2003-03-25 21:17:29 +00:00
void ScreenManager::PostMessageToTopScreen( ScreenMessage SM, float fDelay )
2002-05-20 08:59:37 +00:00
{
Screen* pTopScreen = GetTopScreen();
if( pTopScreen != NULL )
pTopScreen->PostScreenMessage( SM, fDelay );
2003-03-25 21:17:29 +00:00
}
void ScreenManager::SendMessageToTopScreen( ScreenMessage SM )
{
Screen* pTopScreen = GetTopScreen();
if( pTopScreen != NULL )
pTopScreen->HandleScreenMessage( SM );
2002-05-20 08:59:37 +00:00
}
2004-11-26 17:28:47 +00:00
void ScreenManager::SystemMessage( const CString &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
}
2004-11-26 17:28:47 +00:00
void ScreenManager::SystemMessageNoAnimate( const CString &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
void ScreenManager::RefreshCreditsMessages()
{
MESSAGEMAN->Broadcast( "RefreshCreditText" );
2004-03-13 10:26:51 +00:00
/* This is called when GAMESTATE->m_bSideIsJoined changes. */
CString 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()
{
if( !IsConcurrentlyLoading() )
{
LOG->Trace("ScreenManager::ZeroNextUpdate");
m_bZeroNextUpdate = true;
/* 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-07 04:48:07 +00:00
}
/* Always play these sounds, even if we're in a silent attract loop. */
void ScreenManager::PlayStartSound()
{
RageSoundParams p;
p.m_Volume = PREFSMAN->m_fSoundVolume;
m_soundStart.Play( &p );
}
void ScreenManager::PlayCoinSound()
{
RageSoundParams p;
p.m_Volume = PREFSMAN->m_fSoundVolume;
m_soundCoin.Play( &p );
}
void ScreenManager::PlayInvalidSound()
{
RageSoundParams p;
p.m_Volume = PREFSMAN->m_fSoundVolume;
m_soundInvalid.Play( &p );
}
void ScreenManager::PlayScreenshotSound()
{
RageSoundParams p;
p.m_Volume = PREFSMAN->m_fSoundVolume;
m_soundScreenshot.Play( &p );
}
void ScreenManager::PlaySharedBackgroundOffCommand()
{
m_pSharedBGA->PlayCommand("Off");
}
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 ); }
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; }
2005-02-28 07:06:29 +00:00
static void Register(lua_State *L)
{
ADD_METHOD( SetNewScreen )
2005-04-15 07:00:21 +00:00
ADD_METHOD( GetTopScreen )
2005-05-18 04:44:15 +00:00
ADD_METHOD( SystemMessage )
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");
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.
*/