Files
itgmania212121/stepmania/src/ScreenManager.cpp
T

677 lines
21 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-05-20 08:59:37 +00:00
/*
-----------------------------------------------------------------------------
Class: ScreenManager
2002-05-20 08:59:37 +00:00
Desc: See header.
2002-05-20 08:59:37 +00:00
Copyright (c) 2001-2003 by the person(s) listed below. All rights reserved.
Chris Danford
2003-02-19 23:53:19 +00:00
Tim Hentenaar
Glenn Maynard
2002-05-20 08:59:37 +00:00
-----------------------------------------------------------------------------
*/
#include "ScreenManager.h"
#include "IniFile.h"
#include "GameConstantsAndTypes.h"
2002-07-23 01:41:40 +00:00
#include "PrefsManager.h"
2002-05-20 08:59:37 +00:00
#include "RageLog.h"
2003-11-05 19:56:21 +00:00
#include "ActorUtil.h"
#include "GameState.h"
#include "RageException.h"
#include "RageTimer.h"
2003-12-18 08:53:38 +00:00
#include "RageSounds.h"
#include "ThemeManager.h"
2002-12-30 00:13:47 +00:00
#include "RageDisplay.h"
#include "Screen.h"
2003-03-03 10:03:02 +00:00
#include "SongManager.h"
#include "BitmapText.h"
#include "Quad.h"
2003-04-18 23:55:20 +00:00
#include "RageTextureManager.h"
2003-09-08 07:21:41 +00:00
#include "ProfileManager.h"
#include "ThemeManager.h"
2002-05-20 08:59:37 +00:00
ScreenManager* SCREENMAN = NULL; // global and accessable from anywhere in our program
2003-05-05 05:00:40 +00:00
#define STATS_X THEME->GetMetricF("ScreenSystemLayer","StatsX")
#define STATS_Y THEME->GetMetricF("ScreenSystemLayer","StatsY")
2003-11-08 15:49:48 +00:00
#define PLAYER_INFO_PRESS_START THEME->GetMetric ("ScreenSystemLayer","PlayerInfoPressStart")
#define PLAYER_INFO_INSERT_CARD THEME->GetMetric ("ScreenSystemLayer","PlayerInfoInsertCard")
#define PLAYER_INFO_NO_CARD THEME->GetMetric ("ScreenSystemLayer","PlayerInfoNoCard")
#define PLAYER_INFO_NOT_PRESENT THEME->GetMetric ("ScreenSystemLayer","PlayerInfoNotPresent")
#define PLAYER_INFO_INSERT_MORE THEME->GetMetric ("ScreenSystemLayer","PlayerInfoInsertMore")
#define CREDITS_TEXT_HOME THEME->GetMetric ("ScreenSystemLayer","CreditsTextHome")
#define CREDITS_TEXT_FREE_PLAY THEME->GetMetric ("ScreenSystemLayer","CreditsTextFreePlay")
#define CREDITS_TEXT_CREDITS THEME->GetMetric ("ScreenSystemLayer","CreditsTextCredits")
2003-12-29 02:36:55 +00:00
#define CREDITS_JOIN_ONLY THEME->GetMetric ("ScreenSystemLayer","CreditsJoinOnly")
2003-03-15 02:44:54 +00:00
const int NUM_SKIPS = 5;
/* This screen is drawn on top of everything else, and receives updates,
* but not input. */
class ScreenSystemLayer: public Screen
{
BitmapText m_textStats;
BitmapText m_textSystemMessage;
BitmapText m_textPlayerInfo[NUM_PLAYERS];
BitmapText m_textCredits;
BitmapText m_textSysTime;
2003-03-15 02:44:54 +00:00
BitmapText m_Skips[NUM_SKIPS];
int m_LastSkip;
Quad m_SkipBackground;
RageTimer SkipTimer;
void UpdateTimestampAndSkips();
public:
ScreenSystemLayer();
void SystemMessage( CString sMessage );
2003-11-20 11:55:45 +00:00
void SystemMessageNoAnimate( CString sMessage );
void RefreshCreditsMessages();
void Update( float fDeltaTime );
};
2003-04-12 06:16:12 +00:00
ScreenSystemLayer::ScreenSystemLayer() : Screen("ScreenSystemLayer")
2002-05-20 08:59:37 +00:00
{
m_textSystemMessage.LoadFromFont( THEME->GetPathToF("Common normal") );
2002-05-20 08:59:37 +00:00
m_textSystemMessage.SetHorizAlign( Actor::align_left );
m_textSystemMessage.SetVertAlign( Actor::align_top );
m_textSystemMessage.SetXY( 4.0f, 4.0f );
2003-03-09 00:55:49 +00:00
m_textSystemMessage.SetZoom( 0.8f );
m_textSystemMessage.SetShadowLength( 2 );
m_textSystemMessage.SetDiffuse( RageColor(1,1,1,0) );
this->AddChild(&m_textSystemMessage);
m_textStats.LoadFromFont( THEME->GetPathToF("Common normal") );
m_textStats.SetXY( STATS_X, STATS_Y );
m_textStats.SetHorizAlign( Actor::align_right );
m_textStats.SetVertAlign( Actor::align_top );
m_textStats.SetZoom( 0.5f );
m_textStats.SetShadowLength( 2 );
this->AddChild(&m_textStats);
m_textSysTime.LoadFromFont( THEME->GetPathToF("Common normal") );
m_textSysTime.SetXY( 4.0f, 40.0f );
m_textSysTime.SetHorizAlign( Actor::align_left );
m_textSysTime.SetVertAlign( Actor::align_top );
m_textSysTime.SetZoom( 0.5f );
m_textSysTime.SetDiffuse( RageColor(1,0,1,1) );
m_textSysTime.EnableShadow(false);
this->AddChild(&m_textSysTime);
for( int p=0; p<NUM_PLAYERS; p++ )
{
this->AddChild(&m_textPlayerInfo[p]);
}
2003-03-15 02:44:54 +00:00
this->AddChild(&m_textCredits);
2003-03-15 02:44:54 +00:00
/* "Was that a skip?" This displays a message when an update takes
* abnormally long, to quantify skips more precisely, verify them
* when they're subtle, and show the time it happened, so you can pinpoint
* the time in the log. Put a dim quad behind it to make it easier to
* read. */
m_LastSkip = 0;
const float SKIP_LEFT = 320.0f, SKIP_TOP = 60.0f,
SKIP_WIDTH = 160.0f, SKIP_Y_DIST = 16.0f;
m_SkipBackground.StretchTo(RectF(SKIP_LEFT-8, SKIP_TOP-8,
SKIP_LEFT+SKIP_WIDTH, SKIP_TOP+SKIP_Y_DIST*NUM_SKIPS));
m_SkipBackground.SetDiffuse( RageColor(0,0,0,0) );
this->AddChild(&m_SkipBackground);
for( int i=0; i<NUM_SKIPS; i++ )
{
/* This is somewhat big. Let's put it on the right side, where it'll
* obscure the 2P side during gameplay; there's nowhere to put it that
* doesn't obscure something, and it's just a diagnostic. */
m_Skips[i].LoadFromFont( THEME->GetPathToF("Common normal") );
2003-03-15 02:44:54 +00:00
m_Skips[i].SetXY( SKIP_LEFT, SKIP_TOP + SKIP_Y_DIST*i );
m_Skips[i].SetHorizAlign( Actor::align_left );
m_Skips[i].SetVertAlign( Actor::align_top );
m_Skips[i].SetZoom( 0.5f );
m_Skips[i].SetDiffuse( RageColor(1,1,1,0) );
m_Skips[i].EnableShadow(false);
this->AddChild(&m_Skips[i]);
}
}
void ScreenSystemLayer::SystemMessage( CString sMessage )
{
2003-11-20 11:55:45 +00:00
m_textSystemMessage.FinishTweening();
m_textSystemMessage.SetText( sMessage );
2003-11-20 11:55:45 +00:00
m_textSystemMessage.SetDiffuseAlpha( 1 );
m_textSystemMessage.SetX( -640 );
m_textSystemMessage.BeginTweening( 0.5f );
m_textSystemMessage.SetX( 4 );
m_textSystemMessage.BeginTweening( 5 );
m_textSystemMessage.BeginTweening( 0.5f );
m_textSystemMessage.SetDiffuse( RageColor(1,1,1,0) );
}
2003-11-20 11:55:45 +00:00
void ScreenSystemLayer::SystemMessageNoAnimate( CString sMessage )
{
m_textSystemMessage.FinishTweening();
m_textSystemMessage.SetText( sMessage );
m_textSystemMessage.SetX( 4 );
m_textSystemMessage.SetDiffuseAlpha( 1 );
m_textSystemMessage.BeginTweening( 5 );
m_textSystemMessage.BeginTweening( 0.5f );
m_textSystemMessage.SetDiffuse( RageColor(1,1,1,0) );
}
void ScreenSystemLayer::RefreshCreditsMessages()
{
m_textCredits.LoadFromFont( THEME->GetPathToF("ScreenManager credits") );
m_textCredits.SetName( "Credits" );
2003-11-27 05:34:22 +00:00
SET_XY_AND_ON_COMMAND( &m_textCredits );
CString sCredits;
switch( PREFSMAN->m_iCoinMode )
{
case COIN_HOME:
2003-12-26 01:48:46 +00:00
sCredits = CREDITS_TEXT_HOME;
break;
case COIN_PAY:
{
int Coins = GAMESTATE->m_iCoins % PREFSMAN->m_iCoinsPerCredit;
sCredits = ssprintf("%s %d", CREDITS_TEXT_CREDITS.c_str(), GAMESTATE->m_iCoins / PREFSMAN->m_iCoinsPerCredit);
if (Coins)
sCredits += ssprintf(" %d/%d", Coins, PREFSMAN->m_iCoinsPerCredit );
}
break;
case COIN_FREE:
2003-11-08 15:49:48 +00:00
sCredits = CREDITS_TEXT_FREE_PLAY;
break;
default:
ASSERT(0);
}
2003-12-29 02:36:55 +00:00
if( CREDITS_JOIN_ONLY && !GAMESTATE->PlayersCanJoin() )
sCredits = "";
m_textCredits.SetText( sCredits );
// update joined
for( int p=0; p<NUM_PLAYERS; p++ )
{
m_textPlayerInfo[p].LoadFromFont( THEME->GetPathToF("ScreenManager credits") );
m_textPlayerInfo[p].SetName( ssprintf("PlayerInfoP%d",p+1 ) );
2003-11-27 05:34:22 +00:00
SET_XY_AND_ON_COMMAND( &m_textPlayerInfo[p] );
CString sPlayerInfo;
if( GAMESTATE->m_bSideIsJoined[p] )
{
2003-11-08 15:49:48 +00:00
Profile* pProfile = PROFILEMAN->GetProfile( (PlayerNumber)p );
if( pProfile )
{
if( !pProfile->m_sLastUsedHighScoreName.empty() )
sPlayerInfo = pProfile->m_sLastUsedHighScoreName;
else
sPlayerInfo = pProfile->m_sName;
}
2003-11-09 01:09:35 +00:00
else if( GAMESTATE->m_bIsOnSystemMenu ) // no mem card
2003-11-08 15:49:48 +00:00
sPlayerInfo = "";
else
2003-11-09 01:09:35 +00:00
{
if( GAMESTATE->PlayersCanJoin() )
2003-11-09 01:09:35 +00:00
sPlayerInfo = PLAYER_INFO_INSERT_CARD;
else
sPlayerInfo = PLAYER_INFO_NO_CARD;
}
}
else if( GAMESTATE->PlayersCanJoin() )
{
2003-11-09 01:09:35 +00:00
if( PREFSMAN->m_iCoinMode==COIN_PAY &&
PREFSMAN->m_Premium!=PrefsManager::JOINT_PREMIUM &&
GAMESTATE->m_iCoins<PREFSMAN->m_iCoinsPerCredit )
2003-11-08 15:49:48 +00:00
sPlayerInfo = PLAYER_INFO_INSERT_MORE;
else
2003-11-08 15:49:48 +00:00
sPlayerInfo = PLAYER_INFO_PRESS_START;
}
else
{
2003-11-08 15:49:48 +00:00
sPlayerInfo = PLAYER_INFO_NOT_PRESENT;
}
2003-11-01 19:36:52 +00:00
m_textPlayerInfo[p].SetText( sPlayerInfo );
}
}
2003-03-15 02:44:54 +00:00
void ScreenSystemLayer::UpdateTimestampAndSkips()
{
if(!PREFSMAN->m_bTimestamping)
{
/* Hide: */
m_textSysTime.SetDiffuse( RageColor(1,1,1,0) );
m_SkipBackground.SetDiffuse( RageColor(0,0,0,0) );
return;
}
m_SkipBackground.SetDiffuse(RageColor(0,0,0,0.4f));
CString time(SecondsToTime(RageTimer::GetTimeSinceStart()));
/* Use our own timer, so we ignore `/tab. */
const float UpdateTime = SkipTimer.GetDeltaTime();
/* FPS is 0 for a little while after we load a screen; don't report
* during this time. Do clear the timer, though, so we don't report
* a big "skip" after this period passes. */
if(DISPLAY->GetFPS())
{
/* We want to display skips. We expect to get updates of about 1.0/FPS ms. */
const float ExpectedUpdate = 1.0f / DISPLAY->GetFPS();
/* These are thresholds for severity of skips. The smallest
* is slightly above expected, to tolerate normal jitter. */
const float Thresholds[] = {
ExpectedUpdate * 2.0f, ExpectedUpdate * 4.0f, 0.1f, -1
};
int skip = 0;
while(Thresholds[skip] != -1 && UpdateTime > Thresholds[skip])
skip++;
if(skip)
{
static const RageColor colors[] = {
RageColor(0,0,0,0), /* unused */
RageColor(0.2f,0.2f,1,1), /* light blue */
RageColor(1,1,0,1), /* yellow */
RageColor(1,0.2f,0.2f,1) /* light red */
};
m_Skips[m_LastSkip].SetText(ssprintf("%s: %.0fms (%.0f)",
2003-04-25 00:01:35 +00:00
time.c_str(), 1000*UpdateTime, UpdateTime/ExpectedUpdate));
2003-03-15 02:44:54 +00:00
m_Skips[m_LastSkip].StopTweening();
m_Skips[m_LastSkip].SetDiffuse(RageColor(1,1,1,1));
m_Skips[m_LastSkip].BeginTweening(0.2f);
m_Skips[m_LastSkip].SetDiffuse(colors[skip]);
m_Skips[m_LastSkip].BeginTweening(3.0f);
m_Skips[m_LastSkip].BeginTweening(0.2f);
m_Skips[m_LastSkip].SetDiffuse(RageColor(1,1,1,0));
m_LastSkip++;
m_LastSkip %= NUM_SKIPS;
}
}
m_textSysTime.SetText( time );
m_textSysTime.SetDiffuse( RageColor(1,0,1,1) );
}
void ScreenSystemLayer::Update( float fDeltaTime )
{
Screen::Update(fDeltaTime);
if( PREFSMAN && PREFSMAN->m_bShowStats )
{
m_textStats.SetDiffuse( RageColor(1,1,1,0.7f) );
/* If FPS == 0, we don't have stats yet. */
if(DISPLAY->GetFPS())
m_textStats.SetText( ssprintf(
"%i FPS\n%i av FPS\n%i VPF",
DISPLAY->GetFPS(), DISPLAY->GetCumFPS(),
DISPLAY->GetVPF()) );
else
m_textStats.SetText( "-- FPS\n-- av FPS\n-- VPF" );
} else
m_textStats.SetDiffuse( RageColor(1,1,1,0) ); /* hide */
2003-03-15 02:44:54 +00:00
UpdateTimestampAndSkips();
}
ScreenManager::ScreenManager()
{
m_SystemLayer = new ScreenSystemLayer;
m_ScreenBuffered = NULL;
2003-11-12 08:13:02 +00:00
m_MessageSendOnPop = SM_None;
2002-05-20 08:59:37 +00:00
}
ScreenManager::~ScreenManager()
{
LOG->Trace( "ScreenManager::~ScreenManager()" );
2002-05-20 08:59:37 +00:00
2002-08-23 06:50:48 +00:00
EmptyDeleteQueue();
2003-03-09 00:55:49 +00:00
// delete current Screens
for( unsigned i=0; i<m_ScreenStack.size(); i++ )
delete m_ScreenStack[i];
delete m_ScreenBuffered;
delete m_SystemLayer;
2002-05-20 08:59:37 +00:00
}
2002-08-23 06:50:48 +00:00
void ScreenManager::EmptyDeleteQueue()
{
2003-04-24 19:42:06 +00:00
if(!m_ScreensToDelete.size())
return;
2002-08-23 06:50:48 +00:00
// delete all ScreensToDelete
for( unsigned i=0; i<m_ScreensToDelete.size(); i++ )
2002-08-23 06:50:48 +00:00
SAFE_DELETE( m_ScreensToDelete[i] );
2002-05-20 08:59:37 +00:00
2002-10-24 20:15:24 +00:00
m_ScreensToDelete.clear();
2003-04-24 19:42:06 +00:00
/* Now that we've actually deleted a screen, it makes sense to clear out
* cached textures. */
TEXTUREMAN->DeleteCachedTextures();
2003-04-24 19:51:27 +00:00
TEXTUREMAN->DiagnosticOutput();
2002-08-23 06:50:48 +00:00
}
2002-05-20 08:59:37 +00:00
/* XXX: Big hack:
* All screens must receive at least one update before they draw.
* However, no screen should ever receive an update until it's ready
* to be drawn; otherwise Gameplay will queue music to start even though
* Stage might still be delaying for a while, throwing off timing.
*
* This is tricky with prepped screens. We can't do an Update(0) in
* LoadPreppedScreen, since that'll cause the delete queue to be cleared
* while the screen is on the stack, and possibly other odd things.
* So, update before we draw, which is also a hack (rendering shouldn't
* change state!). Only do this when we have to, so we don't double
* the number of updates. */
static bool g_TopNeedsNeedsNullUpdate = false;
static bool g_SkipRendering = false;
2002-05-20 08:59:37 +00:00
void ScreenManager::Update( float fDeltaTime )
{
// Only update the topmost screen on the stack.
/* 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.
*
* XXX: If a new Screen is set during this Update, that new screen is Drawn
2003-09-15 05:27:34 +00:00
* before its first Update.
*/
ASSERT( !m_ScreenStack.empty() || m_DelayedScreen != "" ); // Why play the game if there is nothing showing?
if( !m_ScreenStack.empty() )
{
Screen* pScreen = m_ScreenStack[m_ScreenStack.size()-1];
if( pScreen->IsFirstUpdate() )
pScreen->Update( 0 );
else
pScreen->Update( fDeltaTime );
}
m_SystemLayer->Update( fDeltaTime );
EmptyDeleteQueue();
if(m_DelayedScreen.size() != 0)
{
/* We have a screen to display. Delete the current screens and load it. */
m_ScreensToDelete.insert(m_ScreensToDelete.end(), m_ScreenStack.begin(), m_ScreenStack.end());
m_ScreenStack.clear();
EmptyDeleteQueue();
/* This is the purpose of delayed screen loads: clear out the texture cache
* now, while there's (mostly) nothing loaded. */
TEXTUREMAN->DeleteCachedTextures();
TEXTUREMAN->DiagnosticOutput();
LoadDelayedScreen();
/* Ack. We can't Update(0), since we want to skip the *next* update
* (which is the one that will have all this load time in it). We
* can't draw it until we've updated it. Let's simply not render
* the next frame, so we'll come around quickly and handle it correctly. */
g_SkipRendering = true;
}
2002-05-20 08:59:37 +00:00
}
void ScreenManager::Draw()
{
DISPLAY->BeginFrame();
if(g_SkipRendering)
{
/* Leave the frame there (if any). */
g_SkipRendering = false;
return;
}
if(g_TopNeedsNeedsNullUpdate)
{
g_TopNeedsNeedsNullUpdate = false;
m_ScreenStack.back()->Update( 0 );
}
if( !m_ScreenStack.empty() && !m_ScreenStack.back()->IsTransparent() ) // top screen isn't transparent
m_ScreenStack.back()->Draw();
else
for( unsigned i=0; i<m_ScreenStack.size(); i++ ) // Draw all screens bottom to top
m_ScreenStack[i]->Draw();
m_SystemLayer->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
// pass input only to topmost state
if( !m_ScreenStack.empty() )
m_ScreenStack.back()->Input( DeviceI, type, GameI, MenuI, StyleI );
2002-05-20 08:59:37 +00:00
}
Screen* ScreenManager::MakeNewScreen( CString sClassName )
{
2003-12-18 08:53:38 +00:00
/* By default, RageSounds handles the song timer. When we change screens, reset this;
* screens turn this off in their ctor if they handle timers themselves (gameplay, edit). */
SOUND->HandleSongTimer( true );
2003-12-21 02:54:23 +00:00
/* 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();
2003-03-03 10:03:02 +00:00
Screen *ret = Screen::Create( sClassName );
2002-12-21 07:54:24 +00:00
2002-12-29 23:37:41 +00:00
/* Loading probably took a little while. Let's reset stats. This prevents us
2002-12-21 07:54:24 +00:00
* 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();
return ret;
}
void ScreenManager::PrepNewScreen( CString sClassName )
{
2002-09-04 23:40:01 +00:00
ASSERT(m_ScreenBuffered == NULL);
m_ScreenBuffered = MakeNewScreen(sClassName);
}
void ScreenManager::LoadPreppedScreen()
{
ASSERT( m_ScreenBuffered != NULL);
SetNewScreen( m_ScreenBuffered );
// Need to update the new screen once, or else it will be
// drawn before ever being Update()d.
g_TopNeedsNeedsNullUpdate = true;
2002-09-04 23:40:01 +00:00
m_ScreenBuffered = NULL;
}
void ScreenManager::DeletePreppedScreen()
{
SAFE_DELETE( m_ScreenBuffered );
TEXTUREMAN->DeleteCachedTextures();
}
void ScreenManager::SetNewScreen( Screen *pNewScreen )
{
RefreshCreditsMessages();
THEME->ReloadMetricsIfNecessary();
2002-10-24 19:55:09 +00:00
// move current screen(s) to ScreenToDelete
m_ScreensToDelete.insert(m_ScreensToDelete.end(), m_ScreenStack.begin(), m_ScreenStack.end());
2002-10-24 20:15:24 +00:00
m_ScreenStack.clear();
2002-10-31 04:23:39 +00:00
m_ScreenStack.push_back( pNewScreen );
2003-11-25 21:57:00 +00:00
PostMessageToTopScreen( SM_GainFocus, 0 );
}
void ScreenManager::SetNewScreen( CString sClassName )
2002-05-20 08:59:37 +00:00
{
m_DelayedScreen = sClassName;
/* If we're not delaying screen loads, load it now. Otherwise, we'll load
* it on the next iteration. Only delay if we already have a screen
* loaded; otherwise, there's no reason to delay. */
if(!PREFSMAN->m_bDelayedScreenLoad) // || m_ScreenStack.empty() )
LoadDelayedScreen();
}
void ScreenManager::LoadDelayedScreen()
{
retry:
CString sClassName = m_DelayedScreen;
m_DelayedScreen = "";
/* If we prepped a screen but didn't use it, nuke it. */
SAFE_DELETE( m_ScreenBuffered );
RageTimer t;
2003-03-09 00:55:49 +00:00
Screen* pOldTopScreen = m_ScreenStack.empty() ? NULL : m_ScreenStack.back();
// It makes sense that ScreenManager should allocate memory for a new screen since it
// deletes it later on. This also convention will reduce includes because screens won't
// have to include each other's headers of other screens.
2003-10-19 23:19:02 +00:00
LOG->Trace( "Loading screen %s", sClassName.c_str() );
Screen* pNewScreen = MakeNewScreen(sClassName);
2003-04-25 00:01:35 +00:00
LOG->Trace( "Loaded %s in %f", sClassName.c_str(), t.GetDeltaTime());
2003-03-09 00:55:49 +00:00
if( pOldTopScreen!=NULL && m_ScreenStack.back()!=pOldTopScreen )
{
// While constructing this Screen, it's constructor called
// SetNewScreen again! That SetNewScreen Command should
// override this older one.
SAFE_DELETE( pNewScreen );
return;
}
if( PREFSMAN->m_bDelayedScreenLoad && m_DelayedScreen != "" )
{
/* Same deal: the ctor called SetNewScreen again. Delete the screen
* we just made, but don't delay again. */
SAFE_DELETE( pNewScreen );
goto retry;
}
SetNewScreen( pNewScreen );
/* If this is a system menu, don't let the operator key touch it!
However, if you add an options screen, please include it here -- Miryokuteki */
if( sClassName == "ScreenOptionsMenu" ||
sClassName == "ScreenMachineOptions" ||
sClassName == "ScreenInputOptions" ||
sClassName == "ScreenGraphicOptions" ||
sClassName == "ScreenGameplayOptions" ||
sClassName == "ScreenMapControllers" ||
sClassName == "ScreenAppearanceOptions" ||
sClassName == "ScreenEdit" ||
sClassName == "ScreenEditMenu" ||
sClassName == "ScreenSoundOptions" )
2003-03-09 00:55:49 +00:00
GAMESTATE->m_bIsOnSystemMenu = true;
else
GAMESTATE->m_bIsOnSystemMenu = false;
2002-05-20 08:59:37 +00:00
}
2003-11-12 08:13:02 +00:00
void ScreenManager::AddNewScreenToTop( CString sClassName, ScreenMessage messageSendOnPop )
2003-02-19 05:46:09 +00:00
{
Screen* pNewScreen = MakeNewScreen(sClassName);
m_ScreenStack.push_back( pNewScreen );
2003-11-12 08:13:02 +00:00
m_MessageSendOnPop = messageSendOnPop;
2003-02-19 05:46:09 +00:00
}
2003-03-03 10:03:02 +00:00
#include "ScreenPrompt.h"
#include "ScreenTextEntry.h"
#include "ScreenMiniMenu.h"
void ScreenManager::Prompt( ScreenMessage SM_SendWhenDone, CString sText, bool bYesNo, bool bDefaultAnswer, void(*OnYes)(void*), void(*OnNo)(void*), void* pCallbackData )
2002-05-20 08:59:37 +00:00
{
// add the new state onto the back of the array
m_ScreenStack.push_back( new ScreenPrompt(SM_SendWhenDone, sText, bYesNo, bDefaultAnswer, OnYes, OnNo, pCallbackData) );
}
2002-05-20 08:59:37 +00:00
void ScreenManager::TextEntry( ScreenMessage SM_SendWhenDone, CString sQuestion, CString sInitialAnswer, void(*OnOK)(CString sAnswer), void(*OnCanel)() )
{
2002-05-20 08:59:37 +00:00
// add the new state onto the back of the array
2003-09-27 22:30:51 +00:00
m_ScreenStack.push_back( new ScreenTextEntry("ScreenTextEntry", SM_SendWhenDone, sQuestion, sInitialAnswer, OnOK, OnCanel) );
2002-05-20 08:59:37 +00:00
}
void ScreenManager::MiniMenu( Menu* pDef, ScreenMessage SM_SendOnOK, ScreenMessage SM_SendOnCancel )
2003-02-18 23:15:38 +00:00
{
// add the new state onto the back of the array
m_ScreenStack.push_back( new ScreenMiniMenu(pDef, SM_SendOnOK, SM_SendOnCancel) );
}
2002-07-27 19:29:51 +00:00
void ScreenManager::PopTopScreen( ScreenMessage SM )
2002-05-20 08:59:37 +00:00
{
Screen* pScreenToPop = m_ScreenStack.back(); // top menu
2003-11-25 21:57:00 +00:00
pScreenToPop->HandleScreenMessage( SM_LoseFocus );
2002-10-31 04:11:08 +00:00
m_ScreenStack.erase(m_ScreenStack.end()-1, m_ScreenStack.end());
2002-10-31 04:23:39 +00:00
m_ScreensToDelete.push_back( pScreenToPop );
2003-11-12 08:13:02 +00:00
/* Post to the new top. This must be done now; otherwise, we'll have a single
* frame between popping and these messages, which can result in a frame where eg.
* input is accepted where it shouldn't be. */
SendMessageToTopScreen( SM );
SendMessageToTopScreen( SM_GainFocus );
SendMessageToTopScreen( m_MessageSendOnPop );
2003-11-12 08:13:02 +00:00
m_MessageSendOnPop = SM_None;
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 = m_ScreenStack.back();
2003-03-25 21:17:29 +00:00
pTopScreen->PostScreenMessage( SM, fDelay );
}
void ScreenManager::SendMessageToTopScreen( ScreenMessage SM )
{
Screen* pTopScreen = m_ScreenStack.back();
pTopScreen->HandleScreenMessage( SM );
2002-05-20 08:59:37 +00:00
}
void ScreenManager::SystemMessage( CString sMessage )
{
2003-09-15 20:44:25 +00:00
LOG->Trace( "%s", sMessage.c_str() );
m_SystemLayer->SystemMessage( sMessage );
2002-05-20 08:59:37 +00:00
}
2003-11-20 11:55:45 +00:00
void ScreenManager::SystemMessageNoAnimate( CString sMessage )
{
LOG->Trace( "%s", sMessage.c_str() );
m_SystemLayer->SystemMessageNoAnimate( sMessage );
}
void ScreenManager::RefreshCreditsMessages()
{
m_SystemLayer->RefreshCreditsMessages();
2003-02-12 07:52:20 +00:00
}