diff --git a/stepmania/Themes/default/metrics.ini b/stepmania/Themes/default/metrics.ini index 0c41f665a0..29f423a0e2 100644 --- a/stepmania/Themes/default/metrics.ini +++ b/stepmania/Themes/default/metrics.ini @@ -814,3 +814,18 @@ NextScreen=ScreenHowToPlay [ScreenHowToPlay] SecondsToShow=30 NextScreen=ScreenUnlock + +[ScreenNameEntry] +TimerX=320 +TimerY=30 +CategoryY=444 +CharsZoomSmall=1.0 +CharsZoomLarge=1.5 +CharsSpacingY=40 +ScrollingCharsColor=0.6,0.8,0.8,1 +SelectedCharsColor=0.8,1,1,1 +GrayArrowsY=100 +NumCharsToDrawBehind=2 +NumCharsToDrawTotal=10 +FakeBeatsPerSec=2.5 +TimerSeconds=24 diff --git a/stepmania/src/GameState.cpp b/stepmania/src/GameState.cpp index d8e43f0091..75b2dc88a2 100644 --- a/stepmania/src/GameState.cpp +++ b/stepmania/src/GameState.cpp @@ -80,6 +80,12 @@ void GameState::Reset() for( p=0; pm_fMusicSeconds <= 0 ) // music isn't playing + if( !GAMESTATE->m_bPastHereWeGo ) { SetState( 0 ); + return; } - else - { - /* These could be metrics or configurable. I'd prefer the flash to - * start on the beat, I think ... -glenn */ - /* Start flashing 10% of a beat before the beat starts. */ - const float flash_offset = -0.1f; + /* These could be metrics or configurable. I'd prefer the flash to + * start on the beat, I think ... -glenn */ - /* Flash for 20% of a beat. */ - const float flash_length = 0.2f; + /* Start flashing 10% of a beat before the beat starts. */ + const float flash_offset = -0.1f; - float cur_beat = GAMESTATE->m_fSongBeat; + /* Flash for 20% of a beat. */ + const float flash_length = 0.2f; - /* Beats can start in very negative territory (many BMR songs, Drop Out - * -Remix-). */ - cur_beat += 100.0f; + float cur_beat = GAMESTATE->m_fSongBeat; - cur_beat -= flash_offset; - float fPercentIntoBeat = fmodf(cur_beat, 1); - SetState( (fPercentIntoBeat // // Defines specific to ScreenNameEntry // -#define EXPLANATION_X THEME->GetMetricF("ScreenNameEntry","ExplanationX") -#define EXPLANATION_Y THEME->GetMetricF("ScreenNameEntry","ExplanationY") -#define EXPLANATION_TEXT THEME->GetMetric("ScreenNameEntry","ExplanationText") -#define HELP_TEXT THEME->GetMetric("ScreenNameEntry","HelpText") +#define TIMER_X THEME->GetMetricF("ScreenNameEntry","TimerX") +#define TIMER_Y THEME->GetMetricF("ScreenNameEntry","TimerY") +#define CATEGORY_Y THEME->GetMetricF("ScreenNameEntry","CategoryY") +#define CHARS_ZOOM_SMALL THEME->GetMetricF("ScreenNameEntry","CharsZoomSmall") +#define CHARS_ZOOM_LARGE THEME->GetMetricF("ScreenNameEntry","CharsZoomLarge") +#define CHARS_SPACING_Y THEME->GetMetricF("ScreenNameEntry","CharsSpacingY") +#define SCROLLING_CHARS_COLOR THEME->GetMetricC("ScreenNameEntry","ScrollingCharsColor") +#define SELECTED_CHARS_COLOR THEME->GetMetricC("ScreenNameEntry","SelectedCharsColor") +#define GRAY_ARROWS_Y THEME->GetMetricF("ScreenNameEntry","GrayArrowsY") +#define NUM_CHARS_TO_DRAW_BEHIND THEME->GetMetricI("ScreenNameEntry","NumCharsToDrawBehind") +#define NUM_CHARS_TO_DRAW_TOTAL THEME->GetMetricI("ScreenNameEntry","NumCharsToDrawTotal") +#define FAKE_BEATS_PER_SEC THEME->GetMetricF("ScreenNameEntry","FakeBeatsPerSec") +#define TIMER_SECONDS THEME->GetMetricI("ScreenNameEntry","TimerSeconds") + + +// cache for frequently used metrics +float g_fCharsZoomSmall; +float g_fCharsZoomLarge; +float g_fCharsSpacingY; +RageColor g_ScrollingCharsColor; +RageColor g_SelectedCharsColor; +float g_fGrayArrowsY; +int g_iNumCharsToDrawBehind; +int g_iNumCharsToDrawTotal; +float g_fFakeBeatsPerSec; + + +const char NAME_CHARS[] = +{ + ' ',' ',' ',' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' +}; +#define NUM_NAME_CHARS (sizeof(NAME_CHARS)/sizeof(char)) +#define HEIGHT_OF_ALL_CHARS (NUM_NAME_CHARS * g_fCharsSpacingY) const ScreenMessage SM_GoToPrevScreen = ScreenMessage(SM_User+1); const ScreenMessage SM_GoToNextScreen = ScreenMessage(SM_User+2); +int GetClosestCharIndex( float fFakeBeat ) +{ + return ((int)roundf(fFakeBeat)) % NUM_NAME_CHARS; +}; + +// return value is relative to gray arrows +float GetClosestCharYOffset( float fFakeBeat ) +{ + float f = fmodf(fFakeBeat, 1.0f); + if( f > 0.5f ) + f -= 1; + ASSERT( f>=-0.5f && f<=0.5f ); + return -f; +} + +// return value is relative to gray arrows +float GetClosestCharYPos( float fFakeBeat ) +{ + return roundf( GetClosestCharYOffset(fFakeBeat)*g_fCharsSpacingY ); +} + + ScreenNameEntry::ScreenNameEntry() { LOG->Trace( "ScreenNameEntry::ScreenNameEntry()" ); - m_Background; + // update cache + g_fCharsZoomSmall = CHARS_ZOOM_SMALL; + g_fCharsZoomLarge = CHARS_ZOOM_LARGE; + g_fCharsSpacingY = CHARS_SPACING_Y; + g_ScrollingCharsColor = SCROLLING_CHARS_COLOR; + g_SelectedCharsColor = SELECTED_CHARS_COLOR; + g_fGrayArrowsY = GRAY_ARROWS_Y; + g_iNumCharsToDrawBehind = NUM_CHARS_TO_DRAW_BEHIND; + g_iNumCharsToDrawTotal = NUM_CHARS_TO_DRAW_TOTAL; + g_fFakeBeatsPerSec = FAKE_BEATS_PER_SEC; + + + + // DEBUGGING STUFF + GAMESTATE->m_CurGame = GAME_DANCE; + GAMESTATE->m_CurStyle = STYLE_DANCE_SINGLE; + GAMESTATE->m_PlayMode = PLAY_MODE_ARCADE; + GAMESTATE->m_bSideIsJoined[PLAYER_1] = true; + GAMESTATE->m_MasterPlayerNumber = PLAYER_1; + GAMESTATE->m_LastHighScoreCategory[PLAYER_1] = CATEGORY_A; + GAMESTATE->m_iLastHighScoreIndex[PLAYER_1] = 0; + + + + + GAMESTATE->m_bPastHereWeGo = true; // enable the gray arrows + + m_Background.LoadFromAniDir( THEME->GetPathTo("BGAnimations","name entry") ); + this->AddChild( &m_Background ); for( int p=0; pIsPlayerEnabled(p) ) + bool bNewHighScore = GAMESTATE->m_iLastHighScoreIndex[p] != -1; + m_bConfirmedName[p] = !bNewHighScore; // false if they made a new high score + + if( !bNewHighScore ) continue; // skip - m_iHighScoreIndex[p] = -1; + ASSERT( GAMESTATE->IsPlayerEnabled(p) ); // they better be enabled if they made a high score! - // is this a new high score? + m_GrayArrowRow[p].Load( (PlayerNumber)p ); + m_GrayArrowRow[p].SetX( (float)GAMESTATE->GetCurrentStyleDef()->m_iCenterX[p] ); + m_GrayArrowRow[p].SetY( SCREEN_TOP + 100 ); + this->AddChild( &m_GrayArrowRow[p] ); + + + const StyleDef* pStyleDef = GAMESTATE->GetCurrentStyleDef(); + for( int t=0; tm_iColsPerPlayer; t++ ) + { + float ColX = pStyleDef->m_iCenterX[p] + pStyleDef->m_ColumnInfo[p][t].fXOffset; + + m_textSelectedChars[p][t].LoadFromFont( THEME->GetPathTo("Fonts","high scores") ); + m_textSelectedChars[p][t].SetX( ColX ); + m_textSelectedChars[p][t].SetY( GRAY_ARROWS_Y ); + m_textSelectedChars[p][t].SetDiffuse( g_SelectedCharsColor ); + m_textSelectedChars[p][t].SetZoom( CHARS_ZOOM_LARGE ); + this->AddChild( &m_textSelectedChars[p][t] ); // draw these manually + m_textScrollingChars[p][t].LoadFromFont( THEME->GetPathTo("Fonts","high scores") ); + m_textScrollingChars[p][t].SetX( ColX ); + m_textScrollingChars[p][t].SetY( GRAY_ARROWS_Y ); + m_textScrollingChars[p][t].SetDiffuse( g_ScrollingCharsColor ); + //this->AddChild( &m_textScrollingChars[p][t] ); // draw these manually + } - - m_GrayArrowRow[p]; - m_textSelectedChars[p]; - m_textScrollingChars[p]; - m_textCategory[p]; + m_textCategory[p].LoadFromFont( THEME->GetPathTo("Fonts","header2") ); + m_textCategory[p].SetX( (float)GAMESTATE->GetCurrentStyleDef()->m_iCenterX[p] ); + m_textCategory[p].SetY( CATEGORY_Y ); + CString sCategoryText = ssprintf("No. %d", GAMESTATE->m_iLastHighScoreIndex[p]+1); + switch( GAMESTATE->m_PlayMode ) + { + case PLAY_MODE_ARCADE: + sCategoryText += ssprintf(" in Type %c", 'A'+GAMESTATE->m_LastHighScoreCategory[p]); + break; + case PLAY_MODE_NONSTOP: + case PLAY_MODE_ONI: + case PLAY_MODE_ENDLESS: + sCategoryText += ssprintf(" in %s", GAMESTATE->m_pCurCourse->m_sName.c_str()); + break; + default: + ASSERT(0); + } + m_textCategory[p].SetText( sCategoryText ); + this->AddChild( &m_textCategory[p] ); } - m_Timer; + m_Timer.SetTimer(TIMER_SECONDS); + m_Timer.SetXY( TIMER_X, TIMER_Y ); + this->AddChild( &m_Timer ); - m_Fade; + m_Fade.OpenWipingRight(); +// this->AddChild( &m_Fade ); // draw and update this manually too + + m_soundStep.Load( THEME->GetPathTo("Sounds","name entry step") ); + + SOUNDMAN->PlayMusic( THEME->GetPathTo("Sounds","name entry music") ); + + m_fFakeBeat = 0; } @@ -70,15 +198,103 @@ ScreenNameEntry::~ScreenNameEntry() LOG->Trace( "ScreenNameEntry::~ScreenNameEntry()" ); } +void ScreenNameEntry::Update( float fDelta ) +{ + m_fFakeBeat += fDelta * FAKE_BEATS_PER_SEC; + GAMESTATE->m_fSongBeat = m_fFakeBeat; + Screen::Update(fDelta); + + m_Fade.Update( fDelta ); +} + void ScreenNameEntry::DrawPrimitives() { Screen::DrawPrimitives(); + + int iClosestIndex = GetClosestCharIndex( m_fFakeBeat ); + int iStartDrawingIndex = iClosestIndex - NUM_CHARS_TO_DRAW_BEHIND; + iStartDrawingIndex += NUM_NAME_CHARS; // make positive + + const StyleDef* pStyleDef = GAMESTATE->GetCurrentStyleDef(); + + for( int p=0; pm_iColsPerPlayer; t++ ) + { + m_textScrollingChars[p][t].SetText( ssprintf("%c",c) ); // why doens't CStdStr have a contructor that takes a char? + m_textScrollingChars[p][t].SetY( fY ); + float fZoom = g_fCharsZoomSmall; + if( iCharIndex==iClosestIndex ) + fZoom = SCALE(fabsf(GetClosestCharYOffset(m_fFakeBeat)),0,0.5f,g_fCharsZoomLarge,g_fCharsZoomSmall); + m_textScrollingChars[p][t].SetZoom(fZoom); + RageColor color = g_ScrollingCharsColor; + if( i==0 ) + color.a *= SCALE(GetClosestCharYOffset(m_fFakeBeat),-0.5f,0.f,0.f,1.f); + if( i==g_iNumCharsToDrawTotal-1 ) + color.a *= SCALE(GetClosestCharYOffset(m_fFakeBeat),0.f,0.5f,1.f,0.f); + m_textScrollingChars[p][t].SetDiffuse( color ); + m_textScrollingChars[p][t].Draw(); + } + fY += g_fCharsSpacingY; + iCharIndex = (iCharIndex+1) % NUM_NAME_CHARS; + } + + + for( int t=0; tm_iColsPerPlayer; t++ ) + { + m_textSelectedChars[p][t].Draw(); + } + } + + + m_Fade.Draw(); } void ScreenNameEntry::Input( const DeviceInput& DeviceI, const InputEventType type, const GameInput &GameI, const MenuInput &MenuI, const StyleInput &StyleI ) { LOG->Trace( "ScreenNameEntry::Input()" ); + if( type != IET_FIRST_PRESS ) + return; // ignore + + if( m_Fade.IsClosing() ) + return; + + if( StyleI.IsValid() ) + { + m_GrayArrowRow[StyleI.player].Step( StyleI.col ); + m_soundStep.Play(); + char c = NAME_CHARS[GetClosestCharIndex(m_fFakeBeat)]; + m_textSelectedChars[StyleI.player][StyleI.col].SetText( ssprintf("%c",c) ); + } + + if( MenuI.IsValid() ) + { + if( MenuI.button == MENU_BUTTON_START ) + { + m_soundStep.Play(); + + m_bConfirmedName[MenuI.player] = true; + + bool bAllConfirmed = true; + for( int p=0; pSetNewScreen( "ScreenMusicScroll" ); break; diff --git a/stepmania/src/ScreenNameEntry.h b/stepmania/src/ScreenNameEntry.h index 2f2a988e0b..c303dcc2c1 100644 --- a/stepmania/src/ScreenNameEntry.h +++ b/stepmania/src/ScreenNameEntry.h @@ -16,12 +16,15 @@ #include "RandomSample.h" #include "GrayArrowRow.h" +const int MAX_COLS_IN_NAME_ENTRY = 12; + class ScreenNameEntry : public Screen { public: ScreenNameEntry(); virtual ~ScreenNameEntry(); + virtual void Update( float fDeltaTime ); virtual void DrawPrimitives(); virtual void Input( const DeviceInput& DeviceI, const InputEventType type, const GameInput &GameI, const MenuInput &MenuI, const StyleInput &StyleI ); virtual void HandleScreenMessage( const ScreenMessage SM ); @@ -31,14 +34,17 @@ private: BGAnimation m_Background; GrayArrowRow m_GrayArrowRow[NUM_PLAYERS]; - BitmapText m_textSelectedChars[NUM_PLAYERS]; - BitmapText m_textScrollingChars[NUM_PLAYERS]; + BitmapText m_textSelectedChars[NUM_PLAYERS][MAX_COLS_IN_NAME_ENTRY]; + BitmapText m_textScrollingChars[NUM_PLAYERS][MAX_COLS_IN_NAME_ENTRY]; BitmapText m_textCategory[NUM_PLAYERS]; MenuTimer m_Timer; TransitionFade m_Fade; - int m_iHighScoreIndex[NUM_PLAYERS]; + RageSound m_soundStep; + + float m_fFakeBeat; + bool m_bConfirmedName[NUM_PLAYERS]; }; diff --git a/stepmania/src/StepMania.vcproj b/stepmania/src/StepMania.vcproj index 5d6fb6e382..a35982f2b7 100644 --- a/stepmania/src/StepMania.vcproj +++ b/stepmania/src/StepMania.vcproj @@ -915,6 +915,12 @@ cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ + + + +