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;