From 2e8dc57f3d97d30112f4177993580dc17cee43aa Mon Sep 17 00:00:00 2001 From: Andrew Livy Date: Sat, 2 May 2009 15:30:04 +0000 Subject: [PATCH] Adds Dual List Mode to ScreenSelectMaster. This allows for themers to maintain two separate lists depending on number of players joined. Overcomes some problems such as wanting the list to only contain selectable items if using a scroller. Untested with icon cursor and needs optimising still. --- stepmania/Themes/default/metrics.ini | 29 +- stepmania/src/ScreenSelect.cpp | 130 ++++++- stepmania/src/ScreenSelect.h | 4 + stepmania/src/ScreenSelectMaster.cpp | 523 ++++++++++++++++++++++----- stepmania/src/ScreenSelectMaster.h | 13 + 5 files changed, 601 insertions(+), 98 deletions(-) diff --git a/stepmania/Themes/default/metrics.ini b/stepmania/Themes/default/metrics.ini index 2549ec6ca1..20176e74f4 100644 --- a/stepmania/Themes/default/metrics.ini +++ b/stepmania/Themes/default/metrics.ini @@ -120,6 +120,12 @@ IdleTimeoutSeconds=30 IdleTimeoutScreen=THEME:GetMetric("Common","FirstAttractScreen") OutDelay=.2 DoublePressToSelect=false +UseTwoLists=false +OptionOrderBUp="" +OptionOrderBDown="" +OptionOrderBLeft="" +OptionOrderBRight="" +OptionOrderBAuto="" [ScreenExit] Class="ScreenExit" @@ -130,6 +136,13 @@ AllowOperatorMenuButton=false Class="ScreenTitleMenu" Fallback="ScreenTitleMenu" ChoiceNames="1" +ChoiceNamesB="" +OptionOrderBUp="" +OptionOrderBDown="" +OptionOrderBLeft="" +OptionOrderBRight="" +OptionOrderBAuto="" + [ScreenCaution] Fallback="ScreenSplash" @@ -209,6 +222,13 @@ ExplanationPage2Y=0 HeaderOnCommand= HeaderOffCommand= DoublePressToSelect=false +UseTwoLists=false +ChoiceNamesB="" +OptionOrderBUp="" +OptionOrderBDown="" +OptionOrderBLeft="" +OptionOrderBRight="" +OptionOrderBAuto="" [ScreenSelectPlayMode] Fallback="ScreenWithMenuElements" @@ -266,6 +286,12 @@ ExplanationPage2Y=0 HeaderOnCommand= HeaderOffCommand= DoublePressToSelect=false +OptionOrderBUp= +OptionOrderBDown= +OptionOrderBLeft= +OptionOrderBRight= +OptionOrderBAuto= +UseTwoLists= [ScreenSelectMode] Fallback="ScreenSelect" @@ -2656,6 +2682,7 @@ DisabledColor=#606060 DoublePressToSelect=false + # Blank defaults for a few options. IconChoice1SwitchToPage1Command= IconChoice1SwitchToPage2Command= @@ -2946,7 +2973,7 @@ Unlock0020X=SCREEN_CENTER_X-70 Unlock0020Y=SCREEN_CENTER_Y+85 DancePointsZoom=2 -[ScreenHighScores] +[ScreenHighScores]http://clickingandscreaming.com/2009/05/01/youtube-and-bullying-cause-or-solution/ Fallback="ScreenAttract" Class="ScreenHighScores" TimerSeconds=-1 diff --git a/stepmania/src/ScreenSelect.cpp b/stepmania/src/ScreenSelect.cpp index 3c033da8a4..8775701635 100644 --- a/stepmania/src/ScreenSelect.cpp +++ b/stepmania/src/ScreenSelect.cpp @@ -13,6 +13,10 @@ #define CHOICE( s ) THEME->GetMetric (m_sName,ssprintf("Choice%s",s.c_str())) #define IDLE_TIMEOUT_SCREEN THEME->GetMetric (m_sName,"IdleTimeoutScreen") #define UPDATE_ON_MESSAGE THEME->GetMetric (m_sName,"UpdateOnMessage") +#define USE_TWO_LISTS THEME->GetMetricB (m_sName,"UseTwoLists") + +#define CHOICE_NAMESB THEME->GetMetric (m_sName,"ChoiceNamesB") +#define CHOICEB( s ) THEME->GetMetric (m_sName,ssprintf("ChoiceB%s",s.c_str())) void ScreenSelect::Init() { @@ -54,16 +58,55 @@ void ScreenSelect::Init() mc.Load( c, cmd ); m_aGameCommands.push_back( mc ); } + + m_iSelectedList = 0; + if(USE_TWO_LISTS) + { + m_bUsingTwoLists = true; + vector asChoiceNames2; + split( CHOICE_NAMESB, ",", asChoiceNames2, true ); + + for( unsigned c=0; cGetNumSidesJoined() > 1 ) + { + m_iSelectedList = 1; + this->UpdateSelectableChoices(); + } + } + m_timerIdleComment.GetDeltaTime(); m_timerIdleTimeout.GetDeltaTime(); } @@ -111,11 +154,20 @@ void ScreenSelect::Input( const InputEventPlus &input ) if( input.MenuI == GAME_BUTTON_START && input.type == IET_FIRST_PRESS && GAMESTATE->JoinInput(input.pn) ) { - // HACK: Only play start sound for the 2nd player who joins. The - // start sound for the 1st player will be played by ScreenTitleMenu - // when the player makes a selection on the screen. if( GAMESTATE->GetNumSidesJoined() > 1 ) + { + if(USE_TWO_LISTS) + { + m_iSelectedList = 1; + this->UpdateSelectableChoices(); + } + + + // HACK: Only play start sound for the 2nd player who joins. The + // start sound for the 1st player will be played by ScreenTitleMenu + // when the player makes a selection on the screen. SCREENMAN->PlayStartSound(); + } if( !ALLOW_DISABLED_PLAYER_INPUT ) return; // don't let the screen handle the MENU_START press @@ -162,20 +214,72 @@ void ScreenSelect::HandleScreenMessage( const ScreenMessage SM ) if( bAllPlayersChoseTheSame ) { - const GameCommand &gc = m_aGameCommands[iMastersIndex]; - m_sNextScreen = gc.m_sScreen; - if( !gc.m_bInvalid ) - gc.ApplyToAllPlayers(); + if(USE_TWO_LISTS) // using two lists + { + if(m_iSelectedList == 0) // first list is selected + { + const GameCommand &gc = m_aGameCommands[iMastersIndex]; + + m_sNextScreen = gc.m_sScreen; + if( !gc.m_bInvalid ) + gc.ApplyToAllPlayers(); + } + else // second list is selected + { + const GameCommand &gc = m_aGameCommandsB[iMastersIndex]; + + m_sNextScreen = gc.m_sScreen; + if( !gc.m_bInvalid ) + gc.ApplyToAllPlayers(); + + } + + } + else // not using two lists + { + const GameCommand &gc = m_aGameCommands[iMastersIndex]; + m_sNextScreen = gc.m_sScreen; + if( !gc.m_bInvalid ) + gc.ApplyToAllPlayers(); + } } else { - FOREACH_HumanPlayer( p ) + if(USE_TWO_LISTS) // using two lists { - int iIndex = this->GetSelectionIndex(p); - const GameCommand &gc = m_aGameCommands[iIndex]; - m_sNextScreen = gc.m_sScreen; - if( !gc.m_bInvalid ) - gc.Apply( p ); + if(m_iSelectedList == 0) // first list is selected + { + FOREACH_HumanPlayer( p ) + { + int iIndex = this->GetSelectionIndex(p); + const GameCommand &gc = m_aGameCommands[iIndex]; + m_sNextScreen = gc.m_sScreen; + if( !gc.m_bInvalid ) + gc.Apply( p ); + } + } + else + { + FOREACH_HumanPlayer( p ) + { + int iIndex = this->GetSelectionIndex(p); + const GameCommand &gc = m_aGameCommandsB[iIndex]; + m_sNextScreen = gc.m_sScreen; + if( !gc.m_bInvalid ) + gc.Apply( p ); + } + } + } + else // only using a single list + { + FOREACH_HumanPlayer( p ) + { + int iIndex = this->GetSelectionIndex(p); + const GameCommand &gc = m_aGameCommands[iIndex]; + m_sNextScreen = gc.m_sScreen; + if( !gc.m_bInvalid ) + gc.Apply( p ); + } } } diff --git a/stepmania/src/ScreenSelect.h b/stepmania/src/ScreenSelect.h index 0e9ecf4d58..b85e4b5a70 100644 --- a/stepmania/src/ScreenSelect.h +++ b/stepmania/src/ScreenSelect.h @@ -25,7 +25,11 @@ protected: virtual int GetSelectionIndex( PlayerNumber pn ) = 0; virtual void UpdateSelectableChoices() = 0; // derived screens must handle this + unsigned int m_iSelectedList; + bool m_bUsingTwoLists; + vector m_aGameCommands; // derived classes should look here for what choices are available + vector m_aGameCommandsB; // only used if the theme requires a second list vector m_asSubscribedMessages; diff --git a/stepmania/src/ScreenSelectMaster.cpp b/stepmania/src/ScreenSelectMaster.cpp index f5cde6dad5..4c3a0a10dd 100644 --- a/stepmania/src/ScreenSelectMaster.cpp +++ b/stepmania/src/ScreenSelectMaster.cpp @@ -29,6 +29,9 @@ static RString CURSOR_OFFSET_Y_FROM_ICON_NAME( size_t p ) { return ssprintf("Cur /* e.g. "OptionOrderLeft=0:1,1:2,2:3,3:4" */ static RString OPTION_ORDER_NAME( size_t dir ) { return "OptionOrder"+MenuDirToString((MenuDir)dir); } +static RString OPTION_ORDER_NAME_LIST2( size_t dir ) { return "OptionOrderB"+MenuDirToString((MenuDir)dir); } + + REGISTER_SCREEN_CLASS( ScreenSelectMaster ); #define GetActiveElementPlayerNumbers( vpns ) \ @@ -60,6 +63,10 @@ void ScreenSelectMaster::Init() PRE_SWITCH_PAGE_SECONDS.Load( m_sName, "PreSwitchPageSeconds" ); POST_SWITCH_PAGE_SECONDS.Load( m_sName, "PostSwitchPageSeconds" ); OPTION_ORDER.Load( m_sName, OPTION_ORDER_NAME, NUM_MenuDir ); + + OPTION_ORDER2.Load( m_sName, OPTION_ORDER_NAME_LIST2, NUM_MenuDir ); + + WRAP_CURSOR.Load( m_sName, "WrapCursor" ); WRAP_SCROLLER.Load( m_sName, "WrapScroller" ); LOOP_SCROLLER.Load( m_sName, "LoopScroller" ); @@ -102,7 +109,6 @@ void ScreenSelectMaster::Init() FOREACH( PlayerNumber, vpns, p ) m_vsprScroll[*p].resize( m_aGameCommands.size() ); - for( unsigned c=0; c vs; + vs.push_back( "List2 Icon" ); + if( PER_CHOICE_ICON_ELEMENT ) + vs.push_back( "List2 Choice" + mc.m_sName ); + RString sElement = join( " ", vs ); + m_vsprIconB[c].Load( THEME->GetPathG(m_sName,sElement) ); + RString sName = "List2 Icon" "List2 Choice" + mc.m_sName; + m_vsprIconB[c]->SetName( sName ); + if( USE_ICON_METRICS ) + LOAD_ALL_COMMANDS_AND_SET_XY( m_vsprIconB[c] ); + this->AddChild( m_vsprIconB[c] ); + } + + // init scroll + if( SHOW_SCROLLER ) + { + FOREACH( PlayerNumber, vpns, p ) + { + vector vs; + vs.push_back( "List2 Scroll" ); + if( PER_CHOICE_SCROLL_ELEMENT ) + vs.push_back( "List2 Choice" + mc.m_sName ); + if( !SHARED_SELECTION ) + vs.push_back( PLAYER_APPEND_NO_SPACE(*p) ); + RString sElement = join( " ", vs ); + m_vsprScrollB[*p][c].Load( THEME->GetPathG(m_sName,sElement) ); + RString sName = "List2 Scroll" "List2 Choice" + mc.m_sName; + if( !SHARED_SELECTION ) + sName += PLAYER_APPEND_NO_SPACE(*p); + m_vsprScrollB[*p][c]->SetName( sName ); + m_ScrollerB[*p].AddChild( m_vsprScrollB[*p][c] ); + } + } + } + + // init scroll + if( SHOW_SCROLLER ) + { + FOREACH( PlayerNumber, vpns, p ) + { + m_ScrollerB[*p].SetLoop( LOOP_SCROLLER ); + m_ScrollerB[*p].SetNumItemsToDraw( SCROLLER_NUM_ITEMS_TO_DRAW ); + m_ScrollerB[*p].Load2(); + m_ScrollerB[*p].SetTransformFromReference( SCROLLER_TRANSFORM ); + m_ScrollerB[*p].SetSecondsPerItem( SCROLLER_SECONDS_PER_ITEM ); + m_ScrollerB[*p].SetNumSubdivisions( SCROLLER_SUBDIVISIONS ); + m_ScrollerB[*p].SetName( "Scroller"+PLAYER_APPEND_NO_SPACE(*p) ); + LOAD_ALL_COMMANDS_AND_SET_XY( m_ScrollerB[*p] ); + this->AddChild( &m_ScrollerB[*p] ); + } + } + + FOREACH_MenuDir( dir ) + { + const RString order = OPTION_ORDER2.GetValue( dir ); + vector parts; + split( order, ",", parts, true ); + + for( unsigned part = 0; part < parts.size(); ++part ) + { + int from, to; + if( sscanf( parts[part], "%d:%d", &from, &to ) != 2 ) + { + LOG->Warn( "%s::OptionOrderB%s parse error", m_sName.c_str(), MenuDirToString(dir).c_str() ); + continue; + } + + --from; + --to; + + m_mapCurrentChoiceToNextChoiceB[dir][from] = to; + } + + if( m_mapCurrentChoiceToNextChoiceB[dir].empty() ) // Didn't specify any mappings + { + // Fill with reasonable defaults + for( unsigned c = 0; c < m_aGameCommandsB.size(); ++c ) + { + int add; + switch( dir ) + { + case MenuDir_Up: + case MenuDir_Left: + add = -1; + break; + default: + add = +1; + break; + } + + m_mapCurrentChoiceToNextChoiceB[dir][c] = c + add; + /* Always wrap around MenuDir_Auto. */ + if( dir == MenuDir_Auto || (bool)WRAP_CURSOR ) + wrap( m_mapCurrentChoiceToNextChoiceB[dir][c], m_aGameCommandsB.size() ); + else + m_mapCurrentChoiceToNextChoiceB[dir][c] = clamp( m_mapCurrentChoiceToNextChoiceB[dir][c], 0, (int)m_aGameCommandsB.size()-1 ); + } + } + } + } // end of list2 init + + // init scroll if( SHOW_SCROLLER ) { @@ -331,8 +456,8 @@ void ScreenSelectMaster::HandleScreenMessage( const ScreenMessage SM ) MenuStart( iep ); } } - } + } int ScreenSelectMaster::GetSelectionIndex( PlayerNumber pn ) @@ -345,15 +470,62 @@ void ScreenSelectMaster::UpdateSelectableChoices() vector vpns; GetActiveElementPlayerNumbers( vpns ); - - for( unsigned c=0; cPlayCommand( m_aGameCommands[c].IsPlayable()? "Enabled":"Disabled" ); - - FOREACH( PlayerNumber, vpns, p ) - if( m_vsprScroll[*p][c].IsLoaded() ) - m_vsprScroll[*p][c]->PlayCommand( m_aGameCommands[c].IsPlayable()? "Enabled":"Disabled" ); + for( unsigned c=0; cPlayCommand( "List2Enabled" ); + } + + FOREACH( PlayerNumber, vpns, p ) + if( m_vsprScroll[*p][c].IsLoaded() ) + { + m_vsprScroll[*p][c]->PlayCommand( "List2Enabled" ); + } + } + + for( unsigned c=0; cPlayCommand( "List2Enabled" ); + m_vsprIconB[c]->PlayCommand( m_aGameCommandsB[c].IsPlayable()? "Enabled":"Disabled" ); + + // we will make element 0 the new selection, so it will gain focus + if(c==0) + m_vsprIconB[c]->PlayCommand( "GainFocus" ); + else // everything else loses focus + m_vsprIconB[c]->PlayCommand( "LoseFocus" ); + } + + FOREACH( PlayerNumber, vpns, p ) + if( m_vsprScrollB[*p][c].IsLoaded() ) + { + m_vsprScrollB[*p][c]->PlayCommand( "List2Enabled" ); + m_vsprScrollB[*p][c]->PlayCommand( m_aGameCommandsB[c].IsPlayable()? "Enabled":"Disabled" ); + + if(c==0) + m_vsprScrollB[*p][c]->PlayCommand( "GainFocus" ); + else // everything else loses focus + m_vsprScrollB[*p][c]->PlayCommand( "LoseFocus" ); + + } + } + } + else + { + + for( unsigned c=0; cPlayCommand( m_aGameCommands[c].IsPlayable()? "Enabled":"Disabled" ); + + FOREACH( PlayerNumber, vpns, p ) + if( m_vsprScroll[*p][c].IsLoaded() ) + m_vsprScroll[*p][c]->PlayCommand( m_aGameCommands[c].IsPlayable()? "Enabled":"Disabled" ); + } } /* @@ -363,15 +535,36 @@ void ScreenSelectMaster::UpdateSelectableChoices() * If any options are playable, make sure one is selected. */ FOREACH_HumanPlayer( p ) - if( !m_aGameCommands[m_iChoice[p]].IsPlayable() ) - Move( p, MenuDir_Auto ); + { + if(m_bUsingTwoLists && m_iSelectedList == 1) + { + m_iChoice[p] = 0; // reset their choice position since we are moving to the new list + if( !m_aGameCommandsB[m_iChoice[p]].IsPlayable() ) + Move( p, MenuDir_Auto ); + } + else + { + if( !m_aGameCommands[m_iChoice[p]].IsPlayable() ) + Move( p, MenuDir_Auto ); + } + } } bool ScreenSelectMaster::AnyOptionsArePlayable() const { - for( unsigned i = 0; i < m_aGameCommands.size(); ++i ) - if( m_aGameCommands[i].IsPlayable() ) - return true; + // if they're using the second list + if(m_bUsingTwoLists && m_iSelectedList == 1) + { + for( unsigned i = 0; i < m_aGameCommandsB.size(); ++i ) + if( m_aGameCommandsB[i].IsPlayable() ) + return true; + } + else + { + for( unsigned i = 0; i < m_aGameCommands.size(); ++i ) + if( m_aGameCommands[i].IsPlayable() ) + return true; + } return false; } @@ -384,18 +577,38 @@ bool ScreenSelectMaster::Move( PlayerNumber pn, MenuDir dir ) int iSwitchToIndex = m_iChoice[pn]; set seen; try_again: - map::const_iterator iter = m_mapCurrentChoiceToNextChoice[dir].find( iSwitchToIndex ); - if( iter != m_mapCurrentChoiceToNextChoice[dir].end() ) - iSwitchToIndex = iter->second; - if( iSwitchToIndex < 0 || iSwitchToIndex >= (int) m_aGameCommands.size() ) // out of choice range - return false; // can't go that way - if( seen.find(iSwitchToIndex) != seen.end() ) - return false; // went full circle and none found - seen.insert( iSwitchToIndex ); + // moving list 2 + if(m_bUsingTwoLists && m_iSelectedList == 1) + { + map::const_iterator iter = m_mapCurrentChoiceToNextChoiceB[dir].find( iSwitchToIndex ); + if( iter != m_mapCurrentChoiceToNextChoiceB[dir].end() ) + iSwitchToIndex = iter->second; - if( !m_aGameCommands[iSwitchToIndex].IsPlayable() ) - goto try_again; + if( iSwitchToIndex < 0 || iSwitchToIndex >= (int) m_aGameCommandsB.size() ) // out of choice range + return false; // can't go that way + if( seen.find(iSwitchToIndex) != seen.end() ) + return false; // went full circle and none found + seen.insert( iSwitchToIndex ); + + if( !m_aGameCommandsB[iSwitchToIndex].IsPlayable() ) + goto try_again; + } + else + { + map::const_iterator iter = m_mapCurrentChoiceToNextChoice[dir].find( iSwitchToIndex ); + if( iter != m_mapCurrentChoiceToNextChoice[dir].end() ) + iSwitchToIndex = iter->second; + + if( iSwitchToIndex < 0 || iSwitchToIndex >= (int) m_aGameCommands.size() ) // out of choice range + return false; // can't go that way + if( seen.find(iSwitchToIndex) != seen.end() ) + return false; // went full circle and none found + seen.insert( iSwitchToIndex ); + + if( !m_aGameCommands[iSwitchToIndex].IsPlayable() ) + goto try_again; + } return ChangeSelection( pn, dir, iSwitchToIndex ); } @@ -624,16 +837,75 @@ bool ScreenSelectMaster::ChangeSelection( PlayerNumber pn, MenuDir dir, int iNew bNewAlreadyHadFocus |= m_iChoice[p2] == iNewChoice; } - + //todo: condense this section if(DOUBLE_PRESS_TO_SELECT) { - // this player is currently on a single press, which they are cancelling - if(m_bDoubleChoice[pn]) + if(m_bUsingTwoLists && m_iSelectedList == 1) + { + // this player is currently on a single press, which they are cancelling + if(m_bDoubleChoice[pn]) + { + if( !bOldStillHasFocus ) + m_vsprIconB[iOldChoice]->PlayCommand( "LostSelectedLoseFocus" ); + if( !bNewAlreadyHadFocus ) + m_vsprIconB[iNewChoice]->PlayCommand( "LostSelectedGainFocus" ); + } + else + { + if( !bOldStillHasFocus ) + m_vsprIconB[iOldChoice]->PlayCommand( "LoseFocus" ); + if( !bNewAlreadyHadFocus ) + m_vsprIconB[iNewChoice]->PlayCommand( "GainFocus" ); + } + } + else + { + if(m_bUsingTwoLists && m_iSelectedList == 1) + { + // this player is currently on a single press, which they are cancelling + if(m_bDoubleChoice[pn]) + { + if( !bOldStillHasFocus ) + m_vsprIcon[iOldChoice]->PlayCommand( "LostSelectedLoseFocus" ); + if( !bNewAlreadyHadFocus ) + m_vsprIcon[iNewChoice]->PlayCommand( "LostSelectedGainFocus" ); + } + else + { + if( !bOldStillHasFocus ) + m_vsprIcon[iOldChoice]->PlayCommand( "LoseFocus" ); + if( !bNewAlreadyHadFocus ) + m_vsprIcon[iNewChoice]->PlayCommand( "GainFocus" ); + } + } + else + { + // this player is currently on a single press, which they are cancelling + if(m_bDoubleChoice[pn]) + { + if( !bOldStillHasFocus ) + m_vsprIcon[iOldChoice]->PlayCommand( "LostSelectedLoseFocus" ); + if( !bNewAlreadyHadFocus ) + m_vsprIcon[iNewChoice]->PlayCommand( "LostSelectedGainFocus" ); + } + else + { + if( !bOldStillHasFocus ) + m_vsprIcon[iOldChoice]->PlayCommand( "LoseFocus" ); + if( !bNewAlreadyHadFocus ) + m_vsprIcon[iNewChoice]->PlayCommand( "GainFocus" ); + } + } + } + } + else // not using double selection + { + if(m_bUsingTwoLists && m_iSelectedList == 1) { if( !bOldStillHasFocus ) - m_vsprIcon[iOldChoice]->PlayCommand( "LoseFocus" ); + m_vsprIconB[iOldChoice]->PlayCommand( "LoseFocus" ); if( !bNewAlreadyHadFocus ) - m_vsprIcon[iNewChoice]->PlayCommand( "GainFocus" ); + m_vsprIconB[iNewChoice]->PlayCommand( "GainFocus" ); } else { @@ -643,14 +915,6 @@ bool ScreenSelectMaster::ChangeSelection( PlayerNumber pn, MenuDir dir, int iNew m_vsprIcon[iNewChoice]->PlayCommand( "GainFocus" ); } } - else // not using double selection - { - - if( !bOldStillHasFocus ) - m_vsprIcon[iOldChoice]->PlayCommand( "LostSelectedLoseFocus" ); - if( !bNewAlreadyHadFocus ) - m_vsprIcon[iNewChoice]->PlayCommand( "LostSelectedGainFocus" ); - } } if( SHOW_CURSOR ) @@ -661,26 +925,51 @@ bool ScreenSelectMaster::ChangeSelection( PlayerNumber pn, MenuDir dir, int iNew if( SHOW_SCROLLER ) { - ActorScroller &scroller = SHARED_SELECTION ? m_Scroller[0] : m_Scroller[*p]; - vector &vScroll = SHARED_SELECTION ? m_vsprScroll[0] : m_vsprScroll[*p]; - if( WRAP_SCROLLER ) + + ActorScroller &scroller = (m_bUsingTwoLists && m_iSelectedList == 1)?(SHARED_SELECTION ? m_ScrollerB[0] : m_ScrollerB[*p]):(SHARED_SELECTION ? m_Scroller[0] : m_Scroller[*p]); + vector &vScroll = (m_bUsingTwoLists && m_iSelectedList == 1)?(SHARED_SELECTION ? m_vsprScrollB[0] : m_vsprScrollB[*p]):(SHARED_SELECTION ? m_vsprScroll[0] : m_vsprScroll[*p]); + + // second list + if(m_bUsingTwoLists && m_iSelectedList == 1) { - // HACK: We can't tell from the option orders whether or not we wrapped. - // For now, assume that the order is increasing left to right. - int iPressedDir = (dir == MenuDir_Left) ? -1 : +1; - int iActualDir = (iOldChoice < iNewChoice) ? +1 : -1; - - if( iPressedDir != iActualDir ) // wrapped + if( WRAP_SCROLLER ) { - float fItem = scroller.GetCurrentItem(); - int iNumChoices = m_aGameCommands.size(); - fItem += iActualDir * iNumChoices; - scroller.SetCurrentAndDestinationItem( fItem ); - } - } + // HACK: We can't tell from the option orders whether or not we wrapped. + // For now, assume that the order is increasing left to right. + int iPressedDir = (dir == MenuDir_Left) ? -1 : +1; + int iActualDir = (iOldChoice < iNewChoice) ? +1 : -1; - scroller.SetDestinationItem( (float)iNewChoice ); - + if( iPressedDir != iActualDir ) // wrapped + { + float fItem = scroller.GetCurrentItem(); + int iNumChoices = m_aGameCommandsB.size(); + fItem += iActualDir * iNumChoices; + scroller.SetCurrentAndDestinationItem( fItem ); + } + } + + scroller.SetDestinationItem( (float)iNewChoice ); + } + else + { + if( WRAP_SCROLLER ) + { + // HACK: We can't tell from the option orders whether or not we wrapped. + // For now, assume that the order is increasing left to right. + int iPressedDir = (dir == MenuDir_Left) ? -1 : +1; + int iActualDir = (iOldChoice < iNewChoice) ? +1 : -1; + + if( iPressedDir != iActualDir ) // wrapped + { + float fItem = scroller.GetCurrentItem(); + int iNumChoices = m_aGameCommands.size(); + fItem += iActualDir * iNumChoices; + scroller.SetCurrentAndDestinationItem( fItem ); + } + } + + scroller.SetDestinationItem( (float)iNewChoice ); + } // using double selections if(DOUBLE_PRESS_TO_SELECT) { @@ -783,15 +1072,23 @@ void ScreenSelectMaster::MenuStart( const InputEventPlus &input ) if(SHOW_SCROLLER) { - vector &vScroll = SHARED_SELECTION ? m_vsprScroll[0] : m_vsprScroll[pn]; - vScroll[m_iChoice[pn]]->PlayCommand( "InitialSelection" ); + if(m_bUsingTwoLists && m_iSelectedList == 1) + { + vector &vScroll = SHARED_SELECTION ? m_vsprScrollB[0] : m_vsprScrollB[pn]; + vScroll[m_iChoice[pn]]->PlayCommand( "InitialSelection" ); + } + else + { + vector &vScroll = SHARED_SELECTION ? m_vsprScroll[0] : m_vsprScroll[pn]; + vScroll[m_iChoice[pn]]->PlayCommand( "InitialSelection" ); + } } return; } - const GameCommand &mc = m_aGameCommands[m_iChoice[pn]]; + const GameCommand &mc = (m_bUsingTwoLists && m_iSelectedList == 1)?m_aGameCommandsB[m_iChoice[pn]]:m_aGameCommands[m_iChoice[pn]]; /* If no options are playable, then we're just waiting for one to become available. * If any options are playable, then the selection must be playable. */ @@ -852,26 +1149,55 @@ void ScreenSelectMaster::TweenOnScreen() if( SHOW_ICON ) { - for( unsigned c=0; cPlayCommand( (int(c) == m_iChoice[0])? "GainFocus":"LoseFocus" ); - m_vsprIcon[c]->FinishTweening(); + for( unsigned c=0; cPlayCommand( (int(c) == m_iChoice[0])? "GainFocus":"LoseFocus" ); + m_vsprIconB[c]->FinishTweening(); + } + } + else + { + for( unsigned c=0; cPlayCommand( (int(c) == m_iChoice[0])? "GainFocus":"LoseFocus" ); + m_vsprIcon[c]->FinishTweening(); + } } } if( SHOW_SCROLLER ) { - FOREACH( PlayerNumber, vpns, p ) + if(m_bUsingTwoLists && m_iSelectedList == 1) { - // Play Gain/LoseFocus before playing the on command. Gain/Lose will - // often stop tweening, which ruins the OnCommand. - for( unsigned c=0; cPlayCommand( int(c) == m_iChoice[*p]? "GainFocus":"LoseFocus" ); - m_vsprScroll[*p][c]->FinishTweening(); - } + // Play Gain/LoseFocus before playing the on command. Gain/Lose will + // often stop tweening, which ruins the OnCommand. + for( unsigned c=0; cPlayCommand( int(c) == m_iChoice[*p]? "GainFocus":"LoseFocus" ); + m_vsprScrollB[*p][c]->FinishTweening(); + } - m_Scroller[*p].SetCurrentAndDestinationItem( (float)m_iChoice[*p] ); + m_ScrollerB[*p].SetCurrentAndDestinationItem( (float)m_iChoice[*p] ); + } + } + else + { + FOREACH( PlayerNumber, vpns, p ) + { + // Play Gain/LoseFocus before playing the on command. Gain/Lose will + // often stop tweening, which ruins the OnCommand. + for( unsigned c=0; cPlayCommand( int(c) == m_iChoice[*p]? "GainFocus":"LoseFocus" ); + m_vsprScroll[*p][c]->FinishTweening(); + } + + m_Scroller[*p].SetCurrentAndDestinationItem( (float)m_iChoice[*p] ); + } } } @@ -894,25 +1220,54 @@ void ScreenSelectMaster::TweenOffScreen() vector vpns; GetActiveElementPlayerNumbers( vpns ); - for( unsigned c=0; cPlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" ); - - if( SHOW_SCROLLER ) - { + bool bSelectedByEitherPlayer = false; FOREACH( PlayerNumber, vpns, p ) - m_vsprScroll[*p][c]->PlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" ); + { + if( m_iChoice[*p] == (int)c ) + bSelectedByEitherPlayer = true; + } + + if( SHOW_ICON ) + m_vsprIconB[c]->PlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" ); + + if( SHOW_SCROLLER ) + { + FOREACH( PlayerNumber, vpns, p ) + m_vsprScrollB[*p][c]->PlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" ); + } + } + } + else + { + for( unsigned c=0; cPlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" ); + + if( SHOW_SCROLLER ) + { + FOREACH( PlayerNumber, vpns, p ) + m_vsprScroll[*p][c]->PlayCommand( bSelectedByEitherPlayer? "OffFocused":"OffUnfocused" ); + } } } } diff --git a/stepmania/src/ScreenSelectMaster.h b/stepmania/src/ScreenSelectMaster.h index 346544f07b..9c7de7d515 100644 --- a/stepmania/src/ScreenSelectMaster.h +++ b/stepmania/src/ScreenSelectMaster.h @@ -23,6 +23,7 @@ class ScreenSelectMaster : public ScreenSelect { public: ScreenSelectMaster(); + //~ScreenSelectMaster(); virtual void Init(); virtual RString GetDefaultChoice(); virtual void BeginScreen(); @@ -44,6 +45,7 @@ protected: Page GetPage( int iChoiceIndex ) const; Page GetCurrentPage() const; + ThemeMetric USE_TWO_SCROLLERS; ThemeMetric DOUBLE_PRESS_TO_SELECT; ThemeMetric SHOW_ICON; ThemeMetric SHOW_SCROLLER; @@ -57,6 +59,7 @@ protected: ThemeMetric PRE_SWITCH_PAGE_SECONDS; ThemeMetric POST_SWITCH_PAGE_SECONDS; ThemeMetric1D OPTION_ORDER; + ThemeMetric1D OPTION_ORDER2; ThemeMetric WRAP_CURSOR; ThemeMetric WRAP_SCROLLER; ThemeMetric LOOP_SCROLLER; @@ -69,6 +72,8 @@ protected: ThemeMetric DEFAULT_CHOICE; map m_mapCurrentChoiceToNextChoice[NUM_MenuDir]; + map m_mapCurrentChoiceToNextChoiceB[NUM_MenuDir]; // if using two lists + virtual int GetSelectionIndex( PlayerNumber pn ); virtual void UpdateSelectableChoices(); @@ -87,9 +92,17 @@ protected: AutoActor m_sprMore[NUM_Page]; // icon is the shared, per-choice piece vector m_vsprIcon; + vector m_vsprIconB; // used if the user wants two lists + // preview is per-player, per-choice piece vector m_vsprScroll[NUM_PLAYERS]; + vector m_vsprScrollB[NUM_PLAYERS]; // used if the user wants two lists + + ActorScroller m_Scroller[NUM_PLAYERS]; + ActorScroller m_ScrollerB[NUM_PLAYERS]; // used if the user wants two lists + + // cursor is the per-player, shared by all choices AutoActor m_sprCursor[NUM_PLAYERS];