diff --git a/stepmania/src/Screen.cpp b/stepmania/src/Screen.cpp index 873a36664e..34c41a90d9 100644 --- a/stepmania/src/Screen.cpp +++ b/stepmania/src/Screen.cpp @@ -297,6 +297,7 @@ void Screen::ClearMessageQueue( const ScreenMessage SM ) #include "ScreenExit.h" #include "ScreenAttract.h" #include "ScreenReloadSongs.h" +#include "ScreenOptionsMaster.h" Screen* Screen::Create( CString sClassName ) { @@ -400,6 +401,7 @@ Screen* Screen::Create( CString sClassName ) IF_RETURN( ScreenProfileOptions ); IF_RETURN( ScreenExit ); IF_RETURN( ScreenReloadSongs ); + IF_RETURN( ScreenOptionsMaster ); RageException::Throw( "Invalid Screen class name '%s'", sClassName.c_str() ); } diff --git a/stepmania/src/ScreenOptionsMaster.cpp b/stepmania/src/ScreenOptionsMaster.cpp new file mode 100644 index 0000000000..c5785e5af6 --- /dev/null +++ b/stepmania/src/ScreenOptionsMaster.cpp @@ -0,0 +1,440 @@ +#include "global.h" + +#include "ScreenOptionsMaster.h" +#include "RageException.h" +#include "RageUtil.h" +#include "RageLog.h" +#include "ThemeManager.h" +#include "GameState.h" +#include "ScreenManager.h" +#include "NoteSkinManager.h" +#include "Course.h" +#include "Steps.h" +#include "StyleDef.h" +#include "song.h" +#include "SongManager.h" +#include "Character.h" +#include "ScreenOptionsMasterPrefs.h" + +#define NUM_ROWS THEME->GetMetricI(m_sName,"NumRows") +#define TOGETHER THEME->GetMetricI(m_sName,"Together") +#define EXPLANATIONS THEME->GetMetricB(m_sName,"Explanations") +#define ROW_LINE(i) THEME->GetMetric (m_sName,ssprintf("Line%i",(i+1))) + +#define ENTRY(s) THEME->GetMetric ("ScreenOptionsMasterEntries",s) +#define ENTRY_MODE(s,i) THEME->GetMetric ("ScreenOptionsMasterEntries",ssprintf("%s,%i",(s).c_str(),(i+1))) +#define ENTRY_NAME(s,i) THEME->GetMetric ("ScreenOptionsMasterEntries",ssprintf("%sName,%i",(s).c_str(),(i+1))) +#define ENTRY_DEFAULT(s) THEME->GetMetric ("ScreenOptionsMasterEntries",(s) + "Default") +#define NEXT_SCREEN THEME->GetMetric (m_sName,"NextScreen") +// #define NEXT_SCREEN( play_mode ) THEME->GetMetric (m_sName,"NextScreen"+Capitalize(PlayModeToString(play_mode))) +#define PREV_SCREEN THEME->GetMetric (m_sName,"PrevScreen") +// #define PREV_SCREEN( play_mode ) THEME->GetMetric (m_sName,"PrevScreen"+Capitalize(PlayModeToString(play_mode))) + +#define BEGINNER_DESCRIPTION THEME->GetMetric ("ScreenOptionsMasterEntries","Beginner") +#define EASY_DESCRIPTION THEME->GetMetric ("ScreenOptionsMasterEntries","Easy") +#define MEDIUM_DESCRIPTION THEME->GetMetric ("ScreenOptionsMasterEntries","Medium") +#define HARD_DESCRIPTION THEME->GetMetric ("ScreenOptionsMasterEntries","Hard") +#define CHALLENGE_DESCRIPTION THEME->GetMetric ("ScreenOptionsMasterEntries","Challenge") + +CString ScreenOptionsMaster::ConvertParamToThemeDifficulty( const CString &in ) const +{ + switch( StringToDifficulty(in) ) + { + case DIFFICULTY_BEGINNER: return BEGINNER_DESCRIPTION; + case DIFFICULTY_EASY: return EASY_DESCRIPTION; + case DIFFICULTY_MEDIUM: return MEDIUM_DESCRIPTION; + case DIFFICULTY_HARD: return HARD_DESCRIPTION; + case DIFFICULTY_CHALLENGE: return CHALLENGE_DESCRIPTION; + default: return in; // something else + } +} + +/* Add the list named "ListName" to the given row/handler. */ +void ScreenOptionsMaster::SetList( OptionRow &row, OptionRowHandler &hand, CString ListName, CString &TitleOut ) +{ + hand.type = ROW_LIST; + + TitleOut = ListName; + if( !ListName.CompareNoCase("noteskins") ) + { + hand.Default.Init(); /* none */ + row.bOneChoiceForAllPlayers = false; + + CStringArray arraySkinNames; + NOTESKIN->GetNoteSkinNames( arraySkinNames ); + for( unsigned skin=0; skin 1 ) + row.bOneChoiceForAllPlayers = !asParts[1].CompareNoCase("together"); + + for( int col = 0; col < NumCols; ++col ) + { + ModeChoice mc; + mc.Load( 0, ENTRY_MODE(ListName, col) ); + + if( !mc.IsPlayable() ) + continue; + + hand.ListEntries.push_back( mc ); + row.choices.push_back( ENTRY_NAME(ListName, col) ); + } +} + +/* Add a list of difficulties/edits to the given row/handler. */ +void ScreenOptionsMaster::SetStep( OptionRow &row, OptionRowHandler &hand ) +{ + hand.type = ROW_STEP; + row.bOneChoiceForAllPlayers = false; + + // fill in difficulty names + if( GAMESTATE->m_bEditing ) + { + row.choices.push_back( "" ); + } + else if( GAMESTATE->m_pCurCourse ) // playing a course + { + row.bOneChoiceForAllPlayers = true; + row.choices.push_back( "REGULAR" ); + if( GAMESTATE->m_pCurCourse->HasDifficult( GAMESTATE->GetCurrentStyleDef()->m_StepsType ) ) + row.choices.push_back( "DIFFICULT" ); + } + else if( GAMESTATE->m_pCurSong ) // playing a song + { + vector vNotes; + GAMESTATE->m_pCurSong->GetSteps( vNotes, GAMESTATE->GetCurrentStyleDef()->m_StepsType ); + SortNotesArrayByDifficulty( vNotes ); + for( unsigned i=0; iGetDescription(); + s.MakeUpper(); + + // convert to theme-defined values + s = ConvertParamToThemeDifficulty(s); + + row.choices.push_back( s ); + } + } + else + { + row.choices.push_back( "N/A" ); + } +} + + +/* Add the given configuration value to the given row/handler. */ +void ScreenOptionsMaster::SetConf( OptionRow &row, OptionRowHandler &hand, CString param, CString &TitleOut ) +{ + /* Configuration values are never per-player. */ + row.bOneChoiceForAllPlayers = true; + hand.type = ROW_CONFIG; + + hand.opt = FindConfOption( param ); + if( hand.opt == NULL ) + RageException::Throw( "Invalid Conf type \"%s\"", param.c_str() ); + + for( unsigned i = 0; i < hand.opt->names.size(); ++i ) + row.choices.push_back( hand.opt->names[i] ); + + TitleOut = hand.opt->name; +} + +/* Add a list of available characters to the given row/handler. */ +void ScreenOptionsMaster::SetCharacter( OptionRow &row, OptionRowHandler &hand ) +{ + row.bOneChoiceForAllPlayers = false; + row.choices.push_back( "OFF" ); + vector apCharacters; + GAMESTATE->GetCharacters( apCharacters ); + for( unsigned i=0; im_sName; + s.MakeUpper(); + row.choices.push_back( s ); + } +} + +ScreenOptionsMaster::ScreenOptionsMaster( CString sClassName ): + ScreenOptions( sClassName ) +{ + m_OptionRowAlloc = new OptionRow[NUM_ROWS]; + for( int i = 0; i < NUM_ROWS; ++i ) + { + OptionRow &row = m_OptionRowAlloc[i]; + + CStringArray asParts; + split( ROW_LINE(i), ";", asParts ); + if( asParts.size() < 1 ) + RageException::Throw( "Parse error in %s::Line%i", m_sName.c_str(), i+1 ); + + OptionRowHandler hand; + for( unsigned part = 0; part < asParts.size(); ++part) + { + CStringArray asBits; + split( asParts[part], ",", asBits ); + + const CString name = asBits[0]; + const CString param = asBits.size() > 1? asBits[1]: ""; + + CString Title = ""; + if( !name.CompareNoCase("title") ) + row.name = param; + + else if( !name.CompareNoCase("list") ) + { + SetList( row, hand, param, Title ); + } + else if( !name.CompareNoCase("steps") ) + { + SetStep( row, hand ); + Title = "Steps"; + } + else if( !name.CompareNoCase("conf") ) + { + SetConf( row, hand, param, Title ); + } + else if( !name.CompareNoCase("characters") ) + { + SetCharacter( row, hand ); + Title = "Charac::-ter"; + } + else + RageException::Throw( "Unexpected type '%s' in %s::Line%i", name.c_str(), m_sName.c_str(), i ); + if( row.name == "" ) + row.name = Title; + + } + OptionRowHandlers.push_back( hand ); + } + + ASSERT( (int) OptionRowHandlers.size() == NUM_ROWS ); + + InputMode im = (InputMode) TOGETHER; + Init( im, m_OptionRowAlloc, NUM_ROWS, EXPLANATIONS ); +} + +ScreenOptionsMaster::~ScreenOptionsMaster() +{ + delete m_OptionRowAlloc; +} + +int ScreenOptionsMaster::ImportOption( const OptionRow &row, const OptionRowHandler &hand, int pn ) +{ + /* Figure out which selection is the default. */ + switch( hand.type ) + { + case ROW_LIST: + { + for( unsigned e = 0; e < hand.ListEntries.size(); ++e ) + { + const ModeChoice &mc = hand.ListEntries[e]; + if( row.bOneChoiceForAllPlayers ) + { + if( mc.DescribesCurrentModeForAllPlayers() ) + return e; + } else { + if( mc.DescribesCurrentMode( (PlayerNumber) pn) ) + return e; + } + } + + return 0; + } + case ROW_STEP: + if( GAMESTATE->m_bEditing ) + return 0; + + if( GAMESTATE->m_pCurCourse ) // playing a course + { + if( GAMESTATE->m_bDifficultCourses && + GAMESTATE->m_pCurCourse->HasDifficult( GAMESTATE->GetCurrentStyleDef()->m_StepsType ) ) + return 1; + return 0; + } + + if( GAMESTATE->m_pCurSong ) // playing a song + { + vector vNotes; + GAMESTATE->m_pCurSong->GetSteps( vNotes, GAMESTATE->GetCurrentStyleDef()->m_StepsType ); + SortNotesArrayByDifficulty( vNotes ); + for( unsigned i=0; im_pCurNotes[pn] == vNotes[i] ) + return i; + } + } + + return 0; + case ROW_CHARACTER: + { + vector apCharacters; + GAMESTATE->GetCharacters( apCharacters ); + for( unsigned i=0; im_pCurCharacters[pn] == apCharacters[i] ) + return i+1; + return 0; + } + + case ROW_CONFIG: + return hand.opt->Get(); + + default: + ASSERT(0); + return 0; + } +} + +void ScreenOptionsMaster::ImportOptions() +{ + for( unsigned i = 0; i < OptionRowHandlers.size(); ++i ) + { + const OptionRowHandler &hand = OptionRowHandlers[i]; + const OptionRow &row = m_OptionRowAlloc[i]; + + if( row.bOneChoiceForAllPlayers ) + { + int col = ImportOption( row, hand, 0 ); + m_iSelectedOption[0][i] = col; + } + else + for( int pn=0; pnIsHumanPlayer(pn) ) + continue; + + int col = ImportOption( row, hand, pn ); + m_iSelectedOption[pn][i] = col; + } + } +} + +void ScreenOptionsMaster::ExportOption( const OptionRow &row, const OptionRowHandler &hand, int pn, int sel ) +{ + /* Figure out which selection is the default. */ + switch( hand.type ) + { + case ROW_LIST: + { + const ModeChoice &mc = hand.ListEntries[sel]; + + hand.Default.Apply( (PlayerNumber)pn ); + mc.Apply( (PlayerNumber)pn ); + if( mc.m_sScreen != "" ) + m_NextScreen = mc.m_sScreen; + } + break; + + case ROW_CONFIG: + hand.opt->Put( sel ); + break; + + case ROW_CHARACTER: + if( sel == 0 ) + GAMESTATE->m_pCurCharacters[pn] = NULL; + else + { + vector apCharacters; + GAMESTATE->GetCharacters( apCharacters ); + GAMESTATE->m_pCurCharacters[pn] = apCharacters[sel - 1]; + } + break; + + case ROW_STEP: + if( GAMESTATE->m_bEditing ) + { + // do nothing + } + else if( GAMESTATE->m_pCurCourse ) // playing a course + { + if( sel == 1 ) + { + GAMESTATE->m_bDifficultCourses = true; + LOG->Trace("ScreenPlayerOptions: Using difficult course"); + } + else + { + GAMESTATE->m_bDifficultCourses = false; + LOG->Trace("ScreenPlayerOptions: Using normal course"); + } + } + else if( GAMESTATE->m_pCurSong ) // playing a song + { + vector vNotes; + GAMESTATE->m_pCurSong->GetSteps( vNotes, GAMESTATE->GetCurrentStyleDef()->m_StepsType ); + SortNotesArrayByDifficulty( vNotes ); + GAMESTATE->m_pCurNotes[pn] = vNotes[ sel ]; + } + + break; + default: + ASSERT(0); + break; + } +} + +void ScreenOptionsMaster::ExportOptions() +{ + m_NextScreen = ""; + + for( unsigned i = 0; i < OptionRowHandlers.size(); ++i ) + { + const OptionRowHandler &hand = OptionRowHandlers[i]; + const OptionRow &row = m_OptionRowAlloc[i]; + + if( row.bOneChoiceForAllPlayers ) + { + ExportOption( row, hand, 0, m_iSelectedOption[0][i] ); + } + else + for( int pn=0; pnIsHumanPlayer(pn) ) + continue; + + ExportOption( row, hand, pn, m_iSelectedOption[pn][i] ); + } + } + + // NEXT_SCREEN(GAMESTATE->m_PlayMode) ); + // XXX: handle different destinations based on play mode? + if( m_NextScreen == "" ) + m_NextScreen = NEXT_SCREEN; +} + +void ScreenOptionsMaster::GoToNextState() +{ + if( GAMESTATE->m_bEditing ) + SCREENMAN->PopTopScreen(); + else + SCREENMAN->SetNewScreen( m_NextScreen ); +} + +void ScreenOptionsMaster::GoToPrevState() +{ + /* XXX: A better way to handle this would be to check if we're a pushed screen. */ + if( GAMESTATE->m_bEditing ) + SCREENMAN->PopTopScreen(); + // XXX: handle different destinations based on play mode? + else + SCREENMAN->SetNewScreen( PREV_SCREEN ); // (GAMESTATE->m_PlayMode) ); +} + diff --git a/stepmania/src/ScreenOptionsMaster.h b/stepmania/src/ScreenOptionsMaster.h new file mode 100644 index 0000000000..cd219db569 --- /dev/null +++ b/stepmania/src/ScreenOptionsMaster.h @@ -0,0 +1,60 @@ +#ifndef SCREEN_OPTIONS_MASTER_H +#define SCREEN_OPTIONS_MASTER_H + +#include "ScreenOptions.h" +#include "ModeChoice.h" + +struct ConfOption; + +class ScreenOptionsMaster: public ScreenOptions +{ +public: + ScreenOptionsMaster( CString sName ); + virtual ~ScreenOptionsMaster(); + +private: + + enum OptionRowType + { + ROW_LIST, /* list of custom settings */ + ROW_STEP, /* list of steps for the current song or course */ + ROW_CHARACTER, /* list of characters */ + ROW_CONFIG, + NUM_OPTION_ROW_TYPES + }; + + struct OptionRowHandler + { + OptionRowType type; + + /* ROW_LIST: */ + vector ListEntries; + ModeChoice Default; + + /* ROW_CONFIG: */ + const ConfOption *opt; + }; + + CString m_NextScreen; + + vector OptionRowHandlers; + OptionRow *m_OptionRowAlloc; + + CString ConvertParamToThemeDifficulty( const CString &in ) const; + void ExportOption( const OptionRow &row, const OptionRowHandler &hand, int pn, int sel ); + int ImportOption( const OptionRow &row, const OptionRowHandler &hand, int pn ); + void SetList( OptionRow &row, OptionRowHandler &hand, CString param, CString &TitleOut ); + void SetStep( OptionRow &row, OptionRowHandler &hand ); + void SetConf( OptionRow &row, OptionRowHandler &hand, CString param, CString &TitleOut ); + void SetCharacter( OptionRow &row, OptionRowHandler &hand ); + +protected: + virtual void ImportOptions(); + virtual void ExportOptions(); + + virtual void GoToNextState(); + virtual void GoToPrevState(); +}; + + +#endif diff --git a/stepmania/src/ScreenOptionsMasterPrefs.cpp b/stepmania/src/ScreenOptionsMasterPrefs.cpp new file mode 100644 index 0000000000..b15bb83bdb --- /dev/null +++ b/stepmania/src/ScreenOptionsMasterPrefs.cpp @@ -0,0 +1,53 @@ +#include "global.h" + +#include "ScreenOptionsMasterPrefs.h" +#include "PrefsManager.h" + +/* "sel" is the selection in the menu. */ +static void MoveData( int &sel, int &opt, bool ToSel ) +{ + if( ToSel ) sel = opt; + else opt = sel; +} + +static void MoveData( int &sel, bool &opt, bool ToSel ) +{ + if( ToSel ) sel = opt; + else opt = !!sel; +} + +#define MOVE( name, opt ) \ + static void name( int &sel, bool ToSel ) \ + { \ + MoveData( sel, opt, ToSel ); \ + } + +MOVE( PreloadSounds, PREFSMAN->m_bSoundPreloadAll ); +MOVE( ResamplingQuality, PREFSMAN->m_iSoundResampleQuality ); + +static const ConfOption g_ConfOptions[] = +{ + ConfOption( "Preload\nSounds", PreloadSounds, "NO","YES" ), + ConfOption( "Resampling\nQuality", ResamplingQuality, "FAST","NORMAL","HIGH QUALITY" ), + ConfOption( "", NULL ) +}; + +const ConfOption *FindConfOption( CString name ) +{ + for( unsigned i = 0; g_ConfOptions[i].name != ""; ++i ) + { + const ConfOption *opt = &g_ConfOptions[i]; + + CString match(opt->name); + match.Replace("\n", ""); + match.Replace("-", ""); + + if( match != name ) + continue; + + return opt; + } + + return NULL; +} + diff --git a/stepmania/src/ScreenOptionsMasterPrefs.h b/stepmania/src/ScreenOptionsMasterPrefs.h new file mode 100644 index 0000000000..21b6ee9818 --- /dev/null +++ b/stepmania/src/ScreenOptionsMasterPrefs.h @@ -0,0 +1,28 @@ +#ifndef SCREEN_OPTIONS_MASTER_PREFS_H +#define SCREEN_OPTIONS_MASTER_PREFS_H + +static const int MAX_OPTIONS=16; +struct ConfOption +{ + /* Name of this option. It's helpful to delimit this with newlines, as it'll + * be the default display title. */ + CString name; + void (*MoveData)( int &sel, bool ToSel ); + inline int Get() const { int sel; MoveData( sel, true ); return sel; } + inline void Put( int sel ) const { MoveData( sel, false ); } + vector names; + + ConfOption( const char *n, void (*m)( int &sel, bool in ), + const char *c0=NULL, const char *c1=NULL, const char *c2=NULL, const char *c3=NULL, const char *c4=NULL, const char *c5=NULL, const char *c6=NULL, const char *c7=NULL, const char *c8=NULL, const char *c9=NULL, const char *c10=NULL, const char *c11=NULL, const char *c12=NULL, const char *c13=NULL, const char *c14=NULL, const char *c15=NULL, const char *c16=NULL, const char *c17=NULL, const char *c18=NULL, const char *c19=NULL ) + { + name = n; + MoveData = m; +#define PUSH( c ) if(c) names.push_back(c); + PUSH(c0);PUSH(c1);PUSH(c2);PUSH(c3);PUSH(c4);PUSH(c5);PUSH(c6);PUSH(c7);PUSH(c8);PUSH(c9);PUSH(c10);PUSH(c11);PUSH(c12);PUSH(c13);PUSH(c14);PUSH(c15);PUSH(c16);PUSH(c17);PUSH(c18);PUSH(c19); +#undef PUSH + } +}; + +const ConfOption *FindConfOption( CString name ); + +#endif