#include "global.h" /* ----------------------------------------------------------------------------- Class: ScreenPlayerOptions Desc: Select a song. Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. Chris Danford ----------------------------------------------------------------------------- */ #include "ScreenPlayerOptions.h" #include "RageUtil.h" #include "ScreenManager.h" #include "RageLog.h" #include "GameState.h" #include "ThemeManager.h" #include "AnnouncerManager.h" #include "RageSounds.h" #include "NoteSkinManager.h" #include "NoteFieldPositioning.h" #include "ScreenSongOptions.h" #include "Character.h" #include "Steps.h" #include "Course.h" #define PREV_SCREEN( play_mode ) THEME->GetMetric ("ScreenPlayerOptions","PrevScreen"+Capitalize(PlayModeToString(play_mode))) #define NEXT_SCREEN( play_mode ) THEME->GetMetric ("ScreenPlayerOptions","NextScreen"+Capitalize(PlayModeToString(play_mode))) #define BEGINNER_DESCRIPTION THEME->GetMetric ("ScreenPlayerOptions","Beginner") #define EASY_DESCRIPTION THEME->GetMetric ("ScreenPlayerOptions","Easy") #define MEDIUM_DESCRIPTION THEME->GetMetric ("ScreenPlayerOptions","Medium") #define HARD_DESCRIPTION THEME->GetMetric ("ScreenPlayerOptions","Hard") #define CHALLENGE_DESCRIPTION THEME->GetMetric ("ScreenPlayerOptions","Challenge") enum { PO_SPEED = 0, PO_ACCEL, PO_EFFECT, PO_APPEAR, PO_TURN, PO_TRANSFORM, PO_SCROLL, PO_NOTE_SKIN, PO_HOLD_NOTES, PO_HIDE, PO_PERSPECTIVE, PO_STEP, PO_CHARACTER, NUM_PLAYER_OPTIONS_LINES }; OptionRow g_PlayerOptionsLines[NUM_PLAYER_OPTIONS_LINES] = { OptionRow( "Speed", false, "x0.25","x0.5","x0.75","x1","x1.5","x2","x3","x5","x8","C200", "?" ), OptionRow( "Acceler\n-ation", false, "OFF","BOOST","BRAKE","WAVE","EXPAND","BOOMERANG" ), OptionRow( "Effect", false, "OFF","DRUNK","DIZZY","MINI","FLIP","TORNADO","TIPSY" ), OptionRow( "Appear\n-ance", false, "VISIBLE","HIDDEN","SUDDEN","STEALTH","BLINK", "R.VANISH" ), OptionRow( "Turn", false, "OFF","MIRROR","LEFT","RIGHT","SHUFFLE","S.SHUFFLE" ), OptionRow( "Trans\n-form", false, "OFF","LITTLE","WIDE","BIG","QUICK","SKIPPY","MINES" ), OptionRow( "Scroll", false, "STANDARD","REVERSE","SPLIT","ALTERNATE" ), OptionRow( "Note\nSkin", false, "" ), OptionRow( "Holds", false, "OFF","ON" ), OptionRow( "Hide", false, "OFF","DARK","BLIND"), OptionRow( "Perspec\n-tive", false, "" ), OptionRow( "Step", false, "" ), OptionRow( "Charac\n-ter", false, "" ), }; static const PlayerOptions::Effect ChoosableEffects[] = { PlayerOptions::EFFECT_DRUNK, PlayerOptions::EFFECT_DIZZY, PlayerOptions::EFFECT_MINI, PlayerOptions::EFFECT_FLIP, PlayerOptions::EFFECT_TORNADO, PlayerOptions::EFFECT_TIPSY, PlayerOptions::NUM_EFFECTS }; ScreenPlayerOptions::ScreenPlayerOptions( CString sClassName ) : ScreenOptions( sClassName ) { LOG->Trace( "ScreenPlayerOptions::ScreenPlayerOptions()" ); Init( INPUTMODE_INDIVIDUAL, g_PlayerOptionsLines, NUM_PLAYER_OPTIONS_LINES, false ); /* If we're going to "press start for more options" or skipping options * entirely, we need a different fade out. XXX: this is a hack */ if(PREFSMAN->m_ShowSongOptions == PrefsManager::NO) m_Menu.m_Out.Load( THEME->GetPathToB("ScreenPlayerOptions direct out") ); /* direct to stage */ else if(PREFSMAN->m_ShowSongOptions == PrefsManager::ASK) m_Menu.m_Out.Load( THEME->GetPathToB("ScreenPlayerOptions option out") ); /* optional song options */ m_sprOptionsMessage.Load( THEME->GetPathToG("ScreenPlayerOptions options") ); m_sprOptionsMessage.StopAnimating(); m_sprOptionsMessage.SetXY( CENTER_X, CENTER_Y ); m_sprOptionsMessage.SetZoom( 1 ); m_sprOptionsMessage.SetDiffuse( RageColor(1,1,1,0) ); //this->AddChild( &m_sprOptionsMessage ); // we have to draw this manually over the top of transitions m_bAcceptedChoices = false; m_bGoToOptions = ( PREFSMAN->m_ShowSongOptions == PrefsManager::YES ); SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo("player options intro") ); } void ScreenPlayerOptions::ImportOptions() { // fill in difficulty names m_OptionRow[PO_STEP].choices.clear(); if( GAMESTATE->m_bEditing ) { m_OptionRow[PO_STEP].choices.push_back( "" ); } else if( GAMESTATE->m_pCurCourse ) // playing a course { m_OptionRow[PO_STEP].choices.push_back( "REGULAR" ); if( GAMESTATE->m_pCurCourse->HasDifficult( GAMESTATE->GetCurrentStyleDef()->m_StepsType ) ) m_OptionRow[PO_STEP].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); m_OptionRow[PO_STEP].choices.push_back( s ); } } // // fill in skin names // CStringArray arraySkinNames; NOTESKIN->GetNoteSkinNames( arraySkinNames ); m_OptionRow[PO_NOTE_SKIN].choices.clear(); unsigned i; for( i=0; im_pPosition->GetNamesForCurrentGame(arrayPosNames); for( i=0; i apCharacters; GAMESTATE->GetCharacters( apCharacters ); for( i=0; im_sName; s.MakeUpper(); m_OptionRow[PO_CHARACTER].choices.push_back( s ); } for( int p=0; pm_PlayerOptions[p]; if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 0.25f ) m_iSelectedOption[p][PO_SPEED] = 0; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 0.5f ) m_iSelectedOption[p][PO_SPEED] = 1; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 0.75f ) m_iSelectedOption[p][PO_SPEED] = 2; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 1.0f ) m_iSelectedOption[p][PO_SPEED] = 3; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 1.5f ) m_iSelectedOption[p][PO_SPEED] = 4; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 2.0f ) m_iSelectedOption[p][PO_SPEED] = 5; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 3.0f ) m_iSelectedOption[p][PO_SPEED] = 6; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 5.0f ) m_iSelectedOption[p][PO_SPEED] = 7; else if( !po.m_bTimeSpacing && po.m_fScrollSpeed == 8.0f ) m_iSelectedOption[p][PO_SPEED] = 8; else if( po.m_bTimeSpacing && po.m_fScrollBPM == 200 ) m_iSelectedOption[p][PO_SPEED] = 9; // removed so all of them still fit on line // else if( po.m_bTimeSpacing && po.m_fScrollBPM == 300 ) m_iSelectedOption[p][PO_SPEED] = 10; else if( po.m_bTimeSpacing == IsModTimeSpacing( PREFSMAN->m_sCustomSpeedMod ) && (po.m_fScrollBPM == ConvertModToNumber( PREFSMAN->m_sCustomSpeedMod) || po.m_fScrollSpeed == ConvertModToNumber( PREFSMAN->m_sCustomSpeedMod ) ) ) m_iSelectedOption[p][PO_SPEED] = 10; else m_iSelectedOption[p][PO_SPEED] = 3; m_iSelectedOption[p][PO_ACCEL] = po.GetFirstAccel()+1; const PlayerOptions::Effect e = po.GetFirstEffect(); m_iSelectedOption[p][PO_EFFECT] = 0; for( i = 0; ChoosableEffects[i] != PlayerOptions::NUM_EFFECTS; ++i ) if( ChoosableEffects[i] == e ) m_iSelectedOption[p][PO_EFFECT] = i+1; // m_iSelectedOption[p][PO_EFFECT] = po.GetFirstEffect()+1; m_iSelectedOption[p][PO_APPEAR] = po.GetFirstAppearance()+1; m_iSelectedOption[p][PO_TURN] = po.m_Turn; m_iSelectedOption[p][PO_TRANSFORM] = po.m_Transform; m_iSelectedOption[p][PO_SCROLL] = po.GetFirstScroll()+1; // highlight currently selected skin m_iSelectedOption[p][PO_NOTE_SKIN] = -1; for( unsigned j=0; jm_bEditing ) { m_iSelectedOption[p][PO_STEP] = 0; } else if( GAMESTATE->m_pCurCourse ) // playing a course { // support for difficult on one side and normal on other is not supported // force same choice on both sides g_PlayerOptionsLines[PO_STEP].bOneChoiceForAllPlayers = true; if( GAMESTATE->m_bDifficultCourses && GAMESTATE->m_pCurCourse->HasDifficult( GAMESTATE->GetCurrentStyleDef()->m_StepsType ) ) m_iSelectedOption[p][PO_STEP] = 1; else m_iSelectedOption[p][PO_STEP] = 0; } 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; im_pCurNotes[p] == vNotes[i] ) m_iSelectedOption[p][PO_STEP] = i; } } // initialize custom speed mod text if (PREFSMAN->m_sCustomSpeedMod != "") m_OptionRow[PO_SPEED].choices[10] = PREFSMAN->m_sCustomSpeedMod; else m_OptionRow[PO_SPEED].choices.pop_back(); /* Default: */ m_iSelectedOption[p][PO_PERSPECTIVE] = 1; if( po.m_fSkew==1 && po.m_fPerspectiveTilt==-1 ) m_iSelectedOption[p][PO_PERSPECTIVE] = 0; else if( po.m_fSkew==1 && po.m_fPerspectiveTilt==1 ) m_iSelectedOption[p][PO_PERSPECTIVE] = 2; else if( po.m_fSkew==0 && po.m_fPerspectiveTilt==-1 ) m_iSelectedOption[p][PO_PERSPECTIVE] = 3; else if( po.m_fSkew==0 && po.m_fPerspectiveTilt==1 ) m_iSelectedOption[p][PO_PERSPECTIVE] = 4; else /* po.m_fPerspectiveTilt == 0 */ { vector &choices = m_OptionRow[PO_PERSPECTIVE].choices; for(unsigned n = 3; n < choices.size(); ++n) if(!choices[n].CompareNoCase(GAMESTATE->m_PlayerOptions[p].m_sPositioning)) m_iSelectedOption[p][PO_PERSPECTIVE] = n; } vector apCharacters; GAMESTATE->GetCharacters( apCharacters ); for( i=0; im_pCurCharacters[p] == apCharacters[i] ) m_iSelectedOption[p][PO_CHARACTER] = i+1; /* Why do this? We don't want to erase if we back out. */ // po.Init(); } } void ScreenPlayerOptions::ExportOptions() { for( int p=0; pm_PlayerOptions[p]; /* Why do this? We don't necessarily set everything in po, so we shouldn't * erase everything. */ // po.Init(); // if player isn't enabled, don't bother parsing options // **very important for nonstop difficulties** if (!GAMESTATE->IsPlayerEnabled(p)) continue; switch( m_iSelectedOption[p][PO_SPEED] ) { case 0: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 0.25f; break; case 1: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 0.5f; break; case 2: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 0.75f; break; case 3: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 1.0f; break; case 4: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 1.5f; break; case 5: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 2.0f; break; case 6: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 3.0f; break; case 7: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 5.0f; break; case 8: po.m_bTimeSpacing = false; po.m_fScrollSpeed = 8.0f; break; case 9: po.m_bTimeSpacing = true; po.m_fScrollSpeed = 1.0f; po.m_fScrollBPM = 200; break; // case 10:po.m_bTimeSpacing = true; po.m_fScrollSpeed = 1.0f; po.m_fScrollBPM = 300; break; case 10: po.m_bTimeSpacing = IsModTimeSpacing( PREFSMAN->m_sCustomSpeedMod ); if (po.m_bTimeSpacing) { po.m_fScrollSpeed = 1.0f; po.m_fScrollBPM = ConvertModToNumber( PREFSMAN->m_sCustomSpeedMod ); } else { po.m_fScrollSpeed = ConvertModToNumber( PREFSMAN->m_sCustomSpeedMod ); po.m_fScrollBPM = ConvertModToNumber( PREFSMAN->m_sCustomSpeedMod ); } break; default: ASSERT(0); } ZERO( po.m_fAccels ); if( m_iSelectedOption[p][PO_ACCEL] != 0 ) po.SetOneAccel( (PlayerOptions::Accel)(m_iSelectedOption[p][PO_ACCEL]-1) ); /* Don't change effects that aren't displayed. */ for( int i = 0; ChoosableEffects[i] != PlayerOptions::NUM_EFFECTS; ++i ) { const bool on = (i+1 == m_iSelectedOption[p][PO_EFFECT]); const PlayerOptions::Effect e = ChoosableEffects[i]; po.m_fEffects[e] = on? 1.0f:0.0f; } ZERO( po.m_fAppearances ); if( m_iSelectedOption[p][PO_APPEAR] != 0 ) po.SetOneAppearance( (PlayerOptions::Appearance)(m_iSelectedOption[p][PO_APPEAR]-1) ); po.m_Turn = (PlayerOptions::Turn)m_iSelectedOption[p][PO_TURN]; po.m_Transform = (PlayerOptions::Transform)m_iSelectedOption[p][PO_TRANSFORM]; ZERO( po.m_fScrolls ); if( m_iSelectedOption[p][PO_SCROLL] != 0 ) po.SetOneScroll( (PlayerOptions::Scroll)(m_iSelectedOption[p][PO_SCROLL]-1) ); int iSelectedSkin = m_iSelectedOption[p][PO_NOTE_SKIN]; CString sNewSkin = m_OptionRow[PO_NOTE_SKIN].choices[iSelectedSkin]; po.m_sNoteSkin = sNewSkin; po.m_bHoldNotes = (m_iSelectedOption[p][PO_HOLD_NOTES] == 1); po.m_fDark = (m_iSelectedOption[p][PO_HIDE] == 1) ? 1.f : 0.f; po.m_fBlind = (m_iSelectedOption[p][PO_HIDE] == 2) ? 1.f : 0.f; switch(m_iSelectedOption[p][PO_PERSPECTIVE]) { case 0: po.m_fSkew = 1; po.m_fPerspectiveTilt = -1; break; case 1: po.m_fSkew = 0; po.m_fPerspectiveTilt = 0; break; case 2: po.m_fSkew = 1; po.m_fPerspectiveTilt = 1; break; case 3: po.m_fSkew = 0; po.m_fPerspectiveTilt = -1; break; case 4: po.m_fSkew = 0; po.m_fPerspectiveTilt = 1; break; default: ASSERT(0); } if(m_iSelectedOption[p][PO_PERSPECTIVE] > 4) { const int choice = m_iSelectedOption[p][PO_PERSPECTIVE]; GAMESTATE->m_PlayerOptions[p].m_sPositioning = m_OptionRow[PO_PERSPECTIVE].choices[choice]; } // // apply difficulty selection // if( GAMESTATE->m_bEditing ) { // do nothing } else if( GAMESTATE->m_pCurCourse ) // playing a course { if( m_iSelectedOption[p][PO_STEP] == 1 ) { GAMESTATE->m_bDifficultCourses = true; LOG->Trace("ScreenPlayerOptions: Using difficult course"); } else { GAMESTATE->m_bDifficultCourses = false; LOG->Trace("ScreenPlayerOptions: Using normal course"); } } else { vector vNotes; GAMESTATE->m_pCurSong->GetSteps( vNotes, GAMESTATE->GetCurrentStyleDef()->m_StepsType ); SortNotesArrayByDifficulty( vNotes ); GAMESTATE->m_pCurNotes[p] = vNotes[ m_iSelectedOption[p][PO_STEP] ]; } if( m_iSelectedOption[p][PO_CHARACTER] == 0 ) GAMESTATE->m_pCurCharacters[p] = NULL; else { int choice = m_iSelectedOption[p][PO_CHARACTER] - 1; vector apCharacters; GAMESTATE->GetCharacters( apCharacters ); GAMESTATE->m_pCurCharacters[p] = apCharacters[choice]; } } } void ScreenPlayerOptions::GoToPrevState() { if( GAMESTATE->m_bEditing ) SCREENMAN->PopTopScreen(); else SCREENMAN->SetNewScreen( PREV_SCREEN(GAMESTATE->m_PlayMode) ); } void ScreenPlayerOptions::GoToNextState() { if( GAMESTATE->m_bEditing ) { SCREENMAN->PopTopScreen(); } else { GAMESTATE->AdjustFailType(); if( m_bGoToOptions ) SCREENMAN->SetNewScreen( NEXT_SCREEN(GAMESTATE->m_PlayMode) ); else SCREENMAN->SetNewScreen( ScreenSongOptions::GetNextScreen() ); } } void ScreenPlayerOptions::Update( float fDelta ) { ScreenOptions::Update( fDelta ); m_sprOptionsMessage.Update( fDelta ); } void ScreenPlayerOptions::DrawPrimitives() { ScreenOptions::DrawPrimitives(); m_sprOptionsMessage.Draw(); } void ScreenPlayerOptions::Input( const DeviceInput& DeviceI, const InputEventType type, const GameInput &GameI, const MenuInput &MenuI, const StyleInput &StyleI ) { if( !GAMESTATE->m_bEditing && type == IET_FIRST_PRESS && !m_Menu.m_In.IsTransitioning() && MenuI.IsValid() && MenuI.button == MENU_BUTTON_START && PREFSMAN->m_ShowSongOptions == PrefsManager::ASK ) { if( m_bAcceptedChoices && !m_bGoToOptions ) { m_bGoToOptions = true; m_sprOptionsMessage.SetState( 1 ); SOUND->PlayOnce( THEME->GetPathToS("Common start") ); } } ScreenOptions::Input( DeviceI, type, GameI, MenuI, StyleI ); } void ScreenPlayerOptions::HandleScreenMessage( const ScreenMessage SM ) { if( PREFSMAN->m_ShowSongOptions == PrefsManager::ASK ) { switch( SM ) { case SM_BeginFadingOut: // when the user accepts the page of options { m_bAcceptedChoices = true; float fShowSeconds = m_Menu.m_Out.GetLengthSeconds(); // show "hold START for options" m_sprOptionsMessage.SetDiffuse( RageColor(1,1,1,0) ); m_sprOptionsMessage.BeginTweening( 0.15f ); // fade in m_sprOptionsMessage.SetZoomY( 1 ); m_sprOptionsMessage.SetDiffuse( RageColor(1,1,1,1) ); m_sprOptionsMessage.BeginTweening( fShowSeconds-0.3f ); // sleep m_sprOptionsMessage.BeginTweening( 0.15f ); // fade out m_sprOptionsMessage.SetDiffuse( RageColor(1,1,1,0) ); m_sprOptionsMessage.SetZoomY( 0 ); } break; } } ScreenOptions::HandleScreenMessage( SM ); } CString ScreenPlayerOptions::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 } } float ScreenPlayerOptions::ConvertModToNumber(CString value) { if (IsModTimeSpacing(value)) { return (float) atof(value.c_str() + 1 ); } else { return (float) atof(value.c_str() ); } } bool ScreenPlayerOptions::IsModTimeSpacing(CString value) { ASSERT( value.GetLength() ); if (value[0] == 'C') return true; return false; }