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];