diff --git a/stepmania/src/Player.cpp b/stepmania/src/Player.cpp index 2d5d66a6ed..e0df0b96ed 100644 --- a/stepmania/src/Player.cpp +++ b/stepmania/src/Player.cpp @@ -311,6 +311,10 @@ void Player::Init( m_fNoteFieldHeight = GRAY_ARROWS_Y_REVERSE-GRAY_ARROWS_Y_STANDARD; if( m_pNoteField ) m_pNoteField->Init( m_pPlayerState, m_fNoteFieldHeight ); + + m_vbFretIsDown.resize( GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer ); + FOREACH( bool, m_vbFretIsDown, b ) + *b = false; } void Player::Load() @@ -918,14 +922,74 @@ int Player::GetClosestNote( int col, int iNoteRow, int iMaxRowsAhead, int iMaxRo return iNextIndex; } -void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) +int Player::GetClosestNonEmptyRowDirectional( int iStartRow, int iEndRow, bool bAllowGraded, bool bForward ) const +{ + if( bForward ) + { + NoteData::all_tracks_iterator iter = m_NoteData.GetTapNoteRangeAllTracks( iStartRow, iEndRow ); + + while( !iter.IsAtEnd() ) + { + //NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iRowOfClosestNonEmptyRow, pn) ) + return iter.Row(); + } + } + else + { + NoteData::all_tracks_reverse_iterator iter = m_NoteData.GetTapNoteRangeAllTracksReverse( iStartRow, iEndRow ); + + while( !iter.IsAtEnd() ) + { + //NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iRowOfClosestNonEmptyRow, pn) ) + return iter.Row(); + } + } + + return -1; +} + +/* Find the closest note to fBeat. */ +int Player::GetClosestNonEmptyRow( int iNoteRow, int iMaxRowsAhead, int iMaxRowsBehind, bool bAllowGraded ) const +{ + // Start at iIndexStartLookingAt and search outward. + int iNextRow = GetClosestNonEmptyRowDirectional( iNoteRow, iNoteRow+iMaxRowsAhead, bAllowGraded, true ); + int iPrevRow = GetClosestNonEmptyRowDirectional( iNoteRow-iMaxRowsBehind, iNoteRow, bAllowGraded, false ); + + if( iNextRow == -1 && iPrevRow == -1 ) + return -1; + if( iNextRow == -1 ) + return iPrevRow; + if( iPrevRow == -1 ) + return iNextRow; + + /* Figure out which row is closer. */ + if( abs(iNoteRow-iNextRow) > abs(iNoteRow-iPrevRow) ) + return iPrevRow; + else + return iNextRow; +} + +bool Player::IsOniDead() const { // If we're playing on oni and we've died, do nothing. - if( GAMESTATE->m_SongOptions.GetCurrent().m_LifeType == SongOptions::LIFE_BATTERY && m_pPlayerStageStats && m_pPlayerStageStats->bFailed ) + return GAMESTATE->m_SongOptions.GetCurrent().m_LifeType == SongOptions::LIFE_BATTERY && m_pPlayerStageStats && m_pPlayerStageStats->bFailed; +} + +void Player::Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) +{ + if( IsOniDead() ) return; DEBUG_ASSERT_M( col >= 0 && col <= m_NoteData.GetNumTracks(), ssprintf("%i, %i", col, m_NoteData.GetNumTracks()) ); + m_vbFretIsDown[ col ] = !bRelease; +} + +void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) +{ + if( IsOniDead() ) + return; + const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago(); const float fSongBeat = GAMESTATE->m_pCurSong ? GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds ) : GAMESTATE->m_fSongBeat; @@ -1047,17 +1111,17 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele * Either option would fundamentally change the grading of two quick notes "jack hammers." Hmm. */ const int iStepSearchRows = BeatToNoteRow( StepSearchDistance * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate ); - int iIndexOverlappingNote = row == -1 ? GetClosestNote( col, BeatToNoteRow(fSongBeat), iStepSearchRows, iStepSearchRows, false ) : row; + int iRowOfOverlappingNote = row == -1 ? GetClosestNote( col, BeatToNoteRow(fSongBeat), iStepSearchRows, iStepSearchRows, false ) : row; // calculate TapNoteScore TapNoteScore score = TNS_None; - if( iIndexOverlappingNote != -1 ) + if( iRowOfOverlappingNote != -1 ) { // compute the score for this hit float fNoteOffset = 0.0f; // we need this later if we are autosyncing - const float fStepBeat = NoteRowToBeat( iIndexOverlappingNote ); + const float fStepBeat = NoteRowToBeat( iRowOfOverlappingNote ); const float fStepSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat(fStepBeat); if( row == -1 ) @@ -1081,7 +1145,7 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele const float fSecondsFromExact = fabsf( fNoteOffset ); - NoteData::iterator iter = m_NoteData.FindTapNote( col, iIndexOverlappingNote ); + NoteData::iterator iter = m_NoteData.FindTapNote( col, iRowOfOverlappingNote ); DEBUG_ASSERT( iter!= m_NoteData.end(col) ); TapNote &tn = iter->second; @@ -1140,13 +1204,13 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele // there are are no mines to the left of us. for( int t=0; t= get_to_avoid ) @@ -1201,9 +1265,9 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele // remove all TapAttacks on this row for( int t=0; tDidTapNote( col, score, bBright ); if( score >= TNS_W3 || bBlind ) - HideNote( col, iIndexOverlappingNote ); + HideNote( col, iRowOfOverlappingNote ); } } - else if( NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iIndexOverlappingNote, pn) ) + else if( NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iRowOfOverlappingNote, pn) ) { - FlashGhostRow( iIndexOverlappingNote, pn ); + FlashGhostRow( iRowOfOverlappingNote, pn ); } } @@ -1245,18 +1309,18 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele { /* Search for keyed sounds separately. If we can't find a nearby note, search * backwards indefinitely, and ignore grading. */ - /* XXX: This isn't quite right. As per the above XXX for iIndexOverlappingNote, if iIndexOverlappingNote + /* XXX: This isn't quite right. As per the above XXX for iRowOfOverlappingNote, if iRowOfOverlappingNote * is set to a previous note, the keysound could have changed and this would cause the wrong one to play, * in essence playing two sounds in the opposite order. Maybe this should always perform the search. Still, * even that doesn't seem quite right since it would then play the same (new) keysound twice which would * sound wrong even though the notes were judged as being correct, above. Fixing the above problem would * fix this one as well. */ - if( iIndexOverlappingNote == -1 ) - iIndexOverlappingNote = GetClosestNote( col, BeatToNoteRow(fSongBeat), + if( iRowOfOverlappingNote == -1 ) + iRowOfOverlappingNote = GetClosestNote( col, BeatToNoteRow(fSongBeat), iStepSearchRows, MAX_NOTE_ROW, true ); - if( iIndexOverlappingNote != -1 ) + if( iRowOfOverlappingNote != -1 ) { - const TapNote &tn = m_NoteData.GetTapNote( col, iIndexOverlappingNote ); + const TapNote &tn = m_NoteData.GetTapNote( col, iRowOfOverlappingNote ); if( tn.iKeysoundIndex >= 0 && tn.iKeysoundIndex < (int) m_vKeysounds.size() ) m_vKeysounds[tn.iKeysoundIndex].Play(); } @@ -1270,6 +1334,11 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele } } +void Player::Strum( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) +{ + +} + static bool Unjudged( const TapNote &tn ) { if( tn.result.tns != TNS_None ) diff --git a/stepmania/src/Player.h b/stepmania/src/Player.h index 456c71a98f..5d8f27692b 100644 --- a/stepmania/src/Player.h +++ b/stepmania/src/Player.h @@ -113,6 +113,9 @@ public: void Load(); void CrossedRow( int iNoteRow, const RageTimer &now ); void CrossedMineRow( int iNoteRow, const RageTimer &now ); + bool IsOniDead() const; + void Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ); + void Strum( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ); void Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ); void RandomizeNotes( int iNoteRow ); void FadeToFail(); @@ -138,6 +141,8 @@ protected: int GetClosestNoteDirectional( int col, int iStartRow, int iMaxRowsAhead, bool bAllowGraded, bool bForward ) const; int GetClosestNote( int col, int iNoteRow, int iMaxRowsAhead, int iMaxRowsBehind, bool bAllowGraded ) const; + int GetClosestNonEmptyRowDirectional( int iStartRow, int iMaxRowsAhead, bool bAllowGraded, bool bForward ) const; + int GetClosestNonEmptyRow( int iNoteRow, int iMaxRowsAhead, int iMaxRowsBehind, bool bAllowGraded ) const; bool IsPlayingBeginner() const; inline void HideNote( int col, int row ) @@ -186,6 +191,8 @@ protected: RageSound m_soundAttackLaunch; RageSound m_soundAttackEnding; + vector m_vbFretIsDown; + vector m_vKeysounds; RString m_sMessageToSendOnStep; diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 594c9d6276..2167f047a6 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -329,6 +329,7 @@ ScreenGameplay::ScreenGameplay() void ScreenGameplay::Init() { PLAYER_TYPE.Load( m_sName, "PlayerType" ); + PLAYER_INIT_COMMAND.Load( m_sName, "PlayerInitCommand" ); GIVE_UP_START_TEXT.Load( m_sName, "GiveUpStartText" ); GIVE_UP_BACK_TEXT.Load( m_sName, "GiveUpBackText" ); GIVE_UP_ABORTED_TEXT.Load( m_sName, "GiveUpAbortedText" ); @@ -460,7 +461,8 @@ void ScreenGameplay::Init() if( Center1Player() ) fPlayerX = SCREEN_CENTER_X; - pi->m_pPlayer->SetXY( fPlayerX, SCREEN_CENTER_Y ); + pi->m_pPlayer->SetX( fPlayerX ); + pi->m_pPlayer->RunCommands( PLAYER_INIT_COMMAND ); this->AddChild( pi->m_pPlayer ); } @@ -2139,7 +2141,13 @@ void ScreenGameplay::Input( const InputEventPlus &input ) /* Only handle MENU_BUTTON_BACK as a regular BACK button if BACK_GIVES_UP is * disabled. */ - if( input.MenuI == MENU_BUTTON_BACK && !BACK_GIVES_UP ) + bool bHoldingBack = false; + if( GAMESTATE->GetCurrentStyle()->GameInputToColumn(input.GameI) == Column_Invalid ) + { + bHoldingBack |= input.MenuI == MENU_BUTTON_BACK && !BACK_GIVES_UP; + } + + if( bHoldingBack ) { if( ((!PREFSMAN->m_bDelayedBack && input.type==IET_FIRST_PRESS) || (input.DeviceI.device==DEVICE_KEYBOARD && input.type==IET_REPEAT) || @@ -2171,10 +2179,19 @@ void ScreenGameplay::Input( const InputEventPlus &input ) bool bRelease = input.type == IET_RELEASE; const int iCol = GAMESTATE->GetCurrentStyle()->GameInputToColumn( input.GameI ); + // Don't pass on any inputs to Player that aren't a press or a release. + switch( input.type ) + { + case IET_FIRST_PRESS: + case IET_RELEASE: + break; + default: + return; + } + if( GAMESTATE->m_bMultiplayer ) { if( input.mp != MultiPlayer_Invalid && - input.type==IET_FIRST_PRESS && iCol != Column_Invalid && GAMESTATE->IsMultiPlayerEnabled(input.mp) ) { @@ -2186,16 +2203,30 @@ void ScreenGameplay::Input( const InputEventPlus &input ) // // handle a step or battle item activate // - if( input.type==IET_FIRST_PRESS && - iCol != Column_Invalid && - GAMESTATE->IsHumanPlayer( input.pn ) ) + if( GAMESTATE->IsHumanPlayer( input.pn ) ) { AbortGiveUp( true ); if( PREFSMAN->m_AutoPlay == PC_HUMAN ) { PlayerInfo& pi = GetPlayerInfoForInput( input ); - pi.m_pPlayer->Step( iCol, -1, input.DeviceI.ts, false, bRelease ); + + ASSERT( input.GameI.IsValid() ); + + GameButtonType gbt = INPUTMAPPER->GetInputScheme()->m_GameButtonInfo[input.GameI.button].m_gbt; + switch( gbt ) + { + DEFAULT_FAIL( gbt ); + case GameButtonType_Step: + pi.m_pPlayer->Step( iCol, -1, input.DeviceI.ts, false, bRelease ); + break; + case GameButtonType_Fret: + pi.m_pPlayer->Fret( iCol, -1, input.DeviceI.ts, false, bRelease ); + break; + case GameButtonType_Strum: + pi.m_pPlayer->Strum( iCol, -1, input.DeviceI.ts, false, bRelease ); + break; + } } } } diff --git a/stepmania/src/ScreenGameplay.h b/stepmania/src/ScreenGameplay.h index bb514ce30f..014b09bab3 100644 --- a/stepmania/src/ScreenGameplay.h +++ b/stepmania/src/ScreenGameplay.h @@ -124,6 +124,7 @@ protected: virtual bool UseSongBackgroundAndForeground() const { return true; } ThemeMetric PLAYER_TYPE; + ThemeMetric PLAYER_INIT_COMMAND; LocalizedString GIVE_UP_START_TEXT; LocalizedString GIVE_UP_BACK_TEXT; LocalizedString GIVE_UP_ABORTED_TEXT;