diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 0a05fc5040..0040deed35 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -5,9 +5,13 @@ from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. ________________________________________________________________________________ ================================================================================ -StepMania 5.0 ???? ?? | 20120??? +StepMania 5.0 alpha 3 | 201207xx -------------------------------------------------------------------------------- +2012/07/10 +---------- +* [ScreenSelectMusic] Fix an issue where Control+ wouldn't work. [AJ] + 2012/07/09 ---------- * [InputMapper] Use "|" for separating entries instead of ",", since some @@ -80,7 +84,7 @@ StepMania 5.0 ???? ?? | 20120??? 2012/05/19 ---------- -* [Actor] Made the xy command a source-code level command. +* [Actor] Made the xy command a source-code level command. [AJ] 2012/05/17 ---------- diff --git a/src/ScreenSelectMusic.cpp b/src/ScreenSelectMusic.cpp index ca13e7acfc..d3ca292f7b 100644 --- a/src/ScreenSelectMusic.cpp +++ b/src/ScreenSelectMusic.cpp @@ -1,1956 +1,1959 @@ -#include "global.h" -#include "ScreenSelectMusic.h" -#include "ScreenManager.h" -#include "PrefsManager.h" -#include "SongManager.h" -#include "GameManager.h" -#include "GameSoundManager.h" -#include "GameConstantsAndTypes.h" -#include "RageLog.h" -#include "InputMapper.h" -#include "GameState.h" -#include "CodeDetector.h" -#include "ThemeManager.h" -#include "Steps.h" -#include "ActorUtil.h" -#include "RageTextureManager.h" -#include "Course.h" -#include "ProfileManager.h" -#include "Profile.h" -#include "MenuTimer.h" -#include "StatsManager.h" -#include "StepsUtil.h" -#include "Foreach.h" -#include "Style.h" -#include "PlayerState.h" -#include "CommonMetrics.h" -#include "BannerCache.h" -//#include "BackgroundCache.h" -#include "Song.h" -#include "InputEventPlus.h" -#include "RageInput.h" -#include "OptionsList.h" - -static const char *SelectionStateNames[] = { - "SelectingSong", - "SelectingSteps", - "Finalized" -}; -XToString( SelectionState ); - -/** @brief The maximum number of digits for the ScoreDisplay. */ -const int NUM_SCORE_DIGITS = 9; - -#define SHOW_OPTIONS_MESSAGE_SECONDS THEME->GetMetricF( m_sName, "ShowOptionsMessageSeconds" ) - -AutoScreenMessage( SM_AllowOptionsMenuRepeat ); -AutoScreenMessage( SM_SongChanged ); -AutoScreenMessage( SM_SortOrderChanging ); -AutoScreenMessage( SM_SortOrderChanged ); -AutoScreenMessage( SM_BackFromPlayerOptions ); - -static RString g_sCDTitlePath; -static bool g_bWantFallbackCdTitle; -static bool g_bCDTitleWaiting = false; -static RString g_sBannerPath; -static bool g_bBannerWaiting = false; -static bool g_bSampleMusicWaiting = false; -static RageTimer g_StartedLoadingAt(RageZeroTimer); -static RageTimer g_ScreenStartedLoadingAt(RageZeroTimer); -RageTimer g_CanOpenOptionsList(RageZeroTimer); - -REGISTER_SCREEN_CLASS( ScreenSelectMusic ); -void ScreenSelectMusic::Init() -{ - g_ScreenStartedLoadingAt.Touch(); - if( PREFSMAN->m_sTestInitialScreen.Get() == m_sName ) - { - GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); - GAMESTATE->SetCurrentStyle( GAMEMAN->GameAndStringToStyle(GAMEMAN->GetDefaultGame(),"versus") ); - GAMESTATE->JoinPlayer( PLAYER_1 ); - GAMESTATE->SetMasterPlayerNumber(PLAYER_1); - } - - IDLE_COMMENT_SECONDS.Load( m_sName, "IdleCommentSeconds" ); - SAMPLE_MUSIC_DELAY_INIT.Load( m_sName, "SampleMusicDelayInit" ); - SAMPLE_MUSIC_DELAY.Load( m_sName, "SampleMusicDelay" ); - SAMPLE_MUSIC_LOOPS.Load( m_sName, "SampleMusicLoops" ); - SAMPLE_MUSIC_PREVIEW_MODE.Load( m_sName, "SampleMusicPreviewMode" ); - SAMPLE_MUSIC_FALLBACK_FADE_IN_SECONDS.Load( m_sName, "SampleMusicFallbackFadeInSeconds" ); - DO_ROULETTE_ON_MENU_TIMER.Load( m_sName, "DoRouletteOnMenuTimer" ); - ROULETTE_TIMER_SECONDS.Load( m_sName, "RouletteTimerSeconds" ); - ALIGN_MUSIC_BEATS.Load( m_sName, "AlignMusicBeat" ); - CODES.Load( m_sName, "Codes" ); - MUSIC_WHEEL_TYPE.Load( m_sName, "MusicWheelType" ); - SELECT_MENU_AVAILABLE.Load( m_sName, "SelectMenuAvailable" ); - MODE_MENU_AVAILABLE.Load( m_sName, "ModeMenuAvailable" ); - USE_OPTIONS_LIST.Load( m_sName, "UseOptionsList" ); - USE_PLAYER_SELECT_MENU.Load( m_sName, "UsePlayerSelectMenu" ); - SELECT_MENU_NAME.Load( m_sName, "SelectMenuScreenName" ); - OPTIONS_LIST_TIMEOUT.Load( m_sName, "OptionsListTimeout" ); - SELECT_MENU_CHANGES_DIFFICULTY.Load( m_sName, "SelectMenuChangesDifficulty" ); - TWO_PART_SELECTION.Load( m_sName, "TwoPartSelection" ); - TWO_PART_CONFIRMS_ONLY.Load( m_sName, "TwoPartConfirmsOnly" ); - TWO_PART_TIMER_SECONDS.Load( m_sName, "TwoPartTimerSeconds" ); - WRAP_CHANGE_STEPS.Load( m_sName, "WrapChangeSteps" ); - NULL_SCORE_STRING.Load( m_sName, "NullScoreString" ); - PLAY_SOUND_ON_ENTERING_OPTIONS_MENU.Load( m_sName, "PlaySoundOnEnteringOptionsMenu" ); - // To allow changing steps with gamebuttons -DaisuMaster - CHANGE_STEPS_WITH_GAME_BUTTONS.Load( m_sName, "ChangeStepsWithGameButtons" ); - CHANGE_GROUPS_WITH_GAME_BUTTONS.Load( m_sName, "ChangeGroupsWithGameButtons" ); - - m_GameButtonPreviousSong = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"PreviousSongButton") ); - m_GameButtonNextSong = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"NextSongButton") ); - - // Ask for those only if changing steps with gamebuttons is allowed -DaisuMaster - if( CHANGE_STEPS_WITH_GAME_BUTTONS ) - { - m_GameButtonPreviousDifficulty = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"PreviousDifficultyButton") ); - m_GameButtonNextDifficulty = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"NextDifficultyButton") ); - } - // same here but for groups -DaisuMaster - if( CHANGE_GROUPS_WITH_GAME_BUTTONS ) - { - m_GameButtonPreviousGroup = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"PreviousGroupButton") ); - m_GameButtonNextGroup = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"NextGroupButton") ); - } - - FOREACH_ENUM( PlayerNumber, p ) - { - m_bSelectIsDown[p] = false; // used by UpdateSelectButton - m_bAcceptSelectRelease[p] = false; - } - - ScreenWithMenuElements::Init(); - - this->SubscribeToMessage( Message_PlayerJoined ); - - // Cache these values - // Marking for change -- Midiman (why? -aj) - m_sSectionMusicPath = THEME->GetPathS(m_sName,"section music"); - m_sSortMusicPath = THEME->GetPathS(m_sName,"sort music"); - m_sRouletteMusicPath = THEME->GetPathS(m_sName,"roulette music"); - m_sRandomMusicPath = THEME->GetPathS(m_sName,"random music"); - m_sCourseMusicPath = THEME->GetPathS(m_sName,"course music"); - m_sLoopMusicPath = THEME->GetPathS(m_sName,"loop music"); - m_sFallbackCDTitlePath = THEME->GetPathG(m_sName,"fallback cdtitle"); - - m_TexturePreload.Load( m_sFallbackCDTitlePath ); - - // load banners - if( PREFSMAN->m_BannerCache != BNCACHE_OFF ) - { - m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","all music")) ); - m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Common","fallback banner")) ); - m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","roulette")) ); - m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","random")) ); - m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","mode")) ); - } - // load backgrounds - /* - if( PREFSMAN->m_BackgroundCache != BGCACHE_OFF ) - { - m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","AllMusic")) ); - m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("Common","fallback banner")) ); - m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","roulette")) ); - m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","random")) ); - m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","Mode")) ); - m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","group fallback")) ); - m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","course fallback")) ); - } - */ - - // Load low-res banners and backgrounds if needed. - BANNERCACHE->Demand(); - //BACKGROUNDCACHE->Demand(); - - m_MusicWheel.SetName( "MusicWheel" ); - m_MusicWheel.Load( MUSIC_WHEEL_TYPE ); - LOAD_ALL_COMMANDS_AND_SET_XY( m_MusicWheel ); - this->AddChild( &m_MusicWheel ); - - if( USE_OPTIONS_LIST ) - { - FOREACH_PlayerNumber(p) - { - m_OptionsList[p].SetName( "OptionsList" + PlayerNumberToString(p) ); - m_OptionsList[p].Load( "OptionsList", p ); - m_OptionsList[p].SetDrawOrder( 100 ); - ActorUtil::LoadAllCommands( m_OptionsList[p], m_sName ); - this->AddChild( &m_OptionsList[p] ); - } - m_OptionsList[PLAYER_1].Link( &m_OptionsList[PLAYER_2] ); - m_OptionsList[PLAYER_2].Link( &m_OptionsList[PLAYER_1] ); - } - - // this is loaded SetSong and TweenToSong - m_Banner.SetName( "Banner" ); - LOAD_ALL_COMMANDS_AND_SET_XY( m_Banner ); - this->AddChild( &m_Banner ); - - m_sprCDTitleFront.SetName( "CDTitle" ); - m_sprCDTitleFront.Load( THEME->GetPathG(m_sName,"fallback cdtitle") ); - LOAD_ALL_COMMANDS_AND_SET_XY( m_sprCDTitleFront ); - COMMAND( m_sprCDTitleFront, "Front" ); - this->AddChild( &m_sprCDTitleFront ); - - m_sprCDTitleBack.SetName( "CDTitle" ); - m_sprCDTitleBack.Load( THEME->GetPathG(m_sName,"fallback cdtitle") ); - LOAD_ALL_COMMANDS_AND_SET_XY( m_sprCDTitleBack ); - COMMAND( m_sprCDTitleBack, "Back" ); - this->AddChild( &m_sprCDTitleBack ); - - FOREACH_ENUM( PlayerNumber, p ) - { - m_sprHighScoreFrame[p].Load( THEME->GetPathG(m_sName,ssprintf("ScoreFrame P%d",p+1)) ); - m_sprHighScoreFrame[p]->SetName( ssprintf("ScoreFrameP%d",p+1) ); - LOAD_ALL_COMMANDS_AND_SET_XY( m_sprHighScoreFrame[p] ); - this->AddChild( m_sprHighScoreFrame[p] ); - - m_textHighScore[p].SetName( ssprintf("ScoreP%d",p+1) ); - m_textHighScore[p].LoadFromFont( THEME->GetPathF(m_sName,"score") ); - LOAD_ALL_COMMANDS_AND_SET_XY( m_textHighScore[p] ); - this->AddChild( &m_textHighScore[p] ); - } - - RageSoundLoadParams SoundParams; - SoundParams.m_bSupportPan = true; - - m_soundStart.Load( THEME->GetPathS(m_sName,"start") ); - m_soundDifficultyEasier.Load( THEME->GetPathS(m_sName,"difficulty easier"), false, &SoundParams ); - m_soundDifficultyHarder.Load( THEME->GetPathS(m_sName,"difficulty harder"), false, &SoundParams ); - m_soundOptionsChange.Load( THEME->GetPathS(m_sName,"options") ); - m_soundLocked.Load( THEME->GetPathS(m_sName,"locked") ); - - this->SortByDrawOrder(); -} - -void ScreenSelectMusic::BeginScreen() -{ - g_ScreenStartedLoadingAt.Touch(); - m_timerIdleComment.GetDeltaTime(); - - if( CommonMetrics::AUTO_SET_STYLE ) - { - vector vst; - GAMEMAN->GetStepsTypesForGame( GAMESTATE->m_pCurGame, vst ); - const Style *pStyle = GAMEMAN->GetFirstCompatibleStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined(), vst[0] ); - GAMESTATE->SetCurrentStyle( pStyle ); - } - - if( GAMESTATE->GetCurrentStyle() == NULL ) - RageException::Throw( "The Style has not been set. A theme must set the Style before loading ScreenSelectMusic." ); - - if( GAMESTATE->m_PlayMode == PlayMode_Invalid ) - { - // Instead of crashing here, let's just set the PlayMode to regular - GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); - LOG->Trace( "PlayMode not set, setting as regular." ); - } - FOREACH_ENUM( PlayerNumber, pn ) - { - if( GAMESTATE->IsHumanPlayer(pn) ) - continue; - m_sprHighScoreFrame[pn]->SetVisible( false ); - m_textHighScore[pn].SetVisible( false ); - } - - OPTIONS_MENU_AVAILABLE.Load( m_sName, "OptionsMenuAvailable" ); - PlayCommand( "Mods" ); - m_MusicWheel.BeginScreen(); - - m_SelectionState = SelectionState_SelectingSong; - ZERO( m_bStepsChosen ); - m_bGoToOptions = false; - m_bAllowOptionsMenu = m_bAllowOptionsMenuRepeat = false; - ZERO( m_iSelection ); - - if( USE_OPTIONS_LIST ) - FOREACH_PlayerNumber( pn ) - m_OptionsList[pn].Reset(); - - AfterMusicChange(); - - SOUND->PlayOnceFromAnnouncer( "select music intro" ); - - ScreenWithMenuElements::BeginScreen(); -} - -ScreenSelectMusic::~ScreenSelectMusic() -{ - LOG->Trace( "ScreenSelectMusic::~ScreenSelectMusic()" ); - BANNERCACHE->Undemand(); - //BACKGROUNDCACHE->Undemand(); -} - -// If bForce is true, the next request will be started even if it might cause a skip. -void ScreenSelectMusic::CheckBackgroundRequests( bool bForce ) -{ - if( g_bCDTitleWaiting ) - { - // The CDTitle is normally very small, so we don't bother waiting to display it. - RString sPath; - if( !m_BackgroundLoader.IsCacheFileFinished(g_sCDTitlePath, sPath) ) - return; - - g_bCDTitleWaiting = false; - - RString sCDTitlePath = sPath; - - if( sCDTitlePath.empty() || !IsAFile(sCDTitlePath) ) - sCDTitlePath = g_bWantFallbackCdTitle? m_sFallbackCDTitlePath:RString(""); - - if( !sCDTitlePath.empty() ) - { - TEXTUREMAN->DisableOddDimensionWarning(); - m_sprCDTitleFront.Load( sCDTitlePath ); - m_sprCDTitleBack.Load( sCDTitlePath ); - TEXTUREMAN->EnableOddDimensionWarning(); - } - - m_BackgroundLoader.FinishedWithCachedFile( g_sCDTitlePath ); - } - - /* Loading the rest can cause small skips, so don't do it until the wheel settles. - * Do load if we're transitioning out, though, so we don't miss starting the music - * for the options screen if a song is selected quickly. Also, don't do this - * if the wheel is locked, since we're just bouncing around after selecting - * TYPE_RANDOM, and it'll take a while before the wheel will settle. */ - if( !m_MusicWheel.IsSettled() && !m_MusicWheel.WheelIsLocked() && !bForce ) - return; - - if( g_bBannerWaiting ) - { - if( m_Banner.GetTweenTimeLeft() > 0 ) - return; - - RString sPath; - bool bFreeCache = false; - if( TEXTUREMAN->IsTextureRegistered( Sprite::SongBannerTexture(g_sBannerPath) ) ) - { - /* If the file is already loaded into a texture, it's finished, - * and we only do this to honor the HighQualTime value. */ - sPath = g_sBannerPath; - } - else - { - if( !m_BackgroundLoader.IsCacheFileFinished( g_sBannerPath, sPath ) ) - return; - bFreeCache = true; - } - - g_bBannerWaiting = false; - m_Banner.Load( sPath, true ); - - if( bFreeCache ) - m_BackgroundLoader.FinishedWithCachedFile( g_sBannerPath ); - } - - // Nothing else is going. Start the music, if we haven't yet. - if( g_bSampleMusicWaiting ) - { - if(g_ScreenStartedLoadingAt.Ago() < SAMPLE_MUSIC_DELAY_INIT) - return; - - // Don't start the music sample when moving fast. - if( g_StartedLoadingAt.Ago() < SAMPLE_MUSIC_DELAY && !bForce ) - return; - - g_bSampleMusicWaiting = false; - - GameSoundManager::PlayMusicParams PlayParams; - PlayParams.sFile = m_sSampleMusicToPlay; - PlayParams.pTiming = m_pSampleMusicTimingData; - PlayParams.bForceLoop = SAMPLE_MUSIC_LOOPS; - PlayParams.fStartSecond = m_fSampleStartSeconds; - PlayParams.fLengthSeconds = m_fSampleLengthSeconds; - PlayParams.fFadeOutLengthSeconds = 1.5f; - PlayParams.bAlignBeat = ALIGN_MUSIC_BEATS; - PlayParams.bApplyMusicRate = true; - - GameSoundManager::PlayMusicParams FallbackMusic; - FallbackMusic.sFile = m_sLoopMusicPath; - FallbackMusic.fFadeInLengthSeconds = SAMPLE_MUSIC_FALLBACK_FADE_IN_SECONDS; - FallbackMusic.bAlignBeat = ALIGN_MUSIC_BEATS; - - SOUND->PlayMusic( PlayParams, FallbackMusic ); - } -} - -void ScreenSelectMusic::Update( float fDeltaTime ) -{ - if( !IsTransitioning() ) - { - if( IDLE_COMMENT_SECONDS > 0 && m_timerIdleComment.PeekDeltaTime() >= IDLE_COMMENT_SECONDS ) - { - SOUND->PlayOnceFromAnnouncer( m_sName+" IdleComment" ); - m_timerIdleComment.GetDeltaTime(); - } - } - - ScreenWithMenuElements::Update( fDeltaTime ); - - CheckBackgroundRequests( false ); -} -void ScreenSelectMusic::Input( const InputEventPlus &input ) -{ -// LOG->Trace( "ScreenSelectMusic::Input()" ); - - // reset announcer timer - m_timerIdleComment.GetDeltaTime(); - - // debugging? - // I just like being able to see untransliterated titles occasionally. - if( input.DeviceI.device == DEVICE_KEYBOARD && input.DeviceI.button == KEY_F9 ) - { - if( input.type != IET_FIRST_PRESS ) - return; - PREFSMAN->m_bShowNativeLanguage.Set( !PREFSMAN->m_bShowNativeLanguage ); - MESSAGEMAN->Broadcast( "DisplayLanguageChanged" ); - m_MusicWheel.RebuildWheelItems(); - return; - } - - if( !input.GameI.IsValid() ) - return; // don't care - - // Handle late joining - if( m_SelectionState != SelectionState_Finalized && input.MenuI == GAME_BUTTON_START && input.type == IET_FIRST_PRESS && GAMESTATE->JoinInput(input.pn) ) - return; // don't handle this press again below - - if( !GAMESTATE->IsHumanPlayer(input.pn) ) - return; - - // Check for "Press START again for options" button press - if( m_SelectionState == SelectionState_Finalized && - input.MenuI == GAME_BUTTON_START && - input.type != IET_RELEASE && - OPTIONS_MENU_AVAILABLE.GetValue() ) - { - if( m_bGoToOptions ) - return; // got it already - if( !m_bAllowOptionsMenu ) - return; // not allowed - - if( !m_bAllowOptionsMenuRepeat && input.type == IET_REPEAT ) - { - return; // not allowed yet - } - - m_bGoToOptions = true; - if( PLAY_SOUND_ON_ENTERING_OPTIONS_MENU ) - m_soundStart.Play(); - this->PlayCommand( "ShowEnteringOptions" ); - - // Re-queue SM_BeginFadingOut, since ShowEnteringOptions may have - // short-circuited animations. - this->ClearMessageQueue( SM_BeginFadingOut ); - this->PostScreenMessage( SM_BeginFadingOut, this->GetTweenTimeLeft() ); - - return; - } - - if( IsTransitioning() ) - return; // ignore - - bool bHoldingCtrl = - INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL)) || - INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL)); - - wchar_t c = INPUTMAN->DeviceInputToChar(input.DeviceI,false); - MakeUpper( &c, 1 ); - - if( bHoldingCtrl && ( c >= 'A' ) && ( c <= 'Z' ) ) - { - // Only allow changing the sort order if the wheel is not locked - // and we're not in course mode. -aj - if( !m_MusicWheel.WheelIsLocked() && !GAMESTATE->IsCourseMode() ) - { - SortOrder so = GAMESTATE->m_SortOrder; - // When in Artist sort, this means first letter of the artist. - // Otherwise, if not in Title sort already, switch to Title sort. - if ( so != SORT_ARTIST ) - so = SORT_TITLE; - - GAMESTATE->m_PreferredSortOrder = so; - GAMESTATE->m_SortOrder.Set( so ); - // Odd, changing the sort order requires us to call SetOpenSection more than once - m_MusicWheel.ChangeSort( so ); - m_MusicWheel.SetOpenSection( ssprintf("%c", c ) ); - - m_MusicWheel.SelectSection( ssprintf("%c", c ) ); - m_MusicWheel.ChangeSort( so ); - m_MusicWheel.SetOpenSection( ssprintf("%c", c ) ); - AfterMusicChange(); - return; - } - } - - // Handle unselect steps - // xxx: select button could conflict with OptionsList here -aj - if( m_SelectionState == SelectionState_SelectingSteps && m_bStepsChosen[input.pn] - && input.MenuI == GAME_BUTTON_SELECT && input.type == IET_FIRST_PRESS ) - { - Message msg("StepsUnchosen"); - msg.SetParam( "Player", input.pn ); - MESSAGEMAN->Broadcast( msg ); - m_bStepsChosen[input.pn] = false; - return; - } - - if( m_SelectionState == SelectionState_Finalized || - m_bStepsChosen[input.pn] ) - return; // ignore - - // todo: Allow mousewheel to scroll MusicWheel -aj - /* - if( input.DeviceI.device == DEVICE_MOUSE ) - { - if( input.DeviceI.button == MOUSE_WHEELUP ) - { - MESSAGEMAN->Broadcast( "PreviousSong" ); - m_MusicWheel.Move( -1 ); - } - else if( input.DeviceI.button == MOUSE_WHEELDOWN ) - { - MESSAGEMAN->Broadcast( "NextSong" ); - m_MusicWheel.Move( +1 ); - } - } - */ - - if( USE_PLAYER_SELECT_MENU ) - { - if( input.type == IET_RELEASE && input.MenuI == GAME_BUTTON_SELECT ) - { - SCREENMAN->AddNewScreenToTop( SELECT_MENU_NAME, SM_BackFromPlayerOptions ); - } - } - - // handle OptionsList input - if( USE_OPTIONS_LIST ) - { - PlayerNumber pn = input.pn; - if( pn != PLAYER_INVALID ) - { - if( m_OptionsList[pn].IsOpened() ) - { - m_OptionsList[pn].Input( input ); - - return; - } - else - { - if( input.type == IET_RELEASE && input.MenuI == GAME_BUTTON_SELECT && m_bAcceptSelectRelease[pn] ) - m_OptionsList[pn].Open(); - } - } - } - - if( input.MenuI == GAME_BUTTON_SELECT && input.type != IET_REPEAT ) - m_bAcceptSelectRelease[input.pn] = (input.type == IET_FIRST_PRESS); - - if( SELECT_MENU_AVAILABLE && input.MenuI == GAME_BUTTON_SELECT && input.type != IET_REPEAT ) - UpdateSelectButton( input.pn, input.type == IET_FIRST_PRESS ); - - if( SELECT_MENU_AVAILABLE && m_bSelectIsDown[input.pn] ) - { - if( input.type == IET_FIRST_PRESS && SELECT_MENU_CHANGES_DIFFICULTY ) - { - switch( input.MenuI ) - { - case GAME_BUTTON_LEFT: - ChangeSteps( input.pn, -1 ); - m_bAcceptSelectRelease[input.pn] = false; - break; - case GAME_BUTTON_RIGHT: - ChangeSteps( input.pn, +1 ); - m_bAcceptSelectRelease[input.pn] = false; - break; - case GAME_BUTTON_START: - m_bAcceptSelectRelease[input.pn] = false; - if( MODE_MENU_AVAILABLE ) - m_MusicWheel.NextSort(); - else - m_soundLocked.Play(); - break; - default: break; - } - } - else if( input.type == IET_FIRST_PRESS && input.MenuI != GAME_BUTTON_SELECT ) - { - Message msg("SelectMenuInput"); - msg.SetParam( "Player", input.pn ); - msg.SetParam( "Button", GameButtonToString(INPUTMAPPER->GetInputScheme(), input.MenuI) ); - MESSAGEMAN->Broadcast( msg ); - m_bAcceptSelectRelease[input.pn] = false; - } - if( input.type == IET_FIRST_PRESS ) - g_CanOpenOptionsList.Touch(); - if( g_CanOpenOptionsList.Ago() > OPTIONS_LIST_TIMEOUT ) - m_bAcceptSelectRelease[input.pn] = false; - return; - } - - if( m_SelectionState == SelectionState_SelectingSong && - (input.MenuI == m_GameButtonNextSong || input.MenuI == m_GameButtonPreviousSong || input.MenuI == GAME_BUTTON_SELECT) ) - { - { - // If we're rouletting, hands off. - if( m_MusicWheel.IsRouletting() ) - return; - - bool bLeftIsDown = false; - bool bRightIsDown = false; - - FOREACH_HumanPlayer( p ) - { - if( m_OptionsList[p].IsOpened() ) - continue; - if( SELECT_MENU_AVAILABLE && INPUTMAPPER->IsBeingPressed(GAME_BUTTON_SELECT, p) ) - continue; - - bLeftIsDown |= INPUTMAPPER->IsBeingPressed( m_GameButtonPreviousSong, p ); - bRightIsDown |= INPUTMAPPER->IsBeingPressed( m_GameButtonNextSong, p ); - } - - bool bBothDown = bLeftIsDown && bRightIsDown; - bool bNeitherDown = !bLeftIsDown && !bRightIsDown; - - if( bNeitherDown ) - { - // Both buttons released. - m_MusicWheel.Move( 0 ); - } - else if( bBothDown ) - { - m_MusicWheel.Move( 0 ); - if( input.type == IET_FIRST_PRESS ) - { - if( input.MenuI == m_GameButtonPreviousSong ) - m_MusicWheel.ChangeMusicUnlessLocked( -1 ); - else if( input.MenuI == m_GameButtonNextSong ) - m_MusicWheel.ChangeMusicUnlessLocked( +1 ); - } - } - else if( bLeftIsDown ) - { - if( input.type != IET_RELEASE ) - { - MESSAGEMAN->Broadcast( "PreviousSong" ); - m_MusicWheel.Move( -1 ); - } - } - else if( bRightIsDown ) - { - if( input.type != IET_RELEASE ) - { - MESSAGEMAN->Broadcast( "NextSong" ); - m_MusicWheel.Move( +1 ); - } - } - else - { - ASSERT(0); - } - - // Reset the repeat timer when the button is released. - // This fixes jumping when you release Left and Right after entering the sort - // code at the same if L & R aren't released at the exact same time. - if( input.type == IET_RELEASE ) - { - INPUTMAPPER->ResetKeyRepeat( m_GameButtonPreviousSong, input.pn ); - INPUTMAPPER->ResetKeyRepeat( m_GameButtonNextSong, input.pn ); - } - } - } - - // To allow changing steps with gamebuttons, NOT WITH THE GODDAMN CODEDETECTOR - // yeah, wanted this since a while ago... -DaisuMaster - if( CHANGE_STEPS_WITH_GAME_BUTTONS ) - { - // Avoid any event not being first press - if( input.type != IET_FIRST_PRESS ) - return; - - if( m_SelectionState == SelectionState_SelectingSong ) - { - if (input.MenuI == m_GameButtonPreviousDifficulty ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - ChangeSteps( input.pn, -1 ); - } - else if( input.MenuI == m_GameButtonNextDifficulty ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - ChangeSteps( input.pn, +1 ); - } - } - } - - // Actually I don't like to just copy and paste code because it may go - // wrong if something goes overlooked -DaisuMaster - if( CHANGE_GROUPS_WITH_GAME_BUTTONS ) - { - if( input.type != IET_FIRST_PRESS) - return; - - if( m_SelectionState == SelectionState_SelectingSong ) - { - if(input.MenuI == m_GameButtonPreviousGroup ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - RString sNewGroup = m_MusicWheel.JumpToPrevGroup(); - m_MusicWheel.SelectSection(sNewGroup); - m_MusicWheel.SetOpenSection(sNewGroup); - MESSAGEMAN->Broadcast("PreviousGroup"); - AfterMusicChange(); - } - } - else if(input.MenuI == m_GameButtonNextGroup ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - RString sNewGroup = m_MusicWheel.JumpToNextGroup(); - m_MusicWheel.SelectSection(sNewGroup); - m_MusicWheel.SetOpenSection(sNewGroup); - MESSAGEMAN->Broadcast("NextGroup"); - AfterMusicChange(); - } - } - } - } - - // two part confirms only means we can actually change songs here, - // so we need some hackery. -aj - if( TWO_PART_CONFIRMS_ONLY ) - { - if( m_SelectionState == SelectionState_SelectingSteps ) - { - if( input.MenuI == m_GameButtonPreviousSong ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - m_SelectionState = SelectionState_SelectingSong; - MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); - MESSAGEMAN->Broadcast("PreviousSong"); - m_MusicWheel.ChangeMusicUnlessLocked( -1 ); - } - } - else if( input.MenuI == m_GameButtonNextSong ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - m_SelectionState = SelectionState_SelectingSong; - MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); - MESSAGEMAN->Broadcast("NextSong"); - m_MusicWheel.ChangeMusicUnlessLocked( +1 ); - } - } - // added an entry for difficulty change with gamebuttons -DaisuMaster - else if( input.MenuI == m_GameButtonPreviousDifficulty ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - m_SelectionState = SelectionState_SelectingSong; - MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); - ChangeSteps( input.pn, -1); - } - } - else if( input.MenuI == m_GameButtonNextDifficulty ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - m_SelectionState = SelectionState_SelectingSong; - MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); - ChangeSteps( input.pn, +1); - } - } - // added also for groupchanges - else if(input.MenuI == m_GameButtonPreviousGroup ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - RString sNewGroup = m_MusicWheel.JumpToPrevGroup(); - m_MusicWheel.SelectSection(sNewGroup); - m_MusicWheel.SetOpenSection(sNewGroup); - MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); - MESSAGEMAN->Broadcast("PreviousGroup"); - AfterMusicChange(); - } - } - else if(input.MenuI == m_GameButtonNextGroup ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - RString sNewGroup = m_MusicWheel.JumpToNextGroup(); - m_MusicWheel.SelectSection(sNewGroup); - m_MusicWheel.SetOpenSection(sNewGroup); - MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); - MESSAGEMAN->Broadcast("NextGroup"); - AfterMusicChange(); - } - } - } - } - else - { - if( m_SelectionState == SelectionState_SelectingSteps && input.type == IET_FIRST_PRESS && !m_bStepsChosen[input.pn] ) - { - if( input.MenuI == m_GameButtonNextSong || input.MenuI == m_GameButtonPreviousSong ) - { - if( input.MenuI == m_GameButtonPreviousSong ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - ChangeSteps( input.pn, -1 ); - } - else if( input.MenuI == m_GameButtonNextSong ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - ChangeSteps( input.pn, +1 ); - } - } - else if( input.MenuI == GAME_BUTTON_MENUUP || input.MenuI == GAME_BUTTON_MENUDOWN ) // && TWO_PART_DESELECTS_WITH_MENUUPDOWN - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - // XXX: should this be called "TwoPartCancelled"? - Message msg("SongUnchosen"); - msg.SetParam( "Player", input.pn ); - MESSAGEMAN->Broadcast( msg ); - // unset all steps - FOREACH_ENUM( PlayerNumber , p ) - m_bStepsChosen[p] = false; - m_SelectionState = SelectionState_SelectingSong; - } - } - } - } - - if( input.type == IET_FIRST_PRESS && DetectCodes(input) ) - return; - - ScreenWithMenuElements::Input( input ); -} - -bool ScreenSelectMusic::DetectCodes( const InputEventPlus &input ) -{ - if( CodeDetector::EnteredPrevSteps(input.GameI.controller) && !CHANGE_STEPS_WITH_GAME_BUTTONS ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - ChangeSteps( input.pn, -1 ); - } - else if( CodeDetector::EnteredNextSteps(input.GameI.controller) && !CHANGE_STEPS_WITH_GAME_BUTTONS ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - ChangeSteps( input.pn, +1 ); - } - else if( CodeDetector::EnteredModeMenu(input.GameI.controller) ) - { - if( MODE_MENU_AVAILABLE ) - m_MusicWheel.ChangeSort( SORT_MODE_MENU ); - else - m_soundLocked.Play(); - } - else if( CodeDetector::EnteredNextSort(input.GameI.controller) ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - m_MusicWheel.NextSort(); - } - else if( !GAMESTATE->IsAnExtraStageAndSelectionLocked() && CodeDetector::DetectAndAdjustMusicOptions(input.GameI.controller) ) - { - m_soundOptionsChange.Play(); - - Message msg( "PlayerOptionsChanged" ); - msg.SetParam( "PlayerNumber", input.pn ); - MESSAGEMAN->Broadcast( msg ); - - MESSAGEMAN->Broadcast( "SongOptionsChanged" ); - } - else if( CodeDetector::EnteredNextGroup(input.GameI.controller) && !CHANGE_GROUPS_WITH_GAME_BUTTONS ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - RString sNewGroup = m_MusicWheel.JumpToNextGroup(); - m_MusicWheel.SelectSection(sNewGroup); - m_MusicWheel.SetOpenSection(sNewGroup); - MESSAGEMAN->Broadcast("NextGroup"); - AfterMusicChange(); - } - } - else if( CodeDetector::EnteredPrevGroup(input.GameI.controller) && !CHANGE_GROUPS_WITH_GAME_BUTTONS ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - RString sNewGroup = m_MusicWheel.JumpToPrevGroup(); - m_MusicWheel.SelectSection(sNewGroup); - m_MusicWheel.SetOpenSection(sNewGroup); - MESSAGEMAN->Broadcast("PreviousGroup"); - AfterMusicChange(); - } - } - else if( CodeDetector::EnteredCloseFolder(input.GameI.controller) ) - { - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - m_soundLocked.Play(); - else - { - RString sCurSection = m_MusicWheel.GetSelectedSection(); - m_MusicWheel.SelectSection(sCurSection); - m_MusicWheel.SetOpenSection(""); - AfterMusicChange(); - } - } - else - { - return false; - } - return true; -} - -void ScreenSelectMusic::UpdateSelectButton( PlayerNumber pn, bool bSelectIsDown ) -{ - if( !SELECT_MENU_AVAILABLE || !CanChangeSong() ) - bSelectIsDown = false; - - if( m_bSelectIsDown[pn] != bSelectIsDown ) - { - m_bSelectIsDown[pn] = bSelectIsDown; - Message msg( bSelectIsDown ? "SelectMenuOpened" : "SelectMenuClosed" ); - msg.SetParam( "Player", pn ); - MESSAGEMAN->Broadcast( msg ); - } -} - -void ScreenSelectMusic::ChangeSteps( PlayerNumber pn, int dir ) -{ - LOG->Trace( "ScreenSelectMusic::ChangeSteps( %d, %d )", pn, dir ); - - ASSERT( GAMESTATE->IsHumanPlayer(pn) ); - - if( GAMESTATE->m_pCurSong ) - { - m_iSelection[pn] += dir; - if( WRAP_CHANGE_STEPS ) - { - wrap( m_iSelection[pn], m_vpSteps.size() ); - } - else - { - if( CLAMP(m_iSelection[pn],0,m_vpSteps.size()-1) ) - return; - } - - // the user explicity switched difficulties. Update the preferred Difficulty and StepsType - Steps *pSteps = m_vpSteps[ m_iSelection[pn] ]; - GAMESTATE->ChangePreferredDifficultyAndStepsType( pn, pSteps->GetDifficulty(), pSteps->m_StepsType ); - } - else if( GAMESTATE->m_pCurCourse ) - { - m_iSelection[pn] += dir; - if( WRAP_CHANGE_STEPS ) - { - wrap( m_iSelection[pn], m_vpTrails.size() ); - } - else - { - if( CLAMP(m_iSelection[pn],0,m_vpTrails.size()-1) ) - return; - } - - // the user explicity switched difficulties. Update the preferred Difficulty and StepsType - Trail *pTrail = m_vpTrails[ m_iSelection[pn] ]; - GAMESTATE->ChangePreferredCourseDifficultyAndStepsType( pn, pTrail->m_CourseDifficulty, pTrail->m_StepsType ); - } - else - { - // If we're showing multiple StepsTypes in the list, don't allow - // changing the difficulty/StepsType when a non-Song, non-Course is - // selected. Changing the preferred Difficulty and StepsType by - // direction is complicated when multiple StepsTypes are being shown, - // so we don't support it. - if( CommonMetrics::AUTO_SET_STYLE ) - return; - if( !GAMESTATE->ChangePreferredDifficulty( pn, dir ) ) - return; - } - - vector vpns; - FOREACH_HumanPlayer( p ) - { - if( pn == p || GAMESTATE->DifficultiesLocked() ) - { - m_iSelection[p] = m_iSelection[pn]; - vpns.push_back( p ); - } - } - AfterStepsOrTrailChange( vpns ); - - float fBalance = GameSoundManager::GetPlayerBalance( pn ); - if( dir < 0 ) - { - m_soundDifficultyEasier.SetProperty( "Pan", fBalance ); - m_soundDifficultyEasier.PlayCopy(); - } - else - { - m_soundDifficultyHarder.SetProperty( "Pan", fBalance ); - m_soundDifficultyHarder.PlayCopy(); - } - - Message msg( "ChangeSteps" ); - msg.SetParam( "Player", pn ); - msg.SetParam( "Direction", dir ); - MESSAGEMAN->Broadcast( msg ); -} - - -void ScreenSelectMusic::HandleMessage( const Message &msg ) -{ - if( m_bRunning && msg == Message_PlayerJoined ) - { - PlayerNumber master_pn = GAMESTATE->GetMasterPlayerNumber(); - // The current steps may no longer be playable. If one player has double - // steps selected, they are no longer playable now that P2 has joined. - - // TODO: Invalidate the CurSteps only if they are no longer playable. - // That way, after music change will clamp to the nearest in the StepsDisplayList. - GAMESTATE->m_pCurSteps[master_pn].SetWithoutBroadcast( NULL ); - FOREACH_ENUM( PlayerNumber, p ) - GAMESTATE->m_pCurSteps[p].SetWithoutBroadcast( NULL ); - - /* If a course is selected, it may no longer be playable. - * Let MusicWheel know about the late join. */ - m_MusicWheel.PlayerJoined(); - - AfterMusicChange(); - - int iSel = 0; - PlayerNumber pn; - bool b = msg.GetParam( "Player", pn ); - ASSERT( b ); - - // load player profiles - if( GAMESTATE->HaveProfileToLoad() ) - { - GAMESTATE->LoadProfiles( true ); // I guess you could always load edits here... - SCREENMAN->ZeroNextUpdate(); // be kind, don't skip frames if you can avoid it - } - - m_iSelection[pn] = iSel; - if( GAMESTATE->IsCourseMode() ) - { - Trail* pTrail = m_vpTrails.empty()? NULL: m_vpTrails[m_iSelection[pn]]; - GAMESTATE->m_pCurTrail[pn].Set( pTrail ); - } - else - { - Steps* pSteps = m_vpSteps.empty()? NULL: m_vpSteps[m_iSelection[pn]]; - - // handle changing rave difficulty on join - if(GAMESTATE->m_PlayMode == PLAY_MODE_RAVE) - pSteps = m_vpSteps[m_iSelection[master_pn]]; - - GAMESTATE->m_pCurSteps[pn].Set( pSteps ); - } - } - - ScreenWithMenuElements::HandleMessage( msg ); -} - -void ScreenSelectMusic::HandleScreenMessage( const ScreenMessage SM ) -{ - if( SM == SM_AllowOptionsMenuRepeat ) - { - m_bAllowOptionsMenuRepeat = true; - } - else if( SM == SM_MenuTimer ) - { - if( m_MusicWheel.IsRouletting() ) - { - MenuStart( InputEventPlus() ); - m_MenuTimer->SetSeconds( ROULETTE_TIMER_SECONDS ); - m_MenuTimer->Start(); - } - else if( DO_ROULETTE_ON_MENU_TIMER && m_MusicWheel.GetSelectedSong() == NULL && m_MusicWheel.GetSelectedCourse() == NULL ) - { - m_MusicWheel.StartRoulette(); - m_MenuTimer->SetSeconds( ROULETTE_TIMER_SECONDS ); - m_MenuTimer->Start(); - } - else - { - // Finish sort changing so that the wheel can respond immediately to - // our request to choose random. - m_MusicWheel.FinishChangingSorts(); - if( m_MusicWheel.GetSelectedSong() == NULL && m_MusicWheel.GetSelectedCourse() == NULL ) - m_MusicWheel.StartRandom(); - - MenuStart( InputEventPlus() ); - } - return; - } - else if( SM == SM_GoToPrevScreen ) - { - /* We may have stray SM_SongChanged messages from the music wheel. - * We can't handle them anymore, since the title menu (and attract - * screens) reset the game state, so just discard them. */ - ClearMessageQueue(); - } - else if( SM == SM_BeginFadingOut ) - { - m_bAllowOptionsMenu = false; - if( OPTIONS_MENU_AVAILABLE && !m_bGoToOptions ) - this->PlayCommand( "HidePressStartForOptions" ); - - this->PostScreenMessage( SM_GoToNextScreen, this->GetTweenTimeLeft() ); - } - else if( SM == SM_GoToNextScreen ) - { - if( !m_bGoToOptions ) - SOUND->StopMusic(); - } - else if( SM == SM_SongChanged ) - { - AfterMusicChange(); - } - else if( SM == SM_SortOrderChanging ) // happens immediately - { - this->PlayCommand( "SortChange" ); - } - else if( SM == SM_GainFocus ) - { - CodeDetector::RefreshCacheItems( CODES ); - } - else if( SM == SM_LoseFocus ) - { - CodeDetector::RefreshCacheItems(); // reset for other screens - } - - ScreenWithMenuElements::HandleScreenMessage( SM ); -} - -void ScreenSelectMusic::MenuStart( const InputEventPlus &input ) -{ - if( input.type != IET_FIRST_PRESS ) - return; - - /* If select is being pressed at the same time, this is probably an attempt - * to change the sort, not to pick a song or difficulty. If it gets here, - * the actual select press was probably hit during a tween and ignored. - * Ignore it. */ - if( input.pn != PLAYER_INVALID && INPUTMAPPER->IsBeingPressed(GAME_BUTTON_SELECT, input.pn) ) - return; - - // Honor locked input for start presses. - if( m_fLockInputSecs > 0 ) - return; - - switch( m_SelectionState ) - { - DEFAULT_FAIL( m_SelectionState ); - case SelectionState_SelectingSong: - // If false, we don't have a selection just yet. - if( !m_MusicWheel.Select() ) - return; - - // a song was selected - if( m_MusicWheel.GetSelectedSong() != NULL ) - { - if(TWO_PART_CONFIRMS_ONLY && SAMPLE_MUSIC_PREVIEW_MODE == SampleMusicPreviewMode_StartToPreview) - { - // start playing the preview music. - g_bSampleMusicWaiting = true; - CheckBackgroundRequests( true ); - } - - const bool bIsNew = PROFILEMAN->IsSongNew( m_MusicWheel.GetSelectedSong() ); - bool bIsHard = false; - FOREACH_HumanPlayer( p ) - { - if( GAMESTATE->m_pCurSteps[p] && GAMESTATE->m_pCurSteps[p]->GetMeter() >= 10 ) - bIsHard = true; - } - - // See if this song is a repeat. - // If we're in event mode, only check the last five songs. - bool bIsRepeat = false; - int i = 0; - if( GAMESTATE->IsEventMode() ) - i = max( 0, int(STATSMAN->m_vPlayedStageStats.size())-5 ); - for( ; i < (int)STATSMAN->m_vPlayedStageStats.size(); ++i ) - if( STATSMAN->m_vPlayedStageStats[i].m_vpPlayedSongs.back() == m_MusicWheel.GetSelectedSong() ) - bIsRepeat = true; - - // Don't complain about repeats if the user didn't get to pick. - if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) - bIsRepeat = false; - - if( bIsRepeat ) - SOUND->PlayOnceFromAnnouncer( "select music comment repeat" ); - else if( bIsNew ) - SOUND->PlayOnceFromAnnouncer( "select music comment new" ); - else if( bIsHard ) - SOUND->PlayOnceFromAnnouncer( "select music comment hard" ); - else - SOUND->PlayOnceFromAnnouncer( "select music comment general" ); - - /* If we're in event mode, we may have just played a course (putting - * us in course mode). Make sure we're in a single song mode. */ - if( GAMESTATE->IsCourseMode() ) - GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); - } - else if( m_MusicWheel.GetSelectedCourse() != NULL ) - { - SOUND->PlayOnceFromAnnouncer( "select course comment general" ); - - Course *pCourse = m_MusicWheel.GetSelectedCourse(); - ASSERT( pCourse ); - GAMESTATE->m_PlayMode.Set( pCourse->GetPlayMode() ); - - // apply #LIVES - if( pCourse->m_iLives != -1 ) - { - SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Stage, m_LifeType, SongOptions::LIFE_BATTERY ); - SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Stage, m_iBatteryLives, pCourse->m_iLives ); - } - if( pCourse->GetCourseType() == COURSE_TYPE_SURVIVAL) - SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Stage, m_LifeType, SongOptions::LIFE_TIME ); - } - else - { - // We haven't made a selection yet. - return; - } - // I believe this is for those who like pump pro. -aj - MESSAGEMAN->Broadcast("SongChosen"); - - break; - - case SelectionState_SelectingSteps: - { - PlayerNumber pn = input.pn; - bool bInitiatedByMenuTimer = pn == PLAYER_INVALID; - bool bAllOtherHumanPlayersDone = true; - FOREACH_HumanPlayer( p ) - { - if( p == pn ) - continue; - bAllOtherHumanPlayersDone &= m_bStepsChosen[p]; - } - - bool bAllPlayersDoneSelectingSteps = bInitiatedByMenuTimer || bAllOtherHumanPlayersDone; - if(TWO_PART_CONFIRMS_ONLY) - bAllPlayersDoneSelectingSteps = true; - - /* TRICKY: if we have a Routine chart selected, we need to ensure - * the following: - * 1. Both players must select the same Routine steps. - * 2. If the other player picks non-Routine steps, this player - * cannot pick Routine. - * 3. If the other player picked Routine steps, and we pick - * non-Routine steps, the other player's steps must be unselected. - * 4. If time runs out, and both players don't have the same Routine - * chart selected, we need to bump the player with a Routine - * chart selection to a playable chart. - * (Right now, we bump them to Beginner... Can we come up with - * something better?) - */ - - if( !GAMESTATE->IsCourseMode() && GAMESTATE->GetNumSidesJoined() == 2 ) - { - bool bSelectedRoutineSteps[NUM_PLAYERS], bAnySelectedRoutine = false; - bool bSelectedSameSteps = GAMESTATE->m_pCurSteps[PLAYER_1] == GAMESTATE->m_pCurSteps[PLAYER_2]; - - FOREACH_HumanPlayer( p ) - { - const Steps *pSteps = GAMESTATE->m_pCurSteps[p]; - const StepsTypeInfo &sti = GAMEMAN->GetStepsTypeInfo( pSteps->m_StepsType ); - - bSelectedRoutineSteps[p] = sti.m_StepsTypeCategory == StepsTypeCategory_Routine; - bAnySelectedRoutine |= bSelectedRoutineSteps[p]; - } - - if( bAnySelectedRoutine ) - { - /* Timer ran out. If we haven't agreed on steps, move players with - * Routine steps down to Beginner. I'll admit that's annoying, - * but at least they won't lose more stages. */ - if( bInitiatedByMenuTimer && !bSelectedSameSteps ) - { - /* Since m_vpSteps is sorted by Difficulty, the first - * entry should be the easiest. */ - ASSERT( m_vpSteps.size() ); - Steps *pSteps = m_vpSteps[0]; - - FOREACH_PlayerNumber( p ) - { - if( bSelectedRoutineSteps[p] ) - GAMESTATE->m_pCurSteps[p].Set( pSteps ); - } - - break; - } - - // If the steps don't match up, we need to check some more conditions... - if( !bSelectedSameSteps ) - { - const PlayerNumber other = OPPOSITE_PLAYER[pn]; - - if( m_bStepsChosen[other] ) - { - /* Unready the other player if they selected Routine - * steps, but we didn't. */ - if( bSelectedRoutineSteps[other] ) - { - m_bStepsChosen[other] = false; - bAllPlayersDoneSelectingSteps = false; // if the timer ran out, we handled it earlier - - // HACK: send an event to Input to tell it to unready. - InputEventPlus event; - event.MenuI = GAME_BUTTON_SELECT; - event.pn = other; - - this->Input( event ); - } - else if( bSelectedRoutineSteps[pn] ) - { - /* They selected non-Routine steps, so we can't - * select Routine steps. */ - return; - } - } - } - } - } - - if( !bAllPlayersDoneSelectingSteps ) - { - m_bStepsChosen[pn] = true; - m_soundStart.Play(); - - // impldiff: Pump it Up Pro uses "StepsSelected". -aj - Message msg("StepsChosen"); - msg.SetParam( "Player", pn ); - MESSAGEMAN->Broadcast( msg ); - return; - } - } - break; - } - - FOREACH_ENUM( PlayerNumber, p ) - { - if( !TWO_PART_SELECTION || m_SelectionState == SelectionState_SelectingSteps ) - { - if( m_OptionsList[p].IsOpened() ) - m_OptionsList[p].Close(); - } - UpdateSelectButton( p, false ); - } - - m_SelectionState = GetNextSelectionState(); - Message msg( "Start" + SelectionStateToString(m_SelectionState) ); - MESSAGEMAN->Broadcast( msg ); - - m_soundStart.Play(); - - // If the MenuTimer has forced us to move on && TWO_PART_CONFIRMS_ONLY, - // set Selection State to finalized and move on. - if(TWO_PART_CONFIRMS_ONLY) - { - if(m_MenuTimer->GetSeconds() < 1) - { - m_SelectionState = SelectionState_Finalized; - } - } - - if( m_SelectionState == SelectionState_Finalized ) - { - m_MenuTimer->Stop(); - - FOREACH_HumanPlayer( p ) - { - if( !m_bStepsChosen[p] ) - { - m_bStepsChosen[p] = true; - // Don't play start sound. We play it again below on finalized - //m_soundStart.Play(); - - Message lMsg("StepsChosen"); - lMsg.SetParam( "Player", p ); - MESSAGEMAN->Broadcast( lMsg ); - } - } - - if( CommonMetrics::AUTO_SET_STYLE ) - { - // Now that Steps have been chosen, set a Style that can play them. - const Style *pStyle = NULL; - if( GAMESTATE->IsCourseMode() ) - pStyle = GAMESTATE->m_pCurCourse->GetCourseStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined() ); - if( pStyle == NULL ) - { - StepsType stCurrent; - PlayerNumber pn = GAMESTATE->GetMasterPlayerNumber(); - if( GAMESTATE->IsCourseMode() ) - { - ASSERT( GAMESTATE->m_pCurTrail[pn] ); - stCurrent = GAMESTATE->m_pCurTrail[pn]->m_StepsType; - } - else - { - ASSERT( GAMESTATE->m_pCurSteps[pn] ); - stCurrent = GAMESTATE->m_pCurSteps[pn]->m_StepsType; - } - vector vst; - pStyle = GAMEMAN->GetFirstCompatibleStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined(), stCurrent ); - } - GAMESTATE->SetCurrentStyle( pStyle ); - } - - /* If we're currently waiting on song assets, abort all except the music - * and start the music, so if we make a choice quickly before background - * requests come through, the music will still start. */ - g_bCDTitleWaiting = g_bBannerWaiting = false; - m_BackgroundLoader.Abort(); - CheckBackgroundRequests( true ); - - if( OPTIONS_MENU_AVAILABLE ) - { - // show "hold START for options" - this->PlayCommand( "ShowPressStartForOptions" ); - - m_bAllowOptionsMenu = true; - - /* Don't accept a held START for a little while, so it's not - * hit accidentally. Accept an initial START right away, though, - * so we don't ignore deliberate fast presses (which would be - * annoying). */ - this->PostScreenMessage( SM_AllowOptionsMenuRepeat, 0.5f ); - - StartTransitioningScreen( SM_None ); - float fTime = max( SHOW_OPTIONS_MESSAGE_SECONDS, this->GetTweenTimeLeft() ); - this->PostScreenMessage( SM_BeginFadingOut, fTime ); - } - else - { - StartTransitioningScreen( SM_BeginFadingOut ); - } - } - else // !finalized. Set the timer for selecting difficulty and mods. - { - float fSeconds = m_MenuTimer->GetSeconds(); - if( fSeconds < 10 ) - { - m_MenuTimer->SetSeconds( TWO_PART_TIMER_SECONDS ); // was 13 -aj - m_MenuTimer->Start(); - } - } -} - - -void ScreenSelectMusic::MenuBack( const InputEventPlus &input ) -{ - // Handle unselect song (ffff) - // todo: this isn't right at all. -aj - /* - if( m_SelectionState == SelectionState_SelectingSteps && - !m_bStepsChosen[input.pn] && input.MenuI == GAME_BUTTON_BACK && - input.type == IET_FIRST_PRESS ) - { - // if a player has chosen their steps already, don't unchoose song. - FOREACH_HumanPlayer( p ) - if( m_bStepsChosen[p] ) return; - - // and if we get here... - Message msg("SongUnchosen"); - msg.SetParam( "Player", input.pn ); - MESSAGEMAN->Broadcast( msg ); - m_SelectionState = SelectionState_SelectingSong; - return; - } - */ - - m_BackgroundLoader.Abort(); - - Cancel( SM_GoToPrevScreen ); -} - -void ScreenSelectMusic::AfterStepsOrTrailChange( const vector &vpns ) -{ - if(TWO_PART_CONFIRMS_ONLY && m_SelectionState == SelectionState_SelectingSteps) - { - // if TWO_PART_CONFIRMS_ONLY, changing difficulties unsets the song. -aj - m_SelectionState = SelectionState_SelectingSong; - MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); - } - - FOREACH_CONST( PlayerNumber, vpns, p ) - { - PlayerNumber pn = *p; - ASSERT( GAMESTATE->IsHumanPlayer(pn) ); - - if( GAMESTATE->m_pCurSong ) - { - CLAMP( m_iSelection[pn], 0, m_vpSteps.size()-1 ); - - Song* pSong = GAMESTATE->m_pCurSong; - Steps* pSteps = m_vpSteps.empty()? NULL: m_vpSteps[m_iSelection[pn]]; - - GAMESTATE->m_pCurSteps[pn].Set( pSteps ); - GAMESTATE->m_pCurTrail[pn].Set( NULL ); - - int iScore = 0; - if( pSteps ) - { - const Profile *pProfile = PROFILEMAN->IsPersistentProfile(pn) ? PROFILEMAN->GetProfile(pn) : PROFILEMAN->GetMachineProfile(); - iScore = pProfile->GetStepsHighScoreList(pSong,pSteps).GetTopScore().GetScore(); - } - - m_textHighScore[pn].SetText( ssprintf("%*i", NUM_SCORE_DIGITS, iScore) ); - } - else if( GAMESTATE->m_pCurCourse ) - { - CLAMP( m_iSelection[pn], 0, m_vpTrails.size()-1 ); - - Course* pCourse = GAMESTATE->m_pCurCourse; - Trail* pTrail = m_vpTrails.empty()? NULL: m_vpTrails[m_iSelection[pn]]; - - GAMESTATE->m_pCurSteps[pn].Set( NULL ); - GAMESTATE->m_pCurTrail[pn].Set( pTrail ); - - int iScore = 0; - if( pTrail ) - { - const Profile *pProfile = PROFILEMAN->IsPersistentProfile(pn) ? PROFILEMAN->GetProfile(pn) : PROFILEMAN->GetMachineProfile(); - iScore = pProfile->GetCourseHighScoreList(pCourse,pTrail).GetTopScore().GetScore(); - } - - m_textHighScore[pn].SetText( ssprintf("%*i", NUM_SCORE_DIGITS, iScore) ); - } - else - { - // The numbers shouldn't stay if the current selection is NULL. - m_textHighScore[pn].SetText( NULL_SCORE_STRING ); - } - } -} - -void ScreenSelectMusic::SwitchToPreferredDifficulty() -{ - if( !GAMESTATE->m_pCurCourse ) - { - FOREACH_HumanPlayer( pn ) - { - // Find the closest match to the user's preferred difficulty and StepsType. - int iCurDifference = -1; - int &iSelection = m_iSelection[pn]; - FOREACH_CONST( Steps*, m_vpSteps, s ) - { - int i = s - m_vpSteps.begin(); - - // If the current steps are listed, use them. - if( GAMESTATE->m_pCurSteps[pn] == *s ) - { - iSelection = i; - break; - } - - if( GAMESTATE->m_PreferredDifficulty[pn] != Difficulty_Invalid ) - { - int iDifficultyDifference = abs( (*s)->GetDifficulty() - GAMESTATE->m_PreferredDifficulty[pn] ); - int iStepsTypeDifference = 0; - if( GAMESTATE->m_PreferredStepsType != StepsType_Invalid ) - iStepsTypeDifference = abs( (*s)->m_StepsType - GAMESTATE->m_PreferredStepsType ); - int iTotalDifference = iStepsTypeDifference * NUM_Difficulty + iDifficultyDifference; - - if( iCurDifference == -1 || iTotalDifference < iCurDifference ) - { - iSelection = i; - iCurDifference = iTotalDifference; - } - } - } - - CLAMP( iSelection, 0, m_vpSteps.size()-1 ); - } - } - else - { - FOREACH_HumanPlayer( pn ) - { - // Find the closest match to the user's preferred difficulty. - int iCurDifference = -1; - int &iSelection = m_iSelection[pn]; - FOREACH_CONST( Trail*, m_vpTrails, t ) - { - int i = t - m_vpTrails.begin(); - - // If the current trail is listed, use it. - if( GAMESTATE->m_pCurTrail[pn] == m_vpTrails[i] ) - { - iSelection = i; - break; - } - - if( GAMESTATE->m_PreferredCourseDifficulty[pn] != Difficulty_Invalid && GAMESTATE->m_PreferredStepsType != StepsType_Invalid ) - { - int iDifficultyDifference = abs( (*t)->m_CourseDifficulty - GAMESTATE->m_PreferredCourseDifficulty[pn] ); - int iStepsTypeDifference = abs( (*t)->m_StepsType - GAMESTATE->m_PreferredStepsType ); - int iTotalDifference = iStepsTypeDifference * NUM_CourseDifficulty + iDifficultyDifference; - - if( iCurDifference == -1 || iTotalDifference < iCurDifference ) - { - iSelection = i; - iCurDifference = iTotalDifference; - } - } - } - - CLAMP( iSelection, 0, m_vpTrails.size()-1 ); - } - } - - if( GAMESTATE->DifficultiesLocked() ) - { - FOREACH_HumanPlayer( p ) - m_iSelection[p] = m_iSelection[GAMESTATE->GetMasterPlayerNumber()]; - } -} - -void ScreenSelectMusic::AfterMusicChange() -{ - if( !m_MusicWheel.IsRouletting() ) - m_MenuTimer->Stall(); - - Song* pSong = m_MusicWheel.GetSelectedSong(); - GAMESTATE->m_pCurSong.Set( pSong ); - if( pSong ) - GAMESTATE->m_pPreferredSong = pSong; - - Course* pCourse = m_MusicWheel.GetSelectedCourse(); - GAMESTATE->m_pCurCourse.Set( pCourse ); - if( pCourse ) - GAMESTATE->m_pPreferredCourse = pCourse; - - m_vpSteps.clear(); - m_vpTrails.clear(); - - m_Banner.SetMovingFast( !!m_MusicWheel.IsMoving() ); - - vector m_Artists, m_AltArtists; - - if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) - { - m_sSampleMusicToPlay = ""; - } - m_pSampleMusicTimingData = NULL; - g_sCDTitlePath = ""; - g_sBannerPath = ""; - g_bWantFallbackCdTitle = false; - bool bWantBanner = true; - - static SortOrder s_lastSortOrder = SortOrder_Invalid; - if( GAMESTATE->m_SortOrder != s_lastSortOrder ) - { - // Reload to let Lua metrics have a chance to change the help text. - s_lastSortOrder = GAMESTATE->m_SortOrder; - } - - switch( m_MusicWheel.GetSelectedType() ) - { - case WheelItemDataType_Section: - case WheelItemDataType_Sort: - case WheelItemDataType_Roulette: - case WheelItemDataType_Random: - case WheelItemDataType_Custom: - FOREACH_PlayerNumber( p ) - m_iSelection[p] = -1; - - g_sCDTitlePath = ""; // none - - if( SAMPLE_MUSIC_PREVIEW_MODE == SampleMusicPreviewMode_LastSong ) - { - // HACK: Make random music work in LastSong mode. -aj - if( m_sSampleMusicToPlay == m_sRandomMusicPath ) - { - m_fSampleStartSeconds = 0; - m_fSampleLengthSeconds = -1; - } - } - else - { - m_fSampleStartSeconds = 0; - m_fSampleLengthSeconds = -1; - } - - switch( m_MusicWheel.GetSelectedType() ) - { - case WheelItemDataType_Section: - // reduce scope - { - SortOrder curSort = GAMESTATE->m_SortOrder; - if( curSort == SORT_GROUP) - { - g_sBannerPath = SONGMAN->GetSongGroupBannerPath( m_MusicWheel.GetSelectedSection() ); - } - else - { - bWantBanner = false; // we load it ourself - m_Banner.LoadFromSortOrder(curSort); - } - - if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) - m_sSampleMusicToPlay = m_sSectionMusicPath; - } - break; - case WheelItemDataType_Sort: - bWantBanner = false; // we load it ourself - m_Banner.LoadMode(); - if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) - m_sSampleMusicToPlay = m_sSortMusicPath; - break; - case WheelItemDataType_Roulette: - bWantBanner = false; // we load it ourself - m_Banner.LoadRoulette(); - if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) - m_sSampleMusicToPlay = m_sRouletteMusicPath; - break; - case WheelItemDataType_Random: - bWantBanner = false; // we load it ourself - m_Banner.LoadRandom(); - //if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) - m_sSampleMusicToPlay = m_sRandomMusicPath; - break; - case WheelItemDataType_Custom: - { - bWantBanner = false; // we load it ourself - RString sBannerName = GetMusicWheel()->GetCurWheelItemData( GetMusicWheel()->GetCurrentIndex() )->m_pAction->m_sName.c_str(); - m_Banner.LoadCustom(sBannerName); - if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) - m_sSampleMusicToPlay = m_sSectionMusicPath; - } - break; - default: - ASSERT(0); - } - // override this if the sample music mode wants to. - /* - if(SAMPLE_MUSIC_PREVIEW_MODE == SampleMusicPreviewMode_LastSong) - { - m_sSampleMusicToPlay = pSong->GetMusicPath(); - m_pSampleMusicTimingData = &pSong->m_Timing; - m_fSampleStartSeconds = pSong->m_fMusicSampleStartSeconds; - m_fSampleLengthSeconds = pSong->m_fMusicSampleLengthSeconds; - } - */ - break; - case WheelItemDataType_Song: - case WheelItemDataType_Portal: - // check SampleMusicPreviewMode here. - switch( SAMPLE_MUSIC_PREVIEW_MODE ) - { - case SampleMusicPreviewMode_ScreenMusic: - // play the screen music - m_sSampleMusicToPlay = m_sLoopMusicPath; - m_fSampleStartSeconds = 0; - m_fSampleLengthSeconds = -1; - break; - case SampleMusicPreviewMode_StartToPreview: - // we want to load the sample music, but we don't want to - // actually play it. fall through. -aj - case SampleMusicPreviewMode_Normal: - case SampleMusicPreviewMode_LastSong: // fall through - // play the sample music - m_sSampleMusicToPlay = pSong->GetMusicPath(); - m_pSampleMusicTimingData = &pSong->m_SongTiming; - m_fSampleStartSeconds = pSong->m_fMusicSampleStartSeconds; - m_fSampleLengthSeconds = pSong->m_fMusicSampleLengthSeconds; - break; - default: - ASSERT(0); - } - - SongUtil::GetPlayableSteps( pSong, m_vpSteps ); - - if ( PREFSMAN->m_bShowBanners ) - g_sBannerPath = pSong->GetBannerPath(); - - g_sCDTitlePath = pSong->GetCDTitlePath(); - g_bWantFallbackCdTitle = true; - - SwitchToPreferredDifficulty(); - break; - - case WheelItemDataType_Course: - { - const Course *lCourse = m_MusicWheel.GetSelectedCourse(); - const Style *pStyle = NULL; - if( CommonMetrics::AUTO_SET_STYLE ) - pStyle = pCourse->GetCourseStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined() ); - if( pStyle == NULL ) - pStyle = GAMESTATE->GetCurrentStyle(); - lCourse->GetTrails( m_vpTrails, pStyle->m_StepsType ); - - m_sSampleMusicToPlay = m_sCourseMusicPath; - m_fSampleStartSeconds = 0; - m_fSampleLengthSeconds = -1; - - g_sBannerPath = lCourse->GetBannerPath(); - if( g_sBannerPath.empty() ) - m_Banner.LoadFallback(); - - SwitchToPreferredDifficulty(); - break; - } - default: - ASSERT(0); - } - - m_sprCDTitleFront.UnloadTexture(); - m_sprCDTitleBack.UnloadTexture(); - - // Cancel any previous, incomplete requests for song assets, - // since we need new ones. - m_BackgroundLoader.Abort(); - - g_bCDTitleWaiting = false; - if( !g_sCDTitlePath.empty() || g_bWantFallbackCdTitle ) - { - LOG->Trace( "cache \"%s\"", g_sCDTitlePath.c_str()); - m_BackgroundLoader.CacheFile( g_sCDTitlePath ); // empty OK - g_bCDTitleWaiting = true; - } - - g_bBannerWaiting = false; - if( bWantBanner ) - { - LOG->Trace("LoadFromCachedBanner(%s)",g_sBannerPath .c_str()); - if( m_Banner.LoadFromCachedBanner( g_sBannerPath ) ) - { - /* If the high-res banner is already loaded, just delay before - * loading it, so the low-res one has time to fade in. */ - if( !TEXTUREMAN->IsTextureRegistered( Sprite::SongBannerTexture(g_sBannerPath) ) ) - m_BackgroundLoader.CacheFile( g_sBannerPath ); - - g_bBannerWaiting = true; - } - } - - // Don't stop music if it's already playing the right file. - g_bSampleMusicWaiting = false; - if( !m_MusicWheel.IsRouletting() && SOUND->GetMusicPath() != m_sSampleMusicToPlay ) - { - SOUND->StopMusic(); - // some SampleMusicPreviewModes don't want the sample music immediately. - if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_StartToPreview ) - { - if( !m_sSampleMusicToPlay.empty() ) - g_bSampleMusicWaiting = true; - } - } - - g_StartedLoadingAt.Touch(); - - vector vpns; - FOREACH_HumanPlayer( p ) - vpns.push_back( p ); - - AfterStepsOrTrailChange( vpns ); -} - -void ScreenSelectMusic::OpenOptionsList(PlayerNumber pn) -{ - if( pn != PLAYER_INVALID ) - { - m_OptionsList[pn].Open(); - } -} - -// lua start -#include "LuaBinding.h" - -/** @brief Allow Lua to have access to the ScreenSelectMusic. */ -class LunaScreenSelectMusic: public Luna -{ -public: - static int GetGoToOptions( T* p, lua_State *L ) { lua_pushboolean( L, p->GetGoToOptions() ); return 1; } - static int GetMusicWheel( T* p, lua_State *L ) { - p->GetMusicWheel()->PushSelf(L); - return 1; - } - static int OpenOptionsList( T* p, lua_State *L ) { PlayerNumber pn = Enum::Check(L, 1); p->OpenOptionsList(pn); return 0; } - - LunaScreenSelectMusic() - { - ADD_METHOD( GetGoToOptions ); - ADD_METHOD( GetMusicWheel ); - ADD_METHOD( OpenOptionsList ); - } -}; - -LUA_REGISTER_DERIVED_CLASS( ScreenSelectMusic, ScreenWithMenuElements ) -// lua end - -/* - * (c) 2001-2004 Chris Danford - * 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. - */ +#include "global.h" +#include "ScreenSelectMusic.h" +#include "ScreenManager.h" +#include "PrefsManager.h" +#include "SongManager.h" +#include "GameManager.h" +#include "GameSoundManager.h" +#include "GameConstantsAndTypes.h" +#include "RageLog.h" +#include "InputMapper.h" +#include "GameState.h" +#include "CodeDetector.h" +#include "ThemeManager.h" +#include "Steps.h" +#include "ActorUtil.h" +#include "RageTextureManager.h" +#include "Course.h" +#include "ProfileManager.h" +#include "Profile.h" +#include "MenuTimer.h" +#include "StatsManager.h" +#include "StepsUtil.h" +#include "Foreach.h" +#include "Style.h" +#include "PlayerState.h" +#include "CommonMetrics.h" +#include "BannerCache.h" +//#include "BackgroundCache.h" +#include "Song.h" +#include "InputEventPlus.h" +#include "RageInput.h" +#include "OptionsList.h" + +static const char *SelectionStateNames[] = { + "SelectingSong", + "SelectingSteps", + "Finalized" +}; +XToString( SelectionState ); + +/** @brief The maximum number of digits for the ScoreDisplay. */ +const int NUM_SCORE_DIGITS = 9; + +#define SHOW_OPTIONS_MESSAGE_SECONDS THEME->GetMetricF( m_sName, "ShowOptionsMessageSeconds" ) + +AutoScreenMessage( SM_AllowOptionsMenuRepeat ); +AutoScreenMessage( SM_SongChanged ); +AutoScreenMessage( SM_SortOrderChanging ); +AutoScreenMessage( SM_SortOrderChanged ); +AutoScreenMessage( SM_BackFromPlayerOptions ); + +static RString g_sCDTitlePath; +static bool g_bWantFallbackCdTitle; +static bool g_bCDTitleWaiting = false; +static RString g_sBannerPath; +static bool g_bBannerWaiting = false; +static bool g_bSampleMusicWaiting = false; +static RageTimer g_StartedLoadingAt(RageZeroTimer); +static RageTimer g_ScreenStartedLoadingAt(RageZeroTimer); +RageTimer g_CanOpenOptionsList(RageZeroTimer); + +REGISTER_SCREEN_CLASS( ScreenSelectMusic ); +void ScreenSelectMusic::Init() +{ + g_ScreenStartedLoadingAt.Touch(); + if( PREFSMAN->m_sTestInitialScreen.Get() == m_sName ) + { + GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); + GAMESTATE->SetCurrentStyle( GAMEMAN->GameAndStringToStyle(GAMEMAN->GetDefaultGame(),"versus") ); + GAMESTATE->JoinPlayer( PLAYER_1 ); + GAMESTATE->SetMasterPlayerNumber(PLAYER_1); + } + + IDLE_COMMENT_SECONDS.Load( m_sName, "IdleCommentSeconds" ); + SAMPLE_MUSIC_DELAY_INIT.Load( m_sName, "SampleMusicDelayInit" ); + SAMPLE_MUSIC_DELAY.Load( m_sName, "SampleMusicDelay" ); + SAMPLE_MUSIC_LOOPS.Load( m_sName, "SampleMusicLoops" ); + SAMPLE_MUSIC_PREVIEW_MODE.Load( m_sName, "SampleMusicPreviewMode" ); + SAMPLE_MUSIC_FALLBACK_FADE_IN_SECONDS.Load( m_sName, "SampleMusicFallbackFadeInSeconds" ); + DO_ROULETTE_ON_MENU_TIMER.Load( m_sName, "DoRouletteOnMenuTimer" ); + ROULETTE_TIMER_SECONDS.Load( m_sName, "RouletteTimerSeconds" ); + ALIGN_MUSIC_BEATS.Load( m_sName, "AlignMusicBeat" ); + CODES.Load( m_sName, "Codes" ); + MUSIC_WHEEL_TYPE.Load( m_sName, "MusicWheelType" ); + SELECT_MENU_AVAILABLE.Load( m_sName, "SelectMenuAvailable" ); + MODE_MENU_AVAILABLE.Load( m_sName, "ModeMenuAvailable" ); + USE_OPTIONS_LIST.Load( m_sName, "UseOptionsList" ); + USE_PLAYER_SELECT_MENU.Load( m_sName, "UsePlayerSelectMenu" ); + SELECT_MENU_NAME.Load( m_sName, "SelectMenuScreenName" ); + OPTIONS_LIST_TIMEOUT.Load( m_sName, "OptionsListTimeout" ); + SELECT_MENU_CHANGES_DIFFICULTY.Load( m_sName, "SelectMenuChangesDifficulty" ); + TWO_PART_SELECTION.Load( m_sName, "TwoPartSelection" ); + TWO_PART_CONFIRMS_ONLY.Load( m_sName, "TwoPartConfirmsOnly" ); + TWO_PART_TIMER_SECONDS.Load( m_sName, "TwoPartTimerSeconds" ); + WRAP_CHANGE_STEPS.Load( m_sName, "WrapChangeSteps" ); + NULL_SCORE_STRING.Load( m_sName, "NullScoreString" ); + PLAY_SOUND_ON_ENTERING_OPTIONS_MENU.Load( m_sName, "PlaySoundOnEnteringOptionsMenu" ); + // To allow changing steps with gamebuttons -DaisuMaster + CHANGE_STEPS_WITH_GAME_BUTTONS.Load( m_sName, "ChangeStepsWithGameButtons" ); + CHANGE_GROUPS_WITH_GAME_BUTTONS.Load( m_sName, "ChangeGroupsWithGameButtons" ); + + m_GameButtonPreviousSong = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"PreviousSongButton") ); + m_GameButtonNextSong = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"NextSongButton") ); + + // Ask for those only if changing steps with gamebuttons is allowed -DaisuMaster + if( CHANGE_STEPS_WITH_GAME_BUTTONS ) + { + m_GameButtonPreviousDifficulty = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"PreviousDifficultyButton") ); + m_GameButtonNextDifficulty = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"NextDifficultyButton") ); + } + // same here but for groups -DaisuMaster + if( CHANGE_GROUPS_WITH_GAME_BUTTONS ) + { + m_GameButtonPreviousGroup = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"PreviousGroupButton") ); + m_GameButtonNextGroup = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( THEME->GetMetric(m_sName,"NextGroupButton") ); + } + + FOREACH_ENUM( PlayerNumber, p ) + { + m_bSelectIsDown[p] = false; // used by UpdateSelectButton + m_bAcceptSelectRelease[p] = false; + } + + ScreenWithMenuElements::Init(); + + this->SubscribeToMessage( Message_PlayerJoined ); + + // Cache these values + // Marking for change -- Midiman (why? -aj) + m_sSectionMusicPath = THEME->GetPathS(m_sName,"section music"); + m_sSortMusicPath = THEME->GetPathS(m_sName,"sort music"); + m_sRouletteMusicPath = THEME->GetPathS(m_sName,"roulette music"); + m_sRandomMusicPath = THEME->GetPathS(m_sName,"random music"); + m_sCourseMusicPath = THEME->GetPathS(m_sName,"course music"); + m_sLoopMusicPath = THEME->GetPathS(m_sName,"loop music"); + m_sFallbackCDTitlePath = THEME->GetPathG(m_sName,"fallback cdtitle"); + + m_TexturePreload.Load( m_sFallbackCDTitlePath ); + + // load banners + if( PREFSMAN->m_BannerCache != BNCACHE_OFF ) + { + m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","all music")) ); + m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Common","fallback banner")) ); + m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","roulette")) ); + m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","random")) ); + m_TexturePreload.Load( Banner::SongBannerTexture(THEME->GetPathG("Banner","mode")) ); + } + // load backgrounds + /* + if( PREFSMAN->m_BackgroundCache != BGCACHE_OFF ) + { + m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","AllMusic")) ); + m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("Common","fallback banner")) ); + m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","roulette")) ); + m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","random")) ); + m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","Mode")) ); + m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","group fallback")) ); + m_TexturePreload.Load( Sprite::SongBGTexture(THEME->GetPathG("SongBackgroundItem","course fallback")) ); + } + */ + + // Load low-res banners and backgrounds if needed. + BANNERCACHE->Demand(); + //BACKGROUNDCACHE->Demand(); + + m_MusicWheel.SetName( "MusicWheel" ); + m_MusicWheel.Load( MUSIC_WHEEL_TYPE ); + LOAD_ALL_COMMANDS_AND_SET_XY( m_MusicWheel ); + this->AddChild( &m_MusicWheel ); + + if( USE_OPTIONS_LIST ) + { + FOREACH_PlayerNumber(p) + { + m_OptionsList[p].SetName( "OptionsList" + PlayerNumberToString(p) ); + m_OptionsList[p].Load( "OptionsList", p ); + m_OptionsList[p].SetDrawOrder( 100 ); + ActorUtil::LoadAllCommands( m_OptionsList[p], m_sName ); + this->AddChild( &m_OptionsList[p] ); + } + m_OptionsList[PLAYER_1].Link( &m_OptionsList[PLAYER_2] ); + m_OptionsList[PLAYER_2].Link( &m_OptionsList[PLAYER_1] ); + } + + // this is loaded SetSong and TweenToSong + m_Banner.SetName( "Banner" ); + LOAD_ALL_COMMANDS_AND_SET_XY( m_Banner ); + this->AddChild( &m_Banner ); + + m_sprCDTitleFront.SetName( "CDTitle" ); + m_sprCDTitleFront.Load( THEME->GetPathG(m_sName,"fallback cdtitle") ); + LOAD_ALL_COMMANDS_AND_SET_XY( m_sprCDTitleFront ); + COMMAND( m_sprCDTitleFront, "Front" ); + this->AddChild( &m_sprCDTitleFront ); + + m_sprCDTitleBack.SetName( "CDTitle" ); + m_sprCDTitleBack.Load( THEME->GetPathG(m_sName,"fallback cdtitle") ); + LOAD_ALL_COMMANDS_AND_SET_XY( m_sprCDTitleBack ); + COMMAND( m_sprCDTitleBack, "Back" ); + this->AddChild( &m_sprCDTitleBack ); + + FOREACH_ENUM( PlayerNumber, p ) + { + m_sprHighScoreFrame[p].Load( THEME->GetPathG(m_sName,ssprintf("ScoreFrame P%d",p+1)) ); + m_sprHighScoreFrame[p]->SetName( ssprintf("ScoreFrameP%d",p+1) ); + LOAD_ALL_COMMANDS_AND_SET_XY( m_sprHighScoreFrame[p] ); + this->AddChild( m_sprHighScoreFrame[p] ); + + m_textHighScore[p].SetName( ssprintf("ScoreP%d",p+1) ); + m_textHighScore[p].LoadFromFont( THEME->GetPathF(m_sName,"score") ); + LOAD_ALL_COMMANDS_AND_SET_XY( m_textHighScore[p] ); + this->AddChild( &m_textHighScore[p] ); + } + + RageSoundLoadParams SoundParams; + SoundParams.m_bSupportPan = true; + + m_soundStart.Load( THEME->GetPathS(m_sName,"start") ); + m_soundDifficultyEasier.Load( THEME->GetPathS(m_sName,"difficulty easier"), false, &SoundParams ); + m_soundDifficultyHarder.Load( THEME->GetPathS(m_sName,"difficulty harder"), false, &SoundParams ); + m_soundOptionsChange.Load( THEME->GetPathS(m_sName,"options") ); + m_soundLocked.Load( THEME->GetPathS(m_sName,"locked") ); + + this->SortByDrawOrder(); +} + +void ScreenSelectMusic::BeginScreen() +{ + g_ScreenStartedLoadingAt.Touch(); + m_timerIdleComment.GetDeltaTime(); + + if( CommonMetrics::AUTO_SET_STYLE ) + { + vector vst; + GAMEMAN->GetStepsTypesForGame( GAMESTATE->m_pCurGame, vst ); + const Style *pStyle = GAMEMAN->GetFirstCompatibleStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined(), vst[0] ); + GAMESTATE->SetCurrentStyle( pStyle ); + } + + if( GAMESTATE->GetCurrentStyle() == NULL ) + RageException::Throw( "The Style has not been set. A theme must set the Style before loading ScreenSelectMusic." ); + + if( GAMESTATE->m_PlayMode == PlayMode_Invalid ) + { + // Instead of crashing here, let's just set the PlayMode to regular + GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); + LOG->Trace( "PlayMode not set, setting as regular." ); + } + FOREACH_ENUM( PlayerNumber, pn ) + { + if( GAMESTATE->IsHumanPlayer(pn) ) + continue; + m_sprHighScoreFrame[pn]->SetVisible( false ); + m_textHighScore[pn].SetVisible( false ); + } + + OPTIONS_MENU_AVAILABLE.Load( m_sName, "OptionsMenuAvailable" ); + PlayCommand( "Mods" ); + m_MusicWheel.BeginScreen(); + + m_SelectionState = SelectionState_SelectingSong; + ZERO( m_bStepsChosen ); + m_bGoToOptions = false; + m_bAllowOptionsMenu = m_bAllowOptionsMenuRepeat = false; + ZERO( m_iSelection ); + + if( USE_OPTIONS_LIST ) + FOREACH_PlayerNumber( pn ) + m_OptionsList[pn].Reset(); + + AfterMusicChange(); + + SOUND->PlayOnceFromAnnouncer( "select music intro" ); + + ScreenWithMenuElements::BeginScreen(); +} + +ScreenSelectMusic::~ScreenSelectMusic() +{ + LOG->Trace( "ScreenSelectMusic::~ScreenSelectMusic()" ); + BANNERCACHE->Undemand(); + //BACKGROUNDCACHE->Undemand(); +} + +// If bForce is true, the next request will be started even if it might cause a skip. +void ScreenSelectMusic::CheckBackgroundRequests( bool bForce ) +{ + if( g_bCDTitleWaiting ) + { + // The CDTitle is normally very small, so we don't bother waiting to display it. + RString sPath; + if( !m_BackgroundLoader.IsCacheFileFinished(g_sCDTitlePath, sPath) ) + return; + + g_bCDTitleWaiting = false; + + RString sCDTitlePath = sPath; + + if( sCDTitlePath.empty() || !IsAFile(sCDTitlePath) ) + sCDTitlePath = g_bWantFallbackCdTitle? m_sFallbackCDTitlePath:RString(""); + + if( !sCDTitlePath.empty() ) + { + TEXTUREMAN->DisableOddDimensionWarning(); + m_sprCDTitleFront.Load( sCDTitlePath ); + m_sprCDTitleBack.Load( sCDTitlePath ); + TEXTUREMAN->EnableOddDimensionWarning(); + } + + m_BackgroundLoader.FinishedWithCachedFile( g_sCDTitlePath ); + } + + /* Loading the rest can cause small skips, so don't do it until the wheel settles. + * Do load if we're transitioning out, though, so we don't miss starting the music + * for the options screen if a song is selected quickly. Also, don't do this + * if the wheel is locked, since we're just bouncing around after selecting + * TYPE_RANDOM, and it'll take a while before the wheel will settle. */ + if( !m_MusicWheel.IsSettled() && !m_MusicWheel.WheelIsLocked() && !bForce ) + return; + + if( g_bBannerWaiting ) + { + if( m_Banner.GetTweenTimeLeft() > 0 ) + return; + + RString sPath; + bool bFreeCache = false; + if( TEXTUREMAN->IsTextureRegistered( Sprite::SongBannerTexture(g_sBannerPath) ) ) + { + /* If the file is already loaded into a texture, it's finished, + * and we only do this to honor the HighQualTime value. */ + sPath = g_sBannerPath; + } + else + { + if( !m_BackgroundLoader.IsCacheFileFinished( g_sBannerPath, sPath ) ) + return; + bFreeCache = true; + } + + g_bBannerWaiting = false; + m_Banner.Load( sPath, true ); + + if( bFreeCache ) + m_BackgroundLoader.FinishedWithCachedFile( g_sBannerPath ); + } + + // Nothing else is going. Start the music, if we haven't yet. + if( g_bSampleMusicWaiting ) + { + if(g_ScreenStartedLoadingAt.Ago() < SAMPLE_MUSIC_DELAY_INIT) + return; + + // Don't start the music sample when moving fast. + if( g_StartedLoadingAt.Ago() < SAMPLE_MUSIC_DELAY && !bForce ) + return; + + g_bSampleMusicWaiting = false; + + GameSoundManager::PlayMusicParams PlayParams; + PlayParams.sFile = m_sSampleMusicToPlay; + PlayParams.pTiming = m_pSampleMusicTimingData; + PlayParams.bForceLoop = SAMPLE_MUSIC_LOOPS; + PlayParams.fStartSecond = m_fSampleStartSeconds; + PlayParams.fLengthSeconds = m_fSampleLengthSeconds; + PlayParams.fFadeOutLengthSeconds = 1.5f; + PlayParams.bAlignBeat = ALIGN_MUSIC_BEATS; + PlayParams.bApplyMusicRate = true; + + GameSoundManager::PlayMusicParams FallbackMusic; + FallbackMusic.sFile = m_sLoopMusicPath; + FallbackMusic.fFadeInLengthSeconds = SAMPLE_MUSIC_FALLBACK_FADE_IN_SECONDS; + FallbackMusic.bAlignBeat = ALIGN_MUSIC_BEATS; + + SOUND->PlayMusic( PlayParams, FallbackMusic ); + } +} + +void ScreenSelectMusic::Update( float fDeltaTime ) +{ + if( !IsTransitioning() ) + { + if( IDLE_COMMENT_SECONDS > 0 && m_timerIdleComment.PeekDeltaTime() >= IDLE_COMMENT_SECONDS ) + { + SOUND->PlayOnceFromAnnouncer( m_sName+" IdleComment" ); + m_timerIdleComment.GetDeltaTime(); + } + } + + ScreenWithMenuElements::Update( fDeltaTime ); + + CheckBackgroundRequests( false ); +} +void ScreenSelectMusic::Input( const InputEventPlus &input ) +{ +// LOG->Trace( "ScreenSelectMusic::Input()" ); + + // reset announcer timer + m_timerIdleComment.GetDeltaTime(); + + // debugging? + // I just like being able to see untransliterated titles occasionally. + if( input.DeviceI.device == DEVICE_KEYBOARD && input.DeviceI.button == KEY_F9 ) + { + if( input.type != IET_FIRST_PRESS ) + return; + PREFSMAN->m_bShowNativeLanguage.Set( !PREFSMAN->m_bShowNativeLanguage ); + MESSAGEMAN->Broadcast( "DisplayLanguageChanged" ); + m_MusicWheel.RebuildWheelItems(); + return; + } + + if( !IsTransitioning() && m_SelectionState != SelectionState_Finalized ) + { + bool bHoldingCtrl = + INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL)) || + INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL)); + + wchar_t c = INPUTMAN->DeviceInputToChar(input.DeviceI,false); + MakeUpper( &c, 1 ); + + if( bHoldingCtrl && ( c >= 'A' ) && ( c <= 'Z' ) ) + { + // Only allow changing the sort order if the wheel is not locked + // and we're not in course mode. -aj + if( !m_MusicWheel.WheelIsLocked() && !GAMESTATE->IsCourseMode() ) + { + SortOrder so = GAMESTATE->m_SortOrder; + // When in Artist sort, this means first letter of the artist. + // Otherwise, if not in Title sort already, switch to Title sort. + if ( so != SORT_ARTIST ) + so = SORT_TITLE; + + GAMESTATE->m_PreferredSortOrder = so; + GAMESTATE->m_SortOrder.Set( so ); + // Odd, changing the sort order requires us to call SetOpenSection more than once + m_MusicWheel.ChangeSort( so ); + m_MusicWheel.SetOpenSection( ssprintf("%c", c ) ); + + m_MusicWheel.SelectSection( ssprintf("%c", c ) ); + m_MusicWheel.ChangeSort( so ); + m_MusicWheel.SetOpenSection( ssprintf("%c", c ) ); + AfterMusicChange(); + return; + } + } + } + + if( !input.GameI.IsValid() ) + return; // don't care + + // Handle late joining + if( m_SelectionState != SelectionState_Finalized && input.MenuI == GAME_BUTTON_START && input.type == IET_FIRST_PRESS && GAMESTATE->JoinInput(input.pn) ) + return; // don't handle this press again below + + if( !GAMESTATE->IsHumanPlayer(input.pn) ) + return; + + // Check for "Press START again for options" button press + if( m_SelectionState == SelectionState_Finalized && + input.MenuI == GAME_BUTTON_START && + input.type != IET_RELEASE && + OPTIONS_MENU_AVAILABLE.GetValue() ) + { + if( m_bGoToOptions ) + return; // got it already + if( !m_bAllowOptionsMenu ) + return; // not allowed + + if( !m_bAllowOptionsMenuRepeat && input.type == IET_REPEAT ) + { + return; // not allowed yet + } + + m_bGoToOptions = true; + if( PLAY_SOUND_ON_ENTERING_OPTIONS_MENU ) + m_soundStart.Play(); + this->PlayCommand( "ShowEnteringOptions" ); + + // Re-queue SM_BeginFadingOut, since ShowEnteringOptions may have + // short-circuited animations. + this->ClearMessageQueue( SM_BeginFadingOut ); + this->PostScreenMessage( SM_BeginFadingOut, this->GetTweenTimeLeft() ); + + return; + } + + if( IsTransitioning() ) + return; // ignore + + // Handle unselect steps + // xxx: select button could conflict with OptionsList here -aj + if( m_SelectionState == SelectionState_SelectingSteps && m_bStepsChosen[input.pn] + && input.MenuI == GAME_BUTTON_SELECT && input.type == IET_FIRST_PRESS ) + { + Message msg("StepsUnchosen"); + msg.SetParam( "Player", input.pn ); + MESSAGEMAN->Broadcast( msg ); + m_bStepsChosen[input.pn] = false; + return; + } + + if( m_SelectionState == SelectionState_Finalized || + m_bStepsChosen[input.pn] ) + return; // ignore + + // todo: Allow mousewheel to scroll MusicWheel -aj + /* + if( input.DeviceI.device == DEVICE_MOUSE ) + { + if( input.DeviceI.button == MOUSE_WHEELUP ) + { + MESSAGEMAN->Broadcast( "PreviousSong" ); + m_MusicWheel.Move( -1 ); + } + else if( input.DeviceI.button == MOUSE_WHEELDOWN ) + { + MESSAGEMAN->Broadcast( "NextSong" ); + m_MusicWheel.Move( +1 ); + } + } + */ + + if( USE_PLAYER_SELECT_MENU ) + { + if( input.type == IET_RELEASE && input.MenuI == GAME_BUTTON_SELECT ) + { + SCREENMAN->AddNewScreenToTop( SELECT_MENU_NAME, SM_BackFromPlayerOptions ); + } + } + + // handle OptionsList input + if( USE_OPTIONS_LIST ) + { + PlayerNumber pn = input.pn; + if( pn != PLAYER_INVALID ) + { + if( m_OptionsList[pn].IsOpened() ) + { + m_OptionsList[pn].Input( input ); + + return; + } + else + { + if( input.type == IET_RELEASE && input.MenuI == GAME_BUTTON_SELECT && m_bAcceptSelectRelease[pn] ) + m_OptionsList[pn].Open(); + } + } + } + + if( input.MenuI == GAME_BUTTON_SELECT && input.type != IET_REPEAT ) + m_bAcceptSelectRelease[input.pn] = (input.type == IET_FIRST_PRESS); + + if( SELECT_MENU_AVAILABLE && input.MenuI == GAME_BUTTON_SELECT && input.type != IET_REPEAT ) + UpdateSelectButton( input.pn, input.type == IET_FIRST_PRESS ); + + if( SELECT_MENU_AVAILABLE && m_bSelectIsDown[input.pn] ) + { + if( input.type == IET_FIRST_PRESS && SELECT_MENU_CHANGES_DIFFICULTY ) + { + switch( input.MenuI ) + { + case GAME_BUTTON_LEFT: + ChangeSteps( input.pn, -1 ); + m_bAcceptSelectRelease[input.pn] = false; + break; + case GAME_BUTTON_RIGHT: + ChangeSteps( input.pn, +1 ); + m_bAcceptSelectRelease[input.pn] = false; + break; + case GAME_BUTTON_START: + m_bAcceptSelectRelease[input.pn] = false; + if( MODE_MENU_AVAILABLE ) + m_MusicWheel.NextSort(); + else + m_soundLocked.Play(); + break; + default: break; + } + } + else if( input.type == IET_FIRST_PRESS && input.MenuI != GAME_BUTTON_SELECT ) + { + Message msg("SelectMenuInput"); + msg.SetParam( "Player", input.pn ); + msg.SetParam( "Button", GameButtonToString(INPUTMAPPER->GetInputScheme(), input.MenuI) ); + MESSAGEMAN->Broadcast( msg ); + m_bAcceptSelectRelease[input.pn] = false; + } + if( input.type == IET_FIRST_PRESS ) + g_CanOpenOptionsList.Touch(); + if( g_CanOpenOptionsList.Ago() > OPTIONS_LIST_TIMEOUT ) + m_bAcceptSelectRelease[input.pn] = false; + return; + } + + if( m_SelectionState == SelectionState_SelectingSong && + (input.MenuI == m_GameButtonNextSong || input.MenuI == m_GameButtonPreviousSong || input.MenuI == GAME_BUTTON_SELECT) ) + { + { + // If we're rouletting, hands off. + if( m_MusicWheel.IsRouletting() ) + return; + + bool bLeftIsDown = false; + bool bRightIsDown = false; + + FOREACH_HumanPlayer( p ) + { + if( m_OptionsList[p].IsOpened() ) + continue; + if( SELECT_MENU_AVAILABLE && INPUTMAPPER->IsBeingPressed(GAME_BUTTON_SELECT, p) ) + continue; + + bLeftIsDown |= INPUTMAPPER->IsBeingPressed( m_GameButtonPreviousSong, p ); + bRightIsDown |= INPUTMAPPER->IsBeingPressed( m_GameButtonNextSong, p ); + } + + bool bBothDown = bLeftIsDown && bRightIsDown; + bool bNeitherDown = !bLeftIsDown && !bRightIsDown; + + if( bNeitherDown ) + { + // Both buttons released. + m_MusicWheel.Move( 0 ); + } + else if( bBothDown ) + { + m_MusicWheel.Move( 0 ); + if( input.type == IET_FIRST_PRESS ) + { + if( input.MenuI == m_GameButtonPreviousSong ) + m_MusicWheel.ChangeMusicUnlessLocked( -1 ); + else if( input.MenuI == m_GameButtonNextSong ) + m_MusicWheel.ChangeMusicUnlessLocked( +1 ); + } + } + else if( bLeftIsDown ) + { + if( input.type != IET_RELEASE ) + { + MESSAGEMAN->Broadcast( "PreviousSong" ); + m_MusicWheel.Move( -1 ); + } + } + else if( bRightIsDown ) + { + if( input.type != IET_RELEASE ) + { + MESSAGEMAN->Broadcast( "NextSong" ); + m_MusicWheel.Move( +1 ); + } + } + else + { + ASSERT(0); + } + + // Reset the repeat timer when the button is released. + // This fixes jumping when you release Left and Right after entering the sort + // code at the same if L & R aren't released at the exact same time. + if( input.type == IET_RELEASE ) + { + INPUTMAPPER->ResetKeyRepeat( m_GameButtonPreviousSong, input.pn ); + INPUTMAPPER->ResetKeyRepeat( m_GameButtonNextSong, input.pn ); + } + } + } + + // To allow changing steps with gamebuttons, NOT WITH THE GODDAMN CODEDETECTOR + // yeah, wanted this since a while ago... -DaisuMaster + if( CHANGE_STEPS_WITH_GAME_BUTTONS ) + { + // Avoid any event not being first press + if( input.type != IET_FIRST_PRESS ) + return; + + if( m_SelectionState == SelectionState_SelectingSong ) + { + if (input.MenuI == m_GameButtonPreviousDifficulty ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + ChangeSteps( input.pn, -1 ); + } + else if( input.MenuI == m_GameButtonNextDifficulty ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + ChangeSteps( input.pn, +1 ); + } + } + } + + // Actually I don't like to just copy and paste code because it may go + // wrong if something goes overlooked -DaisuMaster + if( CHANGE_GROUPS_WITH_GAME_BUTTONS ) + { + if( input.type != IET_FIRST_PRESS) + return; + + if( m_SelectionState == SelectionState_SelectingSong ) + { + if(input.MenuI == m_GameButtonPreviousGroup ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + RString sNewGroup = m_MusicWheel.JumpToPrevGroup(); + m_MusicWheel.SelectSection(sNewGroup); + m_MusicWheel.SetOpenSection(sNewGroup); + MESSAGEMAN->Broadcast("PreviousGroup"); + AfterMusicChange(); + } + } + else if(input.MenuI == m_GameButtonNextGroup ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + RString sNewGroup = m_MusicWheel.JumpToNextGroup(); + m_MusicWheel.SelectSection(sNewGroup); + m_MusicWheel.SetOpenSection(sNewGroup); + MESSAGEMAN->Broadcast("NextGroup"); + AfterMusicChange(); + } + } + } + } + + // two part confirms only means we can actually change songs here, + // so we need some hackery. -aj + if( TWO_PART_CONFIRMS_ONLY ) + { + if( m_SelectionState == SelectionState_SelectingSteps ) + { + if( input.MenuI == m_GameButtonPreviousSong ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + m_SelectionState = SelectionState_SelectingSong; + MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); + MESSAGEMAN->Broadcast("PreviousSong"); + m_MusicWheel.ChangeMusicUnlessLocked( -1 ); + } + } + else if( input.MenuI == m_GameButtonNextSong ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + m_SelectionState = SelectionState_SelectingSong; + MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); + MESSAGEMAN->Broadcast("NextSong"); + m_MusicWheel.ChangeMusicUnlessLocked( +1 ); + } + } + // added an entry for difficulty change with gamebuttons -DaisuMaster + else if( input.MenuI == m_GameButtonPreviousDifficulty ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + m_SelectionState = SelectionState_SelectingSong; + MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); + ChangeSteps( input.pn, -1); + } + } + else if( input.MenuI == m_GameButtonNextDifficulty ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + m_SelectionState = SelectionState_SelectingSong; + MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); + ChangeSteps( input.pn, +1); + } + } + // added also for groupchanges + else if(input.MenuI == m_GameButtonPreviousGroup ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + RString sNewGroup = m_MusicWheel.JumpToPrevGroup(); + m_MusicWheel.SelectSection(sNewGroup); + m_MusicWheel.SetOpenSection(sNewGroup); + MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); + MESSAGEMAN->Broadcast("PreviousGroup"); + AfterMusicChange(); + } + } + else if(input.MenuI == m_GameButtonNextGroup ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + RString sNewGroup = m_MusicWheel.JumpToNextGroup(); + m_MusicWheel.SelectSection(sNewGroup); + m_MusicWheel.SetOpenSection(sNewGroup); + MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); + MESSAGEMAN->Broadcast("NextGroup"); + AfterMusicChange(); + } + } + } + } + else + { + if( m_SelectionState == SelectionState_SelectingSteps && input.type == IET_FIRST_PRESS && !m_bStepsChosen[input.pn] ) + { + if( input.MenuI == m_GameButtonNextSong || input.MenuI == m_GameButtonPreviousSong ) + { + if( input.MenuI == m_GameButtonPreviousSong ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + ChangeSteps( input.pn, -1 ); + } + else if( input.MenuI == m_GameButtonNextSong ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + ChangeSteps( input.pn, +1 ); + } + } + else if( input.MenuI == GAME_BUTTON_MENUUP || input.MenuI == GAME_BUTTON_MENUDOWN ) // && TWO_PART_DESELECTS_WITH_MENUUPDOWN + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + // XXX: should this be called "TwoPartCancelled"? + Message msg("SongUnchosen"); + msg.SetParam( "Player", input.pn ); + MESSAGEMAN->Broadcast( msg ); + // unset all steps + FOREACH_ENUM( PlayerNumber , p ) + m_bStepsChosen[p] = false; + m_SelectionState = SelectionState_SelectingSong; + } + } + } + } + + if( input.type == IET_FIRST_PRESS && DetectCodes(input) ) + return; + + ScreenWithMenuElements::Input( input ); +} + +bool ScreenSelectMusic::DetectCodes( const InputEventPlus &input ) +{ + if( CodeDetector::EnteredPrevSteps(input.GameI.controller) && !CHANGE_STEPS_WITH_GAME_BUTTONS ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + ChangeSteps( input.pn, -1 ); + } + else if( CodeDetector::EnteredNextSteps(input.GameI.controller) && !CHANGE_STEPS_WITH_GAME_BUTTONS ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + ChangeSteps( input.pn, +1 ); + } + else if( CodeDetector::EnteredModeMenu(input.GameI.controller) ) + { + if( MODE_MENU_AVAILABLE ) + m_MusicWheel.ChangeSort( SORT_MODE_MENU ); + else + m_soundLocked.Play(); + } + else if( CodeDetector::EnteredNextSort(input.GameI.controller) ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + m_MusicWheel.NextSort(); + } + else if( !GAMESTATE->IsAnExtraStageAndSelectionLocked() && CodeDetector::DetectAndAdjustMusicOptions(input.GameI.controller) ) + { + m_soundOptionsChange.Play(); + + Message msg( "PlayerOptionsChanged" ); + msg.SetParam( "PlayerNumber", input.pn ); + MESSAGEMAN->Broadcast( msg ); + + MESSAGEMAN->Broadcast( "SongOptionsChanged" ); + } + else if( CodeDetector::EnteredNextGroup(input.GameI.controller) && !CHANGE_GROUPS_WITH_GAME_BUTTONS ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + RString sNewGroup = m_MusicWheel.JumpToNextGroup(); + m_MusicWheel.SelectSection(sNewGroup); + m_MusicWheel.SetOpenSection(sNewGroup); + MESSAGEMAN->Broadcast("NextGroup"); + AfterMusicChange(); + } + } + else if( CodeDetector::EnteredPrevGroup(input.GameI.controller) && !CHANGE_GROUPS_WITH_GAME_BUTTONS ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + RString sNewGroup = m_MusicWheel.JumpToPrevGroup(); + m_MusicWheel.SelectSection(sNewGroup); + m_MusicWheel.SetOpenSection(sNewGroup); + MESSAGEMAN->Broadcast("PreviousGroup"); + AfterMusicChange(); + } + } + else if( CodeDetector::EnteredCloseFolder(input.GameI.controller) ) + { + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + m_soundLocked.Play(); + else + { + RString sCurSection = m_MusicWheel.GetSelectedSection(); + m_MusicWheel.SelectSection(sCurSection); + m_MusicWheel.SetOpenSection(""); + AfterMusicChange(); + } + } + else + { + return false; + } + return true; +} + +void ScreenSelectMusic::UpdateSelectButton( PlayerNumber pn, bool bSelectIsDown ) +{ + if( !SELECT_MENU_AVAILABLE || !CanChangeSong() ) + bSelectIsDown = false; + + if( m_bSelectIsDown[pn] != bSelectIsDown ) + { + m_bSelectIsDown[pn] = bSelectIsDown; + Message msg( bSelectIsDown ? "SelectMenuOpened" : "SelectMenuClosed" ); + msg.SetParam( "Player", pn ); + MESSAGEMAN->Broadcast( msg ); + } +} + +void ScreenSelectMusic::ChangeSteps( PlayerNumber pn, int dir ) +{ + LOG->Trace( "ScreenSelectMusic::ChangeSteps( %d, %d )", pn, dir ); + + ASSERT( GAMESTATE->IsHumanPlayer(pn) ); + + if( GAMESTATE->m_pCurSong ) + { + m_iSelection[pn] += dir; + if( WRAP_CHANGE_STEPS ) + { + wrap( m_iSelection[pn], m_vpSteps.size() ); + } + else + { + if( CLAMP(m_iSelection[pn],0,m_vpSteps.size()-1) ) + return; + } + + // the user explicity switched difficulties. Update the preferred Difficulty and StepsType + Steps *pSteps = m_vpSteps[ m_iSelection[pn] ]; + GAMESTATE->ChangePreferredDifficultyAndStepsType( pn, pSteps->GetDifficulty(), pSteps->m_StepsType ); + } + else if( GAMESTATE->m_pCurCourse ) + { + m_iSelection[pn] += dir; + if( WRAP_CHANGE_STEPS ) + { + wrap( m_iSelection[pn], m_vpTrails.size() ); + } + else + { + if( CLAMP(m_iSelection[pn],0,m_vpTrails.size()-1) ) + return; + } + + // the user explicity switched difficulties. Update the preferred Difficulty and StepsType + Trail *pTrail = m_vpTrails[ m_iSelection[pn] ]; + GAMESTATE->ChangePreferredCourseDifficultyAndStepsType( pn, pTrail->m_CourseDifficulty, pTrail->m_StepsType ); + } + else + { + // If we're showing multiple StepsTypes in the list, don't allow + // changing the difficulty/StepsType when a non-Song, non-Course is + // selected. Changing the preferred Difficulty and StepsType by + // direction is complicated when multiple StepsTypes are being shown, + // so we don't support it. + if( CommonMetrics::AUTO_SET_STYLE ) + return; + if( !GAMESTATE->ChangePreferredDifficulty( pn, dir ) ) + return; + } + + vector vpns; + FOREACH_HumanPlayer( p ) + { + if( pn == p || GAMESTATE->DifficultiesLocked() ) + { + m_iSelection[p] = m_iSelection[pn]; + vpns.push_back( p ); + } + } + AfterStepsOrTrailChange( vpns ); + + float fBalance = GameSoundManager::GetPlayerBalance( pn ); + if( dir < 0 ) + { + m_soundDifficultyEasier.SetProperty( "Pan", fBalance ); + m_soundDifficultyEasier.PlayCopy(); + } + else + { + m_soundDifficultyHarder.SetProperty( "Pan", fBalance ); + m_soundDifficultyHarder.PlayCopy(); + } + + Message msg( "ChangeSteps" ); + msg.SetParam( "Player", pn ); + msg.SetParam( "Direction", dir ); + MESSAGEMAN->Broadcast( msg ); +} + + +void ScreenSelectMusic::HandleMessage( const Message &msg ) +{ + if( m_bRunning && msg == Message_PlayerJoined ) + { + PlayerNumber master_pn = GAMESTATE->GetMasterPlayerNumber(); + // The current steps may no longer be playable. If one player has double + // steps selected, they are no longer playable now that P2 has joined. + + // TODO: Invalidate the CurSteps only if they are no longer playable. + // That way, after music change will clamp to the nearest in the StepsDisplayList. + GAMESTATE->m_pCurSteps[master_pn].SetWithoutBroadcast( NULL ); + FOREACH_ENUM( PlayerNumber, p ) + GAMESTATE->m_pCurSteps[p].SetWithoutBroadcast( NULL ); + + /* If a course is selected, it may no longer be playable. + * Let MusicWheel know about the late join. */ + m_MusicWheel.PlayerJoined(); + + AfterMusicChange(); + + int iSel = 0; + PlayerNumber pn; + bool b = msg.GetParam( "Player", pn ); + ASSERT( b ); + + // load player profiles + if( GAMESTATE->HaveProfileToLoad() ) + { + GAMESTATE->LoadProfiles( true ); // I guess you could always load edits here... + SCREENMAN->ZeroNextUpdate(); // be kind, don't skip frames if you can avoid it + } + + m_iSelection[pn] = iSel; + if( GAMESTATE->IsCourseMode() ) + { + Trail* pTrail = m_vpTrails.empty()? NULL: m_vpTrails[m_iSelection[pn]]; + GAMESTATE->m_pCurTrail[pn].Set( pTrail ); + } + else + { + Steps* pSteps = m_vpSteps.empty()? NULL: m_vpSteps[m_iSelection[pn]]; + + // handle changing rave difficulty on join + if(GAMESTATE->m_PlayMode == PLAY_MODE_RAVE) + pSteps = m_vpSteps[m_iSelection[master_pn]]; + + GAMESTATE->m_pCurSteps[pn].Set( pSteps ); + } + } + + ScreenWithMenuElements::HandleMessage( msg ); +} + +void ScreenSelectMusic::HandleScreenMessage( const ScreenMessage SM ) +{ + if( SM == SM_AllowOptionsMenuRepeat ) + { + m_bAllowOptionsMenuRepeat = true; + } + else if( SM == SM_MenuTimer ) + { + if( m_MusicWheel.IsRouletting() ) + { + MenuStart( InputEventPlus() ); + m_MenuTimer->SetSeconds( ROULETTE_TIMER_SECONDS ); + m_MenuTimer->Start(); + } + else if( DO_ROULETTE_ON_MENU_TIMER && m_MusicWheel.GetSelectedSong() == NULL && m_MusicWheel.GetSelectedCourse() == NULL ) + { + m_MusicWheel.StartRoulette(); + m_MenuTimer->SetSeconds( ROULETTE_TIMER_SECONDS ); + m_MenuTimer->Start(); + } + else + { + // Finish sort changing so that the wheel can respond immediately to + // our request to choose random. + m_MusicWheel.FinishChangingSorts(); + if( m_MusicWheel.GetSelectedSong() == NULL && m_MusicWheel.GetSelectedCourse() == NULL ) + m_MusicWheel.StartRandom(); + + MenuStart( InputEventPlus() ); + } + return; + } + else if( SM == SM_GoToPrevScreen ) + { + /* We may have stray SM_SongChanged messages from the music wheel. + * We can't handle them anymore, since the title menu (and attract + * screens) reset the game state, so just discard them. */ + ClearMessageQueue(); + } + else if( SM == SM_BeginFadingOut ) + { + m_bAllowOptionsMenu = false; + if( OPTIONS_MENU_AVAILABLE && !m_bGoToOptions ) + this->PlayCommand( "HidePressStartForOptions" ); + + this->PostScreenMessage( SM_GoToNextScreen, this->GetTweenTimeLeft() ); + } + else if( SM == SM_GoToNextScreen ) + { + if( !m_bGoToOptions ) + SOUND->StopMusic(); + } + else if( SM == SM_SongChanged ) + { + AfterMusicChange(); + } + else if( SM == SM_SortOrderChanging ) // happens immediately + { + this->PlayCommand( "SortChange" ); + } + else if( SM == SM_GainFocus ) + { + CodeDetector::RefreshCacheItems( CODES ); + } + else if( SM == SM_LoseFocus ) + { + CodeDetector::RefreshCacheItems(); // reset for other screens + } + + ScreenWithMenuElements::HandleScreenMessage( SM ); +} + +void ScreenSelectMusic::MenuStart( const InputEventPlus &input ) +{ + if( input.type != IET_FIRST_PRESS ) + return; + + /* If select is being pressed at the same time, this is probably an attempt + * to change the sort, not to pick a song or difficulty. If it gets here, + * the actual select press was probably hit during a tween and ignored. + * Ignore it. */ + if( input.pn != PLAYER_INVALID && INPUTMAPPER->IsBeingPressed(GAME_BUTTON_SELECT, input.pn) ) + return; + + // Honor locked input for start presses. + if( m_fLockInputSecs > 0 ) + return; + + switch( m_SelectionState ) + { + DEFAULT_FAIL( m_SelectionState ); + case SelectionState_SelectingSong: + // If false, we don't have a selection just yet. + if( !m_MusicWheel.Select() ) + return; + + // a song was selected + if( m_MusicWheel.GetSelectedSong() != NULL ) + { + if(TWO_PART_CONFIRMS_ONLY && SAMPLE_MUSIC_PREVIEW_MODE == SampleMusicPreviewMode_StartToPreview) + { + // start playing the preview music. + g_bSampleMusicWaiting = true; + CheckBackgroundRequests( true ); + } + + const bool bIsNew = PROFILEMAN->IsSongNew( m_MusicWheel.GetSelectedSong() ); + bool bIsHard = false; + FOREACH_HumanPlayer( p ) + { + if( GAMESTATE->m_pCurSteps[p] && GAMESTATE->m_pCurSteps[p]->GetMeter() >= 10 ) + bIsHard = true; + } + + // See if this song is a repeat. + // If we're in event mode, only check the last five songs. + bool bIsRepeat = false; + int i = 0; + if( GAMESTATE->IsEventMode() ) + i = max( 0, int(STATSMAN->m_vPlayedStageStats.size())-5 ); + for( ; i < (int)STATSMAN->m_vPlayedStageStats.size(); ++i ) + if( STATSMAN->m_vPlayedStageStats[i].m_vpPlayedSongs.back() == m_MusicWheel.GetSelectedSong() ) + bIsRepeat = true; + + // Don't complain about repeats if the user didn't get to pick. + if( GAMESTATE->IsAnExtraStageAndSelectionLocked() ) + bIsRepeat = false; + + if( bIsRepeat ) + SOUND->PlayOnceFromAnnouncer( "select music comment repeat" ); + else if( bIsNew ) + SOUND->PlayOnceFromAnnouncer( "select music comment new" ); + else if( bIsHard ) + SOUND->PlayOnceFromAnnouncer( "select music comment hard" ); + else + SOUND->PlayOnceFromAnnouncer( "select music comment general" ); + + /* If we're in event mode, we may have just played a course (putting + * us in course mode). Make sure we're in a single song mode. */ + if( GAMESTATE->IsCourseMode() ) + GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); + } + else if( m_MusicWheel.GetSelectedCourse() != NULL ) + { + SOUND->PlayOnceFromAnnouncer( "select course comment general" ); + + Course *pCourse = m_MusicWheel.GetSelectedCourse(); + ASSERT( pCourse ); + GAMESTATE->m_PlayMode.Set( pCourse->GetPlayMode() ); + + // apply #LIVES + if( pCourse->m_iLives != -1 ) + { + SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Stage, m_LifeType, SongOptions::LIFE_BATTERY ); + SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Stage, m_iBatteryLives, pCourse->m_iLives ); + } + if( pCourse->GetCourseType() == COURSE_TYPE_SURVIVAL) + SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Stage, m_LifeType, SongOptions::LIFE_TIME ); + } + else + { + // We haven't made a selection yet. + return; + } + // I believe this is for those who like pump pro. -aj + MESSAGEMAN->Broadcast("SongChosen"); + + break; + + case SelectionState_SelectingSteps: + { + PlayerNumber pn = input.pn; + bool bInitiatedByMenuTimer = pn == PLAYER_INVALID; + bool bAllOtherHumanPlayersDone = true; + FOREACH_HumanPlayer( p ) + { + if( p == pn ) + continue; + bAllOtherHumanPlayersDone &= m_bStepsChosen[p]; + } + + bool bAllPlayersDoneSelectingSteps = bInitiatedByMenuTimer || bAllOtherHumanPlayersDone; + if(TWO_PART_CONFIRMS_ONLY) + bAllPlayersDoneSelectingSteps = true; + + /* TRICKY: if we have a Routine chart selected, we need to ensure + * the following: + * 1. Both players must select the same Routine steps. + * 2. If the other player picks non-Routine steps, this player + * cannot pick Routine. + * 3. If the other player picked Routine steps, and we pick + * non-Routine steps, the other player's steps must be unselected. + * 4. If time runs out, and both players don't have the same Routine + * chart selected, we need to bump the player with a Routine + * chart selection to a playable chart. + * (Right now, we bump them to Beginner... Can we come up with + * something better?) + */ + + if( !GAMESTATE->IsCourseMode() && GAMESTATE->GetNumSidesJoined() == 2 ) + { + bool bSelectedRoutineSteps[NUM_PLAYERS], bAnySelectedRoutine = false; + bool bSelectedSameSteps = GAMESTATE->m_pCurSteps[PLAYER_1] == GAMESTATE->m_pCurSteps[PLAYER_2]; + + FOREACH_HumanPlayer( p ) + { + const Steps *pSteps = GAMESTATE->m_pCurSteps[p]; + const StepsTypeInfo &sti = GAMEMAN->GetStepsTypeInfo( pSteps->m_StepsType ); + + bSelectedRoutineSteps[p] = sti.m_StepsTypeCategory == StepsTypeCategory_Routine; + bAnySelectedRoutine |= bSelectedRoutineSteps[p]; + } + + if( bAnySelectedRoutine ) + { + /* Timer ran out. If we haven't agreed on steps, move players with + * Routine steps down to Beginner. I'll admit that's annoying, + * but at least they won't lose more stages. */ + if( bInitiatedByMenuTimer && !bSelectedSameSteps ) + { + /* Since m_vpSteps is sorted by Difficulty, the first + * entry should be the easiest. */ + ASSERT( m_vpSteps.size() ); + Steps *pSteps = m_vpSteps[0]; + + FOREACH_PlayerNumber( p ) + { + if( bSelectedRoutineSteps[p] ) + GAMESTATE->m_pCurSteps[p].Set( pSteps ); + } + + break; + } + + // If the steps don't match up, we need to check some more conditions... + if( !bSelectedSameSteps ) + { + const PlayerNumber other = OPPOSITE_PLAYER[pn]; + + if( m_bStepsChosen[other] ) + { + /* Unready the other player if they selected Routine + * steps, but we didn't. */ + if( bSelectedRoutineSteps[other] ) + { + m_bStepsChosen[other] = false; + bAllPlayersDoneSelectingSteps = false; // if the timer ran out, we handled it earlier + + // HACK: send an event to Input to tell it to unready. + InputEventPlus event; + event.MenuI = GAME_BUTTON_SELECT; + event.pn = other; + + this->Input( event ); + } + else if( bSelectedRoutineSteps[pn] ) + { + /* They selected non-Routine steps, so we can't + * select Routine steps. */ + return; + } + } + } + } + } + + if( !bAllPlayersDoneSelectingSteps ) + { + m_bStepsChosen[pn] = true; + m_soundStart.Play(); + + // impldiff: Pump it Up Pro uses "StepsSelected". -aj + Message msg("StepsChosen"); + msg.SetParam( "Player", pn ); + MESSAGEMAN->Broadcast( msg ); + return; + } + } + break; + } + + FOREACH_ENUM( PlayerNumber, p ) + { + if( !TWO_PART_SELECTION || m_SelectionState == SelectionState_SelectingSteps ) + { + if( m_OptionsList[p].IsOpened() ) + m_OptionsList[p].Close(); + } + UpdateSelectButton( p, false ); + } + + m_SelectionState = GetNextSelectionState(); + Message msg( "Start" + SelectionStateToString(m_SelectionState) ); + MESSAGEMAN->Broadcast( msg ); + + m_soundStart.Play(); + + // If the MenuTimer has forced us to move on && TWO_PART_CONFIRMS_ONLY, + // set Selection State to finalized and move on. + if(TWO_PART_CONFIRMS_ONLY) + { + if(m_MenuTimer->GetSeconds() < 1) + { + m_SelectionState = SelectionState_Finalized; + } + } + + if( m_SelectionState == SelectionState_Finalized ) + { + m_MenuTimer->Stop(); + + FOREACH_HumanPlayer( p ) + { + if( !m_bStepsChosen[p] ) + { + m_bStepsChosen[p] = true; + // Don't play start sound. We play it again below on finalized + //m_soundStart.Play(); + + Message lMsg("StepsChosen"); + lMsg.SetParam( "Player", p ); + MESSAGEMAN->Broadcast( lMsg ); + } + } + + if( CommonMetrics::AUTO_SET_STYLE ) + { + // Now that Steps have been chosen, set a Style that can play them. + const Style *pStyle = NULL; + if( GAMESTATE->IsCourseMode() ) + pStyle = GAMESTATE->m_pCurCourse->GetCourseStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined() ); + if( pStyle == NULL ) + { + StepsType stCurrent; + PlayerNumber pn = GAMESTATE->GetMasterPlayerNumber(); + if( GAMESTATE->IsCourseMode() ) + { + ASSERT( GAMESTATE->m_pCurTrail[pn] ); + stCurrent = GAMESTATE->m_pCurTrail[pn]->m_StepsType; + } + else + { + ASSERT( GAMESTATE->m_pCurSteps[pn] ); + stCurrent = GAMESTATE->m_pCurSteps[pn]->m_StepsType; + } + vector vst; + pStyle = GAMEMAN->GetFirstCompatibleStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined(), stCurrent ); + } + GAMESTATE->SetCurrentStyle( pStyle ); + } + + /* If we're currently waiting on song assets, abort all except the music + * and start the music, so if we make a choice quickly before background + * requests come through, the music will still start. */ + g_bCDTitleWaiting = g_bBannerWaiting = false; + m_BackgroundLoader.Abort(); + CheckBackgroundRequests( true ); + + if( OPTIONS_MENU_AVAILABLE ) + { + // show "hold START for options" + this->PlayCommand( "ShowPressStartForOptions" ); + + m_bAllowOptionsMenu = true; + + /* Don't accept a held START for a little while, so it's not + * hit accidentally. Accept an initial START right away, though, + * so we don't ignore deliberate fast presses (which would be + * annoying). */ + this->PostScreenMessage( SM_AllowOptionsMenuRepeat, 0.5f ); + + StartTransitioningScreen( SM_None ); + float fTime = max( SHOW_OPTIONS_MESSAGE_SECONDS, this->GetTweenTimeLeft() ); + this->PostScreenMessage( SM_BeginFadingOut, fTime ); + } + else + { + StartTransitioningScreen( SM_BeginFadingOut ); + } + } + else // !finalized. Set the timer for selecting difficulty and mods. + { + float fSeconds = m_MenuTimer->GetSeconds(); + if( fSeconds < 10 ) + { + m_MenuTimer->SetSeconds( TWO_PART_TIMER_SECONDS ); // was 13 -aj + m_MenuTimer->Start(); + } + } +} + + +void ScreenSelectMusic::MenuBack( const InputEventPlus &input ) +{ + // Handle unselect song (ffff) + // todo: this isn't right at all. -aj + /* + if( m_SelectionState == SelectionState_SelectingSteps && + !m_bStepsChosen[input.pn] && input.MenuI == GAME_BUTTON_BACK && + input.type == IET_FIRST_PRESS ) + { + // if a player has chosen their steps already, don't unchoose song. + FOREACH_HumanPlayer( p ) + if( m_bStepsChosen[p] ) return; + + // and if we get here... + Message msg("SongUnchosen"); + msg.SetParam( "Player", input.pn ); + MESSAGEMAN->Broadcast( msg ); + m_SelectionState = SelectionState_SelectingSong; + return; + } + */ + + m_BackgroundLoader.Abort(); + + Cancel( SM_GoToPrevScreen ); +} + +void ScreenSelectMusic::AfterStepsOrTrailChange( const vector &vpns ) +{ + if(TWO_PART_CONFIRMS_ONLY && m_SelectionState == SelectionState_SelectingSteps) + { + // if TWO_PART_CONFIRMS_ONLY, changing difficulties unsets the song. -aj + m_SelectionState = SelectionState_SelectingSong; + MESSAGEMAN->Broadcast("TwoPartConfirmCanceled"); + } + + FOREACH_CONST( PlayerNumber, vpns, p ) + { + PlayerNumber pn = *p; + ASSERT( GAMESTATE->IsHumanPlayer(pn) ); + + if( GAMESTATE->m_pCurSong ) + { + CLAMP( m_iSelection[pn], 0, m_vpSteps.size()-1 ); + + Song* pSong = GAMESTATE->m_pCurSong; + Steps* pSteps = m_vpSteps.empty()? NULL: m_vpSteps[m_iSelection[pn]]; + + GAMESTATE->m_pCurSteps[pn].Set( pSteps ); + GAMESTATE->m_pCurTrail[pn].Set( NULL ); + + int iScore = 0; + if( pSteps ) + { + const Profile *pProfile = PROFILEMAN->IsPersistentProfile(pn) ? PROFILEMAN->GetProfile(pn) : PROFILEMAN->GetMachineProfile(); + iScore = pProfile->GetStepsHighScoreList(pSong,pSteps).GetTopScore().GetScore(); + } + + m_textHighScore[pn].SetText( ssprintf("%*i", NUM_SCORE_DIGITS, iScore) ); + } + else if( GAMESTATE->m_pCurCourse ) + { + CLAMP( m_iSelection[pn], 0, m_vpTrails.size()-1 ); + + Course* pCourse = GAMESTATE->m_pCurCourse; + Trail* pTrail = m_vpTrails.empty()? NULL: m_vpTrails[m_iSelection[pn]]; + + GAMESTATE->m_pCurSteps[pn].Set( NULL ); + GAMESTATE->m_pCurTrail[pn].Set( pTrail ); + + int iScore = 0; + if( pTrail ) + { + const Profile *pProfile = PROFILEMAN->IsPersistentProfile(pn) ? PROFILEMAN->GetProfile(pn) : PROFILEMAN->GetMachineProfile(); + iScore = pProfile->GetCourseHighScoreList(pCourse,pTrail).GetTopScore().GetScore(); + } + + m_textHighScore[pn].SetText( ssprintf("%*i", NUM_SCORE_DIGITS, iScore) ); + } + else + { + // The numbers shouldn't stay if the current selection is NULL. + m_textHighScore[pn].SetText( NULL_SCORE_STRING ); + } + } +} + +void ScreenSelectMusic::SwitchToPreferredDifficulty() +{ + if( !GAMESTATE->m_pCurCourse ) + { + FOREACH_HumanPlayer( pn ) + { + // Find the closest match to the user's preferred difficulty and StepsType. + int iCurDifference = -1; + int &iSelection = m_iSelection[pn]; + FOREACH_CONST( Steps*, m_vpSteps, s ) + { + int i = s - m_vpSteps.begin(); + + // If the current steps are listed, use them. + if( GAMESTATE->m_pCurSteps[pn] == *s ) + { + iSelection = i; + break; + } + + if( GAMESTATE->m_PreferredDifficulty[pn] != Difficulty_Invalid ) + { + int iDifficultyDifference = abs( (*s)->GetDifficulty() - GAMESTATE->m_PreferredDifficulty[pn] ); + int iStepsTypeDifference = 0; + if( GAMESTATE->m_PreferredStepsType != StepsType_Invalid ) + iStepsTypeDifference = abs( (*s)->m_StepsType - GAMESTATE->m_PreferredStepsType ); + int iTotalDifference = iStepsTypeDifference * NUM_Difficulty + iDifficultyDifference; + + if( iCurDifference == -1 || iTotalDifference < iCurDifference ) + { + iSelection = i; + iCurDifference = iTotalDifference; + } + } + } + + CLAMP( iSelection, 0, m_vpSteps.size()-1 ); + } + } + else + { + FOREACH_HumanPlayer( pn ) + { + // Find the closest match to the user's preferred difficulty. + int iCurDifference = -1; + int &iSelection = m_iSelection[pn]; + FOREACH_CONST( Trail*, m_vpTrails, t ) + { + int i = t - m_vpTrails.begin(); + + // If the current trail is listed, use it. + if( GAMESTATE->m_pCurTrail[pn] == m_vpTrails[i] ) + { + iSelection = i; + break; + } + + if( GAMESTATE->m_PreferredCourseDifficulty[pn] != Difficulty_Invalid && GAMESTATE->m_PreferredStepsType != StepsType_Invalid ) + { + int iDifficultyDifference = abs( (*t)->m_CourseDifficulty - GAMESTATE->m_PreferredCourseDifficulty[pn] ); + int iStepsTypeDifference = abs( (*t)->m_StepsType - GAMESTATE->m_PreferredStepsType ); + int iTotalDifference = iStepsTypeDifference * NUM_CourseDifficulty + iDifficultyDifference; + + if( iCurDifference == -1 || iTotalDifference < iCurDifference ) + { + iSelection = i; + iCurDifference = iTotalDifference; + } + } + } + + CLAMP( iSelection, 0, m_vpTrails.size()-1 ); + } + } + + if( GAMESTATE->DifficultiesLocked() ) + { + FOREACH_HumanPlayer( p ) + m_iSelection[p] = m_iSelection[GAMESTATE->GetMasterPlayerNumber()]; + } +} + +void ScreenSelectMusic::AfterMusicChange() +{ + if( !m_MusicWheel.IsRouletting() ) + m_MenuTimer->Stall(); + + Song* pSong = m_MusicWheel.GetSelectedSong(); + GAMESTATE->m_pCurSong.Set( pSong ); + if( pSong ) + GAMESTATE->m_pPreferredSong = pSong; + + Course* pCourse = m_MusicWheel.GetSelectedCourse(); + GAMESTATE->m_pCurCourse.Set( pCourse ); + if( pCourse ) + GAMESTATE->m_pPreferredCourse = pCourse; + + m_vpSteps.clear(); + m_vpTrails.clear(); + + m_Banner.SetMovingFast( !!m_MusicWheel.IsMoving() ); + + vector m_Artists, m_AltArtists; + + if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) + { + m_sSampleMusicToPlay = ""; + } + m_pSampleMusicTimingData = NULL; + g_sCDTitlePath = ""; + g_sBannerPath = ""; + g_bWantFallbackCdTitle = false; + bool bWantBanner = true; + + static SortOrder s_lastSortOrder = SortOrder_Invalid; + if( GAMESTATE->m_SortOrder != s_lastSortOrder ) + { + // Reload to let Lua metrics have a chance to change the help text. + s_lastSortOrder = GAMESTATE->m_SortOrder; + } + + switch( m_MusicWheel.GetSelectedType() ) + { + case WheelItemDataType_Section: + case WheelItemDataType_Sort: + case WheelItemDataType_Roulette: + case WheelItemDataType_Random: + case WheelItemDataType_Custom: + FOREACH_PlayerNumber( p ) + m_iSelection[p] = -1; + + g_sCDTitlePath = ""; // none + + if( SAMPLE_MUSIC_PREVIEW_MODE == SampleMusicPreviewMode_LastSong ) + { + // HACK: Make random music work in LastSong mode. -aj + if( m_sSampleMusicToPlay == m_sRandomMusicPath ) + { + m_fSampleStartSeconds = 0; + m_fSampleLengthSeconds = -1; + } + } + else + { + m_fSampleStartSeconds = 0; + m_fSampleLengthSeconds = -1; + } + + switch( m_MusicWheel.GetSelectedType() ) + { + case WheelItemDataType_Section: + // reduce scope + { + SortOrder curSort = GAMESTATE->m_SortOrder; + if( curSort == SORT_GROUP) + { + g_sBannerPath = SONGMAN->GetSongGroupBannerPath( m_MusicWheel.GetSelectedSection() ); + } + else + { + bWantBanner = false; // we load it ourself + m_Banner.LoadFromSortOrder(curSort); + } + + if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) + m_sSampleMusicToPlay = m_sSectionMusicPath; + } + break; + case WheelItemDataType_Sort: + bWantBanner = false; // we load it ourself + m_Banner.LoadMode(); + if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) + m_sSampleMusicToPlay = m_sSortMusicPath; + break; + case WheelItemDataType_Roulette: + bWantBanner = false; // we load it ourself + m_Banner.LoadRoulette(); + if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) + m_sSampleMusicToPlay = m_sRouletteMusicPath; + break; + case WheelItemDataType_Random: + bWantBanner = false; // we load it ourself + m_Banner.LoadRandom(); + //if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) + m_sSampleMusicToPlay = m_sRandomMusicPath; + break; + case WheelItemDataType_Custom: + { + bWantBanner = false; // we load it ourself + RString sBannerName = GetMusicWheel()->GetCurWheelItemData( GetMusicWheel()->GetCurrentIndex() )->m_pAction->m_sName.c_str(); + m_Banner.LoadCustom(sBannerName); + if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_LastSong ) + m_sSampleMusicToPlay = m_sSectionMusicPath; + } + break; + default: + ASSERT(0); + } + // override this if the sample music mode wants to. + /* + if(SAMPLE_MUSIC_PREVIEW_MODE == SampleMusicPreviewMode_LastSong) + { + m_sSampleMusicToPlay = pSong->GetMusicPath(); + m_pSampleMusicTimingData = &pSong->m_Timing; + m_fSampleStartSeconds = pSong->m_fMusicSampleStartSeconds; + m_fSampleLengthSeconds = pSong->m_fMusicSampleLengthSeconds; + } + */ + break; + case WheelItemDataType_Song: + case WheelItemDataType_Portal: + // check SampleMusicPreviewMode here. + switch( SAMPLE_MUSIC_PREVIEW_MODE ) + { + case SampleMusicPreviewMode_ScreenMusic: + // play the screen music + m_sSampleMusicToPlay = m_sLoopMusicPath; + m_fSampleStartSeconds = 0; + m_fSampleLengthSeconds = -1; + break; + case SampleMusicPreviewMode_StartToPreview: + // we want to load the sample music, but we don't want to + // actually play it. fall through. -aj + case SampleMusicPreviewMode_Normal: + case SampleMusicPreviewMode_LastSong: // fall through + // play the sample music + m_sSampleMusicToPlay = pSong->GetMusicPath(); + m_pSampleMusicTimingData = &pSong->m_SongTiming; + m_fSampleStartSeconds = pSong->m_fMusicSampleStartSeconds; + m_fSampleLengthSeconds = pSong->m_fMusicSampleLengthSeconds; + break; + default: + ASSERT(0); + } + + SongUtil::GetPlayableSteps( pSong, m_vpSteps ); + + if ( PREFSMAN->m_bShowBanners ) + g_sBannerPath = pSong->GetBannerPath(); + + g_sCDTitlePath = pSong->GetCDTitlePath(); + g_bWantFallbackCdTitle = true; + + SwitchToPreferredDifficulty(); + break; + + case WheelItemDataType_Course: + { + const Course *lCourse = m_MusicWheel.GetSelectedCourse(); + const Style *pStyle = NULL; + if( CommonMetrics::AUTO_SET_STYLE ) + pStyle = pCourse->GetCourseStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined() ); + if( pStyle == NULL ) + pStyle = GAMESTATE->GetCurrentStyle(); + lCourse->GetTrails( m_vpTrails, pStyle->m_StepsType ); + + m_sSampleMusicToPlay = m_sCourseMusicPath; + m_fSampleStartSeconds = 0; + m_fSampleLengthSeconds = -1; + + g_sBannerPath = lCourse->GetBannerPath(); + if( g_sBannerPath.empty() ) + m_Banner.LoadFallback(); + + SwitchToPreferredDifficulty(); + break; + } + default: + ASSERT(0); + } + + m_sprCDTitleFront.UnloadTexture(); + m_sprCDTitleBack.UnloadTexture(); + + // Cancel any previous, incomplete requests for song assets, + // since we need new ones. + m_BackgroundLoader.Abort(); + + g_bCDTitleWaiting = false; + if( !g_sCDTitlePath.empty() || g_bWantFallbackCdTitle ) + { + LOG->Trace( "cache \"%s\"", g_sCDTitlePath.c_str()); + m_BackgroundLoader.CacheFile( g_sCDTitlePath ); // empty OK + g_bCDTitleWaiting = true; + } + + g_bBannerWaiting = false; + if( bWantBanner ) + { + LOG->Trace("LoadFromCachedBanner(%s)",g_sBannerPath .c_str()); + if( m_Banner.LoadFromCachedBanner( g_sBannerPath ) ) + { + /* If the high-res banner is already loaded, just delay before + * loading it, so the low-res one has time to fade in. */ + if( !TEXTUREMAN->IsTextureRegistered( Sprite::SongBannerTexture(g_sBannerPath) ) ) + m_BackgroundLoader.CacheFile( g_sBannerPath ); + + g_bBannerWaiting = true; + } + } + + // Don't stop music if it's already playing the right file. + g_bSampleMusicWaiting = false; + if( !m_MusicWheel.IsRouletting() && SOUND->GetMusicPath() != m_sSampleMusicToPlay ) + { + SOUND->StopMusic(); + // some SampleMusicPreviewModes don't want the sample music immediately. + if( SAMPLE_MUSIC_PREVIEW_MODE != SampleMusicPreviewMode_StartToPreview ) + { + if( !m_sSampleMusicToPlay.empty() ) + g_bSampleMusicWaiting = true; + } + } + + g_StartedLoadingAt.Touch(); + + vector vpns; + FOREACH_HumanPlayer( p ) + vpns.push_back( p ); + + AfterStepsOrTrailChange( vpns ); +} + +void ScreenSelectMusic::OpenOptionsList(PlayerNumber pn) +{ + if( pn != PLAYER_INVALID ) + { + m_OptionsList[pn].Open(); + } +} + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the ScreenSelectMusic. */ +class LunaScreenSelectMusic: public Luna +{ +public: + static int GetGoToOptions( T* p, lua_State *L ) { lua_pushboolean( L, p->GetGoToOptions() ); return 1; } + static int GetMusicWheel( T* p, lua_State *L ) { + p->GetMusicWheel()->PushSelf(L); + return 1; + } + static int OpenOptionsList( T* p, lua_State *L ) { PlayerNumber pn = Enum::Check(L, 1); p->OpenOptionsList(pn); return 0; } + + LunaScreenSelectMusic() + { + ADD_METHOD( GetGoToOptions ); + ADD_METHOD( GetMusicWheel ); + ADD_METHOD( OpenOptionsList ); + } +}; + +LUA_REGISTER_DERIVED_CLASS( ScreenSelectMusic, ScreenWithMenuElements ) +// lua end + +/* + * (c) 2001-2004 Chris Danford + * 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. + */