evil big commit (sorry):

Remove m_HoldNote array entirely.  Remove 2sAnd3s.  Store hold note
durations in the hold head.  hold_tail only still exists when InsertHoldTails
is used, to make SM parsing a little easier (may go away).  Add helpers
for iterating over ranges while including or excluding adjacent and overlapping
hold notes.  Range operations are now [start,end) instead of [start,end].
(probably more details coming to sm-dev soon)
This commit is contained in:
Glenn Maynard
2005-01-25 05:02:35 +00:00
parent 6ece8829c4
commit bb7482e722
12 changed files with 771 additions and 762 deletions
+343 -344
View File
@@ -30,69 +30,82 @@ void NoteData::SetNumTracks( int iNewNumTracks )
ASSERT( iNewNumTracks > 0 );
m_TapNotes.resize( iNewNumTracks );
/* Remove all hold notes that are out of bounds. */
// Iterate backwards so that we can delete.
for( int h = m_HoldNotes.size()-1; h >= 0; --h )
if( m_HoldNotes[h].iTrack >= iNewNumTracks )
m_HoldNotes.erase( m_HoldNotes.begin()+h );
}
/* Clear [rowBegin,rowEnd]; that is, including rowEnd. */
/* Clear [rowBegin,rowEnd). */
void NoteData::ClearRangeForTrack( int rowBegin, int rowEnd, int iTrack )
{
/* Optimization: if the range encloses everything, just clear the whole maps. */
if( rowBegin == 0 && rowEnd == MAX_NOTE_ROW )
{
m_TapNotes[iTrack].clear();
for( int i = GetNumHoldNotes()-1; i >= 0; --i )
{
HoldNote hn = GetHoldNote(i);
if( hn.iTrack != iTrack )
continue;
this->RemoveHoldNote( i );
}
return;
}
/* Crop or split hold notes overlapping the range. */
for( int i = GetNumHoldNotes()-1; i >= 0; --i ) // for each HoldNote
iterator begin, end;
GetTapNoteRangeInclusive( iTrack, rowBegin, rowEnd, begin, end );
if( begin != end && begin->first < rowBegin && begin->first + begin->second.iDuration > rowEnd )
{
HoldNote hn = GetHoldNote(i);
if( hn.iTrack != iTrack )
continue;
/* A hold note overlaps the whole range. Truncate it, and add the remainder to
* the end. */
TapNote tn1 = begin->second;
TapNote tn2 = tn1;
if( !hn.RangeOverlaps(rowBegin, rowEnd) )
continue;
int iEndRow = begin->first + tn1.iDuration;
int iRow = begin->first;
this->RemoveHoldNote( i );
tn1.iDuration = rowBegin - iRow;
tn2.iDuration = iEndRow - rowEnd;
/* If the range encloses the hold note completely, just delete it. */
if( hn.ContainedByRange(rowBegin, rowEnd) )
continue;
SetTapNote( iTrack, iRow, tn1 );
SetTapNote( iTrack, rowEnd, tn2 );
if( hn.RangeInside(rowBegin, rowEnd) )
{
/* The hold note encloses the range, so we need to split the hold note. */
HoldNote hnLater(hn);
hn.iEndRow = rowBegin;
hnLater.iStartRow = rowEnd;
this->AddHoldNote( hn );
this->AddHoldNote( hnLater );
continue;
}
/* We may have invalidated our iterators. */
GetTapNoteRangeInclusive( iTrack, rowBegin, rowEnd, begin, end );
}
else if( begin != end && begin->first < rowBegin )
{
/* A hold note overlaps the beginning of the range. Truncate it. */
TapNote &tn1 = begin->second;
int iRow = begin->first;
tn1.iDuration = rowBegin - iRow;
if( hn.iStartRow < rowBegin )
hn.iEndRow = min( hn.iEndRow, rowBegin );
else
hn.iStartRow = max( hn.iStartRow, rowEnd );
this->AddHoldNote( hn );
++begin;
}
/* Clear other notes in the region. */
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, iTrack, r, rowBegin, rowEnd )
SetTapNote( iTrack, r, TAP_EMPTY );
if( begin != end )
{
iterator prev = end;
--prev;
TapNote tn = begin->second;
int iRow = prev->first;
if( tn.type == TapNote::hold_head && iRow + tn.iDuration > rowEnd )
{
/* A hold note overlaps the end of the range. Separate it. */
SetTapNote( iTrack, iRow, TAP_EMPTY );
int iAdd = rowEnd - iRow;
tn.iDuration -= iAdd;
iRow += iAdd;
SetTapNote( iTrack, iRow, tn );
end = prev;
}
/* We may have invalidated our iterators. */
GetTapNoteRangeInclusive( iTrack, rowBegin, rowEnd, begin, end );
}
while( begin != end )
{
iterator next = begin;
++next;
RemoveTapNote( iTrack, begin );
begin = next;
}
}
void NoteData::ClearRange( int rowBegin, int rowEnd )
@@ -105,7 +118,6 @@ void NoteData::ClearAll()
{
for( int t=0; t<GetNumTracks(); t++ )
m_TapNotes[t].clear();
m_HoldNotes.clear();
}
/* Copy a range from pFrom to this. (Note that this does *not* overlay;
@@ -114,97 +126,52 @@ void NoteData::CopyRange( const NoteData& from, int rowFromBegin, int rowFromEnd
{
ASSERT( from.GetNumTracks() == GetNumTracks() );
int rowToEnd = (rowFromEnd-rowFromBegin) + rowToBegin;
if( rowFromBegin > rowFromEnd )
return; /* empty range */
const int rowToEnd = (rowFromEnd-rowFromBegin) + rowToBegin;
const int iMoveBy = rowToBegin-rowFromBegin;
/* Clear the region. */
ClearRange( rowToBegin, rowToEnd );
/* Copy everything except for hold notes. */
for( int t=0; t<GetNumTracks(); t++ )
{
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( from, t, iFrom, rowFromBegin, rowFromEnd )
const_iterator begin, end;
from.GetTapNoteRangeInclusive( t, rowFromBegin, rowFromEnd, begin, end );
for( ; begin != end; ++begin )
{
const TapNote &tn = from.GetTapNote( t, iFrom );
switch( tn.type )
{
case TapNote::empty:
case TapNote::hold_tail:
case TapNote::hold_head:
TapNote head = begin->second;
if( head.type == TapNote::empty )
continue;
if( head.type == TapNote::hold_head )
{
int iStartRow = begin->first + iMoveBy;
int iEndRow = iStartRow + head.iDuration;
iStartRow = clamp( iStartRow, rowToBegin, rowToEnd );
iEndRow = clamp( iEndRow, rowToBegin, rowToEnd );
this->AddHoldNote( t, iStartRow, iEndRow, head );
}
else
{
int iTo = begin->first + iMoveBy;
if( iTo >= rowToBegin && iTo <= rowToEnd )
this->SetTapNote( t, iTo, head );
}
int iTo = rowToBegin + iFrom - rowFromBegin;
this->SetTapNote( t, iTo, tn );
}
}
/* Copy hold notes. */
for( int i=0; i<from.GetNumHoldNotes(); i++ ) // for each HoldNote
{
HoldNote hn = from.GetHoldNote(i);
if( !hn.RangeOverlaps(rowFromBegin, rowFromEnd) )
continue;
/* Move the hold note. */
int iMoveBy = rowToBegin-rowFromBegin;
hn.iStartRow += iMoveBy;
hn.iEndRow += iMoveBy;
/* Crop the hold note to the region. */
hn.iStartRow = max( hn.iStartRow, rowToBegin );
hn.iEndRow = min( hn.iEndRow, rowToEnd );
/* The beginning of the hold might match up to the end of an existing hold, the end
* may match with the beginning, or both. */
int iEarlierHoldNote = -1, iLaterHoldNote = -1;
for( int j=0; j<this->GetNumHoldNotes(); ++j ) // for each HoldNote
{
const HoldNote &hn2 = this->GetHoldNote(j);
if( hn2.iEndRow == hn.iStartRow )
iEarlierHoldNote = j;
if( hn2.iStartRow == hn.iEndRow )
iLaterHoldNote = j;
}
if( iEarlierHoldNote != -1 && iLaterHoldNote != -1 )
{
HoldNote &hnEarlier = this->GetHoldNote( iEarlierHoldNote );
const HoldNote &hnLater = this->GetHoldNote( iLaterHoldNote );
hnEarlier.iEndRow = hnLater.iEndRow;
this->RemoveHoldNote( iLaterHoldNote );
}
else if( iEarlierHoldNote == -1 && iLaterHoldNote == -1 )
AddHoldNote( hn );
else if( iEarlierHoldNote != -1 )
{
HoldNote &hn2 = this->GetHoldNote(iEarlierHoldNote);
hn2.iEndRow = hn.iEndRow;
}
else if( iLaterHoldNote != -1 )
{
HoldNote &hn2 = this->GetHoldNote(iLaterHoldNote);
hn2.iStartRow = hn.iStartRow;
}
else
FAIL_M(ssprintf("%i,%i", iEarlierHoldNote, iLaterHoldNote));
}
}
void NoteData::Config( const NoteData& from )
{
SetNumTracks( from.GetNumTracks() );
}
void NoteData::CopyAll( const NoteData& from )
{
Config(from);
SetNumTracks( from.GetNumTracks() );
ClearAll();
for( int c=0; c<GetNumTracks(); c++ )
m_TapNotes[c] = from.m_TapNotes[c];
m_HoldNotes = from.m_HoldNotes;
}
bool NoteData::IsRowEmpty( int row ) const
@@ -326,144 +293,98 @@ int NoteData::GetFirstTrackWithTapOrHoldHead( int row ) const
return -1;
}
void NoteData::AddHoldNote( HoldNote add )
void NoteData::AddHoldNote( int iTrack, int iStartRow, int iEndRow, TapNote tn )
{
ASSERT( add.iStartRow>=0 && add.iEndRow>=0 );
ASSERT( iStartRow>=0 && iEndRow>=0 );
ASSERT( iEndRow >= iStartRow );
tn.iDuration = iEndRow - iStartRow;
/* Include adjacent (non-overlapping) hold notes, since we need to merge with them. */
iterator begin, end;
GetTapNoteRangeInclusive( iTrack, iStartRow, iStartRow+tn.iDuration, begin, end, true );
// look for other hold notes that overlap and merge them
for( int i=0; i<GetNumHoldNotes(); i++ ) // for each HoldNote
/* Look for other hold notes that overlap and merge them into add. */
for( iterator it = begin; it != end; ++it )
{
HoldNote &other = GetHoldNote(i);
if( add.iTrack == other.iTrack && // the tracks correspond
add.RangeOverlaps(other) ) // they overlap
int iRow = it->first;
if( it->second.type == TapNote::hold_head )
{
add.iStartRow = min(add.iStartRow, other.iStartRow);
add.iEndRow = max(add.iEndRow, other.iEndRow);
// delete this HoldNote
RemoveHoldNote( i );
--i;
if( iRow < iStartRow )
{
/* extend tn up */
int iExtendBy = iStartRow - iRow;
tn.iDuration += iExtendBy;
iStartRow -= iExtendBy;
}
else
{
/* extend tn down */
int iEndRow = iRow + it->second.iDuration;
tn.iDuration = iEndRow - iStartRow;
}
}
}
int iAddStartIndex = add.iStartRow;
int iAddEndIndex = add.iEndRow;
/* Remove everything in the range. */
while( begin != end )
{
iterator next = begin;
++next;
// delete TapNotes under this HoldNote
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, add.iTrack, i, iAddStartIndex+1, iAddEndIndex )
SetTapNote( add.iTrack, i, TAP_EMPTY );
RemoveTapNote( iTrack, begin );
begin = next;
}
/* Additionally, if there's a tap note lying at the end of our range, remove it,
* too. */
SetTapNote( iTrack, iEndRow, TAP_EMPTY );
// add a tap note at the start of this hold
SetTapNote( add.iTrack, iAddStartIndex, TAP_ORIGINAL_HOLD_HEAD ); // Hold begin marker. Don't draw this, but do grade it.
m_HoldNotes.push_back(add);
SetTapNote( iTrack, iStartRow, tn );
}
void NoteData::RemoveHoldNote( int iHoldIndex )
/* Return true if a hold note lies on or adjacent to the given spot. */
bool NoteData::IsHoldNoteAtBeat( int iTrack, int iRow, int *pHeadRow ) const
{
ASSERT( iHoldIndex >= 0 && iHoldIndex < GetNumHoldNotes() );
HoldNote& hn = GetHoldNote(iHoldIndex);
const int iHoldStartIndex = hn.iStartRow;
// delete a tap note at the start of this hold
SetTapNote(hn.iTrack, iHoldStartIndex, TAP_EMPTY);
// remove from list
m_HoldNotes.erase(m_HoldNotes.begin()+iHoldIndex, m_HoldNotes.begin()+iHoldIndex+1);
}
/* Return true if a hold note lies on the given spot. Must be in 2sAnd3s. */
bool NoteData::IsHoldNoteAtBeat( int iTrack, int iRow, int *pHeadRow, int *pTailRow ) const
{
/* If neither the actual head nor tail row were requested, search for the head to
* determine the return value. */
int iDummy;
if( pHeadRow == NULL )
pHeadRow = &iDummy;
bool bFoundHead = false;
if( pHeadRow != NULL )
/* Starting at iRow, search upwards. If we find a TapNote::hold_head, we're within
* a hold. If we find a tap, mine or attack, we're not--those never lie within hold
* notes. Ignore autoKeysound. */
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE_REVERSE( *this, iTrack, r, 0, iRow )
{
/* Starting at iRow, search upwards. If we find a TapNote::hold_head, we're within
* a hold. If we find a tap, mine or attack, we're not--those never lie within hold
* notes. Ignore autoKeysound. */
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE_REVERSE( *this, iTrack, r, 0, iRow )
const TapNote &tn = GetTapNote( iTrack, r );
switch( tn.type )
{
const TapNote &tn = GetTapNote( iTrack, r );
switch( tn.type )
{
case TapNote::hold_head:
*pHeadRow = r;
bFoundHead = true;
break;
case TapNote::tap:
case TapNote::mine:
case TapNote::attack:
case TapNote::hold_tail:
case TapNote::hold_head:
// if( tn.iDuration + r <= iRow )
if( tn.iDuration + r < iRow )
return false;
case TapNote::empty:
case TapNote::autoKeysound:
/* ignore */
continue;
default:
FAIL_M( ssprintf("%i", tn.type) );
}
if( bFoundHead )
break;
}
/* If we didn't find a matching head, we're not within a hold note, so don't bother
* searching for a tail. */
if( !bFoundHead )
return false;
}
bool bFoundTail = false;
if( pTailRow != NULL )
{
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, iTrack, r, iRow, MAX_NOTE_ROW )
{
const TapNote &tn = GetTapNote( iTrack, r );
switch( tn.type )
{
case TapNote::hold_tail:
*pTailRow = r;
bFoundTail = true;
break;
case TapNote::tap:
case TapNote::mine:
case TapNote::attack:
return false;
case TapNote::hold_head:
/* If iRow is a hold head, we're within a hold note, and need to continue searching;
* if any row after that is a head, we're not in it, so stop. This is different
* than above, since holds are [head,tail); the row of the tail isn't actually
* part of the hold. */
if( r == iRow )
continue;
return false;
case TapNote::empty:
case TapNote::autoKeysound:
/* ignore */
continue;
default:
FAIL_M( ssprintf("%i", tn.type) );
}
if( bFoundTail )
break;
*pHeadRow = r;
return true;
case TapNote::tap:
case TapNote::mine:
case TapNote::attack:
return false;
case TapNote::empty:
case TapNote::autoKeysound:
/* ignore */
continue;
default:
FAIL_M( ssprintf("%i", tn.type) );
}
if( bFoundHead )
break;
}
return bFoundHead || bFoundTail;
return bFoundHead;
}
int NoteData::GetFirstRow() const
@@ -482,13 +403,6 @@ int NoteData::GetFirstRow() const
iEarliestRowFoundSoFar = min( iEarliestRowFoundSoFar, iRow );
}
for( int i=0; i<GetNumHoldNotes(); i++ )
{
if( iEarliestRowFoundSoFar == -1 ||
GetHoldNote(i).iStartRow < iEarliestRowFoundSoFar )
iEarliestRowFoundSoFar = GetHoldNote(i).iStartRow;
}
if( iEarliestRowFoundSoFar == -1 ) // there are no notes
return 0;
@@ -504,13 +418,14 @@ int NoteData::GetLastRow() const
int iRow = MAX_NOTE_ROW;
if( !GetPrevTapNoteRowForTrack( t, iRow ) )
continue;
iOldestRowFoundSoFar = max( iOldestRowFoundSoFar, iRow );
}
for( int i=0; i<GetNumHoldNotes(); i++ )
{
if( GetHoldNote(i).iEndRow > iOldestRowFoundSoFar )
iOldestRowFoundSoFar = GetHoldNote(i).iEndRow;
/* XXX: We might have a hold note near the end with autoplay sounds
* after it. Do something else with autoplay sounds ... */
const TapNote &tn = GetTapNote( t, iRow );
if( tn.type == TapNote::hold_head )
iRow += tn.iDuration;
iOldestRowFoundSoFar = max( iOldestRowFoundSoFar, iRow );
}
return iOldestRowFoundSoFar;
@@ -576,7 +491,7 @@ int NoteData::RowNeedsHands( const int row ) const
{
case TapNote::mine:
case TapNote::empty:
case TapNote::hold_tail:
case TapNote::hold_head: // handled below
continue; // skip these types - they don't count
}
++iNumNotesThisIndex;
@@ -588,11 +503,10 @@ int NoteData::RowNeedsHands( const int row ) const
if( iNumNotesThisIndex < 3 )
{
/* We have at least one, but not enough. Count holds. */
for( int j=0; j<GetNumHoldNotes(); j++ )
/* We have at least one, but not enough. Count holds. Do count adjacent holds. */
for( int t=0; t<GetNumTracks(); ++t )
{
const HoldNote &hn = GetHoldNote(j);
if( hn.iStartRow+1 <= row && row <= hn.iEndRow )
if( IsHoldNoteAtBeat(t, row) )
++iNumNotesThisIndex;
}
}
@@ -641,82 +555,18 @@ int NoteData::GetNumN( int iMinTaps, int iStartIndex, int iEndIndex ) const
int NoteData::GetNumHoldNotes( int iStartIndex, int iEndIndex ) const
{
int iNumHolds = 0;
for( int i=0; i<GetNumHoldNotes(); i++ )
for( int t=0; t<GetNumTracks(); ++t )
{
const HoldNote &hn = GetHoldNote(i);
if( iStartIndex <= hn.iStartRow && hn.iEndRow <= iEndIndex )
iNumHolds++;
}
return iNumHolds;
}
void NoteData::Convert2sAnd3sToHoldNotes()
{
// Any note will end a hold (not just a TAP_HOLD_TAIL). This makes parsing DWIs much easier.
// Plus, allowing tap notes in the middle of a hold doesn't make sense!
for( int t=0; t<GetNumTracks(); t++ ) // foreach column
{
FOREACH_NONEMPTY_ROW_IN_TRACK( *this, t, r )
const_iterator begin, end;
GetTapNoteRangeExclusive( t, iStartIndex, iEndIndex, begin, end );
for( ; begin != end; ++begin )
{
TapNote head = GetTapNote(t,r);
if( head.type != TapNote::hold_head )
continue; // skip
SetTapNote(t, r, TAP_EMPTY); // clear the hold head marker
// search for end of HoldNote
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, j, r+1, MAX_NOTE_ROW )
{
// End hold on the next note we see. This should be a hold_tail if the
// data is in a consistent state, but doesn't have to be.
if( GetTapNote(t, j).type == TapNote::empty )
continue;
SetTapNote(t, j, TAP_EMPTY);
HoldNote hold(t, r, j);
hold.result = head.HoldResult;
AddHoldNote( hold );
break; // done searching for the end of this hold
}
if( begin->second.type != TapNote::hold_head )
continue;
iNumHolds++;
}
}
}
/* "102000301" ==
* "104444001" */
void NoteData::ConvertHoldNotesTo2sAnd3s()
{
// copy HoldNotes into the new structure, but expand them into 2s and 3s
for( int i=0; i<GetNumHoldNotes(); i++ )
{
const HoldNote &hn = GetHoldNote(i);
/* If they're the same, then they got clamped together, so just ignore it. */
if( hn.iStartRow == hn.iEndRow )
continue;
TapNote head = TAP_ORIGINAL_HOLD_HEAD;
head.HoldResult = hn.result;
SetTapNote( hn.iTrack, hn.iStartRow, head );
SetTapNote( hn.iTrack, hn.iEndRow, TAP_ORIGINAL_HOLD_TAIL );
}
m_HoldNotes.clear();
}
void NoteData::To2sAnd3s( const NoteData& from )
{
CopyAll( from );
ConvertHoldNotesTo2sAnd3s();
}
void NoteData::From2sAnd3s( const NoteData& from )
{
CopyAll( from );
Convert2sAnd3sToHoldNotes();
return iNumHolds;
}
// -1 for iOriginalTracksToTakeFrom means no track
@@ -726,10 +576,6 @@ void NoteData::LoadTransformed( const NoteData& in, int iNewNumTracks, const int
Init();
SetNumTracks( iNewNumTracks );
ConvertHoldNotesTo2sAnd3s();
NoteData Input;
Input.To2sAnd3s( in );
// copy tracks
for( int t=0; t<GetNumTracks(); t++ )
@@ -740,10 +586,8 @@ void NoteData::LoadTransformed( const NoteData& in, int iNewNumTracks, const int
if( iOriginalTrack == -1 )
continue;
m_TapNotes[t] = Input.m_TapNotes[iOriginalTrack];
m_TapNotes[t] = in.m_TapNotes[iOriginalTrack];
}
Convert2sAnd3sToHoldNotes();
}
void NoteData::MoveTapNoteTrack( int dest, int src )
@@ -779,12 +623,9 @@ void NoteData::SetTapNote( int track, int row, const TapNote& t )
void NoteData::GetTracksHeldAtRow( int row, set<int>& addTo )
{
for( unsigned i=0; i<m_HoldNotes.size(); i++ )
{
const HoldNote& hn = m_HoldNotes[i];
if( hn.RowIsInRange(row) )
addTo.insert( hn.iTrack );
}
for( int t=0; t<GetNumTracks(); ++t )
if( IsHoldNoteAtBeat( t, row ) )
addTo.insert( t );
}
int NoteData::GetNumTracksHeldAtRow( int row )
@@ -832,21 +673,117 @@ bool NoteData::GetPrevTapNoteRowForTrack( int track, int &rowInOut ) const
return true;
}
/* Return an iterator range. This can be used to iterate trackwise over a range of
* notes. It's like FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE, except it only requires
* two map searches (iterating is O(1)), but the iterators will become invalid if
* the notes they represent disappear, so you need to pay attention to how you modify
* the data. */
void NoteData::GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const
/* Return an iterator range for [rowBegin,rowEnd). This can be used to efficiently
* iterate trackwise over a range of notes. It's like FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE,
* except it only requires two map searches (iterating is constant time), but the iterators will
* become invalid if the notes they represent disappear, so you need to pay attention to
* how you modify the data. */
void NoteData::GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::iterator &begin, TrackMap::iterator &end )
{
ASSERT_M( iTrack < GetNumTracks(), ssprintf("%i,%i", iTrack, GetNumTracks()) );
ASSERT_M( iStartRow <= iEndRow, ssprintf("%i > %i", iStartRow, iEndRow) );
TrackMap &mapTrack = m_TapNotes[iTrack];
const TrackMap &mapTrack = m_TapNotes[iTrack];
begin = mapTrack.lower_bound( iStartRow );
end = mapTrack.upper_bound( iEndRow );
if( iStartRow > iEndRow )
{
begin = end = mapTrack.end();
return;
}
if( iStartRow == 0 )
begin = mapTrack.begin(); /* optimization */
else if( iStartRow == MAX_NOTE_ROW )
begin = mapTrack.end(); /* optimization */
else
begin = mapTrack.lower_bound( iStartRow );
if( iEndRow == 0 )
end = mapTrack.begin(); /* optimization */
else if( iEndRow == MAX_NOTE_ROW )
end = mapTrack.end(); /* optimization */
else
end = mapTrack.lower_bound( iEndRow );
}
/* Include hold notes that overlap the edges. If a hold note completely surrounds the given
* range, included it, too. If bIncludeAdjacent is true, also include hold notes adjacent to,
* but not overlapping, the edge. */
void NoteData::GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, iterator &begin, iterator &end, bool bIncludeAdjacent )
{
GetTapNoteRange( iTrack, iStartRow, iEndRow, begin, end );
if( begin != this->begin(iTrack) )
{
iterator prev = Decrement(begin);
const TapNote &tn = prev->second;
if( tn.type == TapNote::hold_head )
{
int iHoldStartRow = prev->first;
int iHoldEndRow = iHoldStartRow + tn.iDuration;
if( iHoldEndRow > iStartRow || (bIncludeAdjacent && iHoldEndRow == iStartRow ) )
{
/* The previous note is a hold. */
begin = prev;
}
}
}
if( bIncludeAdjacent && end != this->end(iTrack) )
{
/* Include the next note if it's a hold and starts on iEndRow. */
const TapNote &tn = end->second;
int iHoldStartRow = end->first;
if( tn.type == TapNote::hold_head && iHoldStartRow == iEndRow )
++end;
}
}
void NoteData::GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, iterator &begin, iterator &end )
{
GetTapNoteRange( iTrack, iStartRow, iEndRow, begin, end );
/* If end-1 is a hold_head, and extends beyond iEndRow, exclude it. */
if( begin != end && end != this->begin(iTrack) )
{
iterator prev = end;
--prev;
if( prev->second.type == TapNote::hold_head )
{
int iStartRow = prev->first;
const TapNote &tn = prev->second;
if( iStartRow + tn.iDuration >= iEndRow )
end = prev;
}
}
}
void NoteData::GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const
{
TrackMap::iterator const_begin, const_end;
const_cast<NoteData *>(this)->GetTapNoteRange( iTrack, iStartRow, iEndRow, const_begin, const_end );
begin = const_begin;
end = const_end;
}
void NoteData::GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end, bool bIncludeAdjacent ) const
{
TrackMap::iterator const_begin, const_end;
const_cast<NoteData *>(this)->GetTapNoteRangeInclusive( iTrack, iStartRow, iEndRow, const_begin, const_end, bIncludeAdjacent );
begin = const_begin;
end = const_end;
}
void NoteData::GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const
{
TrackMap::iterator const_begin, const_end;
const_cast<NoteData *>(this)->GetTapNoteRange( iTrack, iStartRow, iEndRow, const_begin, const_end );
begin = const_begin;
end = const_end;
}
bool NoteData::GetNextTapNoteRowForAllTracks( int &rowInOut ) const
{
int iClosestNextRow = MAX_NOTE_ROW;
@@ -873,6 +810,68 @@ bool NoteData::GetNextTapNoteRowForAllTracks( int &rowInOut ) const
}
}
void NoteData::InsertHoldTails()
{
for( int t=0; t<GetNumTracks(); t++ )
{
iterator begin, end;
GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
for( ; begin != end; ++begin )
{
int iRow = begin->first;
const TapNote &tn = begin->second;
if( tn.type != TapNote::hold_head )
continue;
TapNote tail = tn;
tail.type = TapNote::hold_tail;
SetTapNote( t, iRow + tn.iDuration, tail );
}
}
}
void NoteData::RemoveHoldTails()
{
for( int t=0; t<GetNumTracks(); t++ )
{
iterator begin, end;
GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
iterator next;
for( ; begin != end; begin = next )
{
next = begin; ++next;
int iStartRow = begin->first;
const TapNote &tn = begin->second;
if( tn.type != TapNote::hold_head )
continue;
/* Search forward until we find a hold_tail. */
iterator tail = begin;
while( tail != end && tail->second.type != TapNote::hold_tail )
++tail;
if( tail == end )
{
/* If we didn't find one, the hold is invalid; delete it. */
RemoveTapNote( t, begin );
}
else
{
/* Delete the tail, and update iDuration of the head. */
if( next == tail )
++next;
int iEndRow = tail->first;
begin->second.iDuration = iEndRow - iStartRow;
RemoveTapNote( t, tail );
}
}
}
}
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
+37 -31
View File
@@ -11,32 +11,27 @@
#define FOREACH_NONEMPTY_ROW_IN_TRACK( nd, track, row ) \
for( int row = -1; (nd).GetNextTapNoteRowForTrack(track,row); )
#define FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( nd, track, row, start, last ) \
for( int row = start-1; (nd).GetNextTapNoteRowForTrack(track,row) && row <= (last); )
for( int row = start-1; (nd).GetNextTapNoteRowForTrack(track,row) && row < (last); )
#define FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE_REVERSE( nd, track, row, start, last ) \
for( int row = last+1; (nd).GetPrevTapNoteRowForTrack(track,row) && row >= (start); )
for( int row = last; (nd).GetPrevTapNoteRowForTrack(track,row) && row >= (start); )
#define FOREACH_NONEMPTY_ROW_ALL_TRACKS( nd, row ) \
for( int row = -1; (nd).GetNextTapNoteRowForAllTracks(row); )
#define FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( nd, row, start, last ) \
for( int row = start-1; (nd).GetNextTapNoteRowForAllTracks(row) && row <= (last); )
for( int row = start-1; (nd).GetNextTapNoteRowForAllTracks(row) && row < (last); )
class NoteData
{
public:
typedef map<int,TapNote> TrackMap;
typedef map<int,TapNote>::iterator iterator;
typedef map<int,TapNote>::const_iterator const_iterator;
private:
// There's no point in inserting empty notes into the map.
// Any blank space in the map is defined to be empty.
vector<TrackMap> m_TapNotes;
vector<HoldNote> m_HoldNotes;
public:
/* Set up to hold the data in From; same number of tracks, same
* divisor. Doesn't allocate or copy anything. */
void Config( const NoteData& from );
NoteData();
~NoteData();
void Init();
@@ -56,6 +51,30 @@ public:
return TAP_EMPTY;
}
iterator begin( int iTrack ) { return m_TapNotes[iTrack].begin(); }
const_iterator begin( int iTrack ) const { return m_TapNotes[iTrack].begin(); }
iterator end( int iTrack ) { return m_TapNotes[iTrack].end(); }
const_iterator end( int iTrack ) const { return m_TapNotes[iTrack].end(); }
inline iterator FindTapNote( unsigned iTrack, int iRow ) { return m_TapNotes[iTrack].find( iRow ); }
inline const_iterator FindTapNote( unsigned iTrack, int iRow ) const { return m_TapNotes[iTrack].find( iRow ); }
void RemoveTapNote( unsigned iTrack, iterator it ) { m_TapNotes[iTrack].erase( it ); }
/* Return an iterator range including exactly iStartRow to iEndRow. */
void GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, const_iterator &begin, const_iterator &end ) const;
void GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::iterator &begin, TrackMap::iterator &end );
/* Return an iterator range include iStartRow to iEndRow. Extend the range to include
* hold notes overlapping the boundary. */
void GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, const_iterator &begin, const_iterator &end, bool bIncludeAdjacent=false ) const;
void GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, iterator &begin, iterator &end, bool bIncludeAdjacent=false );
/* Return an iterator range include iStartRow to iEndRow. Shrink the range to exclude
* hold notes overlapping the boundary. */
void GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, const_iterator &begin, const_iterator &end ) const;
void GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, iterator &begin, iterator &end );
// Use this to iterate over notes.
// Returns the row index of the first TapNote on the track that has a row
// index > afterRow.
@@ -63,11 +82,10 @@ public:
bool GetNextTapNoteRowForAllTracks( int &rowInOut ) const;
bool GetPrevTapNoteRowForTrack( int track, int &rowInOut ) const;
void GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const;
void MoveTapNoteTrack( int dest, int src );
void SetTapNote( int track, int row, const TapNote& tn );
void AddHoldNote( int iTrack, int iStartRow, int iEndRow, TapNote tn ); // add note hold note merging overlapping HoldNotes and destroying TapNotes underneath
void ClearRangeForTrack( int rowBegin, int rowEnd, int iTrack );
void ClearRange( int rowBegin, int rowEnd );
void ClearAll();
@@ -97,19 +115,10 @@ public:
void GetTracksHeldAtRow( int row, set<int>& addTo );
int GetNumTracksHeldAtRow( int row );
//
// used in edit/record
//
void AddHoldNote( HoldNote newNote ); // add note hold note merging overlapping HoldNotes and destroying TapNotes underneath
void RemoveHoldNote( int index );
HoldNote &GetHoldNote( int index ) { ASSERT( index < (int) m_HoldNotes.size() ); return m_HoldNotes[index]; }
const HoldNote &GetHoldNote( int index ) const { ASSERT( index < (int) m_HoldNotes.size() ); return m_HoldNotes[index]; }
/* Determine if a given spot is within a hold note (being on the tail doesn't count).
* Return true if so. If pHeadRow is non-NULL, return the row of the head. If pTailRow is
* non-NULL, return the row of the tail. This function is faster if pTailRow is NULL.
* Must be in 2sAnd3s. */
bool IsHoldNoteAtBeat( int iTrack, int iRow, int *pHeadRow = NULL, int *pTailRow = NULL ) const;
* non-NULL, return the row of the tail. This function is faster if pTailRow is NULL. */
bool IsHoldNoteAtBeat( int iTrack, int iRow, int *pHeadRow = NULL ) const;
//
// statistics
@@ -127,18 +136,15 @@ public:
// should hands also count as a jump?
int GetNumDoubles( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const { return GetNumN( 2, iStartIndex, iEndIndex ); }
/* optimization: for the default of start to end, use the second (faster) */
int GetNumHoldNotes( const int iStartIndex, const int iEndIndex = MAX_NOTE_ROW ) const;
int GetNumHoldNotes() const { return m_HoldNotes.size(); }
int GetNumHoldNotes( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const;
int RowNeedsHands( int row ) const;
// Transformations
void LoadTransformed( const NoteData& original, int iNewNumTracks, const int iOriginalTrackToTakeFrom[] ); // -1 for iOriginalTracksToTakeFrom means no track
// Convert between HoldNote representation and '2' and '3' markers in TapNotes
void Convert2sAnd3sToHoldNotes();
void ConvertHoldNotesTo2sAnd3s();
void To2sAnd3s( const NoteData& from );
void From2sAnd3s( const NoteData& from );
/* hold_tail is only used to make SM parsing easier. */
void InsertHoldTails();
void RemoveHoldTails(); /* adjusts iDuration */
};
+173 -174
View File
@@ -181,7 +181,8 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData
}
}
}
out.Convert2sAnd3sToHoldNotes();
out.RemoveHoldTails();
}
void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString &notes_out )
@@ -189,8 +190,8 @@ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString &notes_out
//
// Get note data
//
NoteData in;
in.To2sAnd3s( in_ );
NoteData in( in_ );
in.InsertHoldTails();
float fLastBeat = in.GetLastBeat();
int iLastMeasure = int( fLastBeat/BEATS_PER_MEASURE );
@@ -264,21 +265,17 @@ void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &o
return;
}
NoteData Original;
Original.To2sAnd3s( in );
out.Config( in );
out.SetNumTracks( iNewNumTracks );
int iCurTrackOffset = 0;
int iTrackOffsetMin = 0;
int iTrackOffsetMax = abs( iNewNumTracks - Original.GetNumTracks() );
int iTrackOffsetMax = abs( iNewNumTracks - in.GetNumTracks() );
int bOffsetIncreasing = true;
int iLastMeasure = 0;
int iMeasuresSinceChange = 0;
FOREACH_NONEMPTY_ROW_ALL_TRACKS( Original, r )
FOREACH_NONEMPTY_ROW_ALL_TRACKS( in, r )
{
const int iMeasure = r / ROWS_PER_MEASURE;
if( iMeasure != iLastMeasure )
@@ -289,10 +286,10 @@ void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &o
// See if there is a hold crossing the beginning of this measure
bool bHoldCrossesThisMeasure = false;
for( int t=0; t<Original.GetNumTracks(); t++ )
for( int t=0; t<in.GetNumTracks(); t++ )
{
if( Original.IsHoldNoteAtBeat( t, r-1 ) &&
Original.IsHoldNoteAtBeat( t, r ) )
if( in.IsHoldNoteAtBeat( t, r-1 ) &&
in.IsHoldNoteAtBeat( t, r ) )
{
bHoldCrossesThisMeasure = true;
break;
@@ -313,25 +310,20 @@ void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &o
iLastMeasure = iMeasure;
// copy notes in this measure
for( int t=0; t<Original.GetNumTracks(); t++ )
for( int t=0; t<in.GetNumTracks(); t++ )
{
int iOldTrack = t;
int iNewTrack = (iOldTrack + iCurTrackOffset) % iNewNumTracks;
const TapNote &tn = Original.GetTapNote( iOldTrack, r );
const TapNote &tn = in.GetTapNote( iOldTrack, r );
out.SetTapNote( iNewTrack, r, tn );
}
}
out.Convert2sAnd3sToHoldNotes();
}
void NoteDataUtil::LoadOverlapped( const NoteData &input, NoteData &out, int iNewNumTracks )
void NoteDataUtil::LoadOverlapped( const NoteData &in, NoteData &out, int iNewNumTracks )
{
out.SetNumTracks( iNewNumTracks );
NoteData in;
in.To2sAnd3s( input );
/* Keep track of the last source track that put a tap into each destination track,
* and the row of that tap. Then, if two rows are trying to put taps into the
* same row within the shift threshold, shift the newcomer source row. */
@@ -356,13 +348,11 @@ void NoteDataUtil::LoadOverlapped( const NoteData &input, NoteData &out, int iNe
const TapNote &tnFrom = in.GetTapNote( iTrackFrom, row );
if( tnFrom.type == TapNote::empty )
continue;
if( tnFrom.type == TapNote::hold_tail )
continue; /* copied with hold_head */
/* If this is a hold note, find the end. */
int iEndIndex = row;
if( tnFrom.type == TapNote::hold_head )
in.IsHoldNoteAtBeat( iTrackFrom, row, NULL, &iEndIndex );
iEndIndex = row + tnFrom.iDuration;
int &iTrackTo = DestRow[iTrackFrom];
if( LastSourceTrack[iTrackTo] != iTrackFrom )
@@ -391,8 +381,6 @@ void NoteDataUtil::LoadOverlapped( const NoteData &input, NoteData &out, int iNe
}
}
}
out.Convert2sAnd3sToHoldNotes();
}
int FindLongestOverlappingHoldNoteForAnyTrack( const NoteData &in, int iRow )
@@ -400,11 +388,12 @@ int FindLongestOverlappingHoldNoteForAnyTrack( const NoteData &in, int iRow )
int iMaxTailRow = -1;
for( int t=0; t<in.GetNumTracks(); t++ )
{
int iTailRow;
if( !in.IsHoldNoteAtBeat( t, iRow, NULL, &iTailRow ) )
continue;
iMaxTailRow = max( iMaxTailRow, iTailRow );
for( int t=0; t<in.GetNumTracks(); t++ )
{
const TapNote &tn = in.GetTapNote( t, iRow );
if( tn.type == TapNote::hold_head )
iMaxTailRow = max( iMaxTailRow, iRow + tn.iDuration );
}
}
return iMaxTailRow;
@@ -415,9 +404,6 @@ void NoteDataUtil::LoadTransformedLights( const NoteData &in, NoteData &out, int
// reset all notes
out.Init();
NoteData Original;
Original.To2sAnd3s( in );
out.SetNumTracks( iNewNumTracks );
FOREACH_NONEMPTY_ROW_ALL_TRACKS( in, r )
@@ -428,7 +414,7 @@ void NoteDataUtil::LoadTransformedLights( const NoteData &in, NoteData &out, int
int iHoldEnd = -1;
while(1)
{
int iMaxTailRow = FindLongestOverlappingHoldNoteForAnyTrack( Original, r );
int iMaxTailRow = FindLongestOverlappingHoldNoteForAnyTrack( in, r );
if( iMaxTailRow == -1 )
break;
iHoldEnd = iMaxTailRow;
@@ -438,16 +424,12 @@ void NoteDataUtil::LoadTransformedLights( const NoteData &in, NoteData &out, int
if( iHoldEnd != -1 )
{
/* If we found a hold note, add it to all tracks. */
HoldNote hn( 0, iHoldStart, iHoldEnd );
for( int t=0; t<Original.GetNumTracks(); t++ )
{
hn.iTrack = t;
out.AddHoldNote( hn );
}
for( int t=0; t<in.GetNumTracks(); t++ )
out.AddHoldNote( t, iHoldStart, iHoldEnd, TAP_ORIGINAL_HOLD_HEAD );
continue;
}
if( Original.IsRowEmpty( r ) )
if( in.IsRowEmpty( r ) )
continue;
/* Enable every track in the output. */
@@ -554,14 +536,16 @@ float NoteDataUtil::GetChaosRadarValue( const NoteData &in, float fSongSeconds )
void NoteDataUtil::RemoveHoldNotes( NoteData &in, int iStartIndex, int iEndIndex )
{
// turn all the HoldNotes into TapNotes
for( int i=in.GetNumHoldNotes()-1; i>=0; i-- ) // iterate backwards so we can delete
for( int t=0; t<in.GetNumTracks(); ++t )
{
const HoldNote hn = in.GetHoldNote( i );
if( !hn.RangeOverlaps(iStartIndex,iEndIndex) )
continue; // skip
in.RemoveHoldNote( i );
in.SetTapNote( hn.iTrack, hn.iStartRow, TAP_ORIGINAL_TAP );
NoteData::iterator begin, end;
in.GetTapNoteRangeInclusive( t, iStartIndex, iEndIndex, begin, end );
for( ; begin != end; ++begin )
{
if( begin->second.type != TapNote::hold_head )
continue;
begin->second.type = TapNote::tap;
}
}
}
@@ -579,22 +563,19 @@ void NoteDataUtil::RemoveSimultaneousNotes( NoteData &in, int iMaxSimultaneous,
int iTracksToRemove = max( 0, iTotalTracksPressed - iMaxSimultaneous );
for( int t=0; iTracksToRemove>0 && t<in.GetNumTracks(); t++ )
{
if( in.GetTapNote(t,r).type == TapNote::tap )
const TapNote &tn = in.GetTapNote(t,r);
if( tn.type == TapNote::tap )
{
in.SetTapNote( t, r, TAP_EMPTY );
iTracksToRemove--;
}
else if( in.GetTapNote(t,r).type == TapNote::hold_head )
else if( tn.type == TapNote::hold_head )
{
int i;
for( i=0; i<in.GetNumHoldNotes(); i++ )
{
const HoldNote& hn = in.GetHoldNote(i);
if( hn.iStartRow == r )
break;
}
ASSERT( i<in.GetNumHoldNotes() ); // if we hit this, there was a hold head with no hold
in.RemoveHoldNote( i );
int iStartTrack;
if( !in.IsHoldNoteAtBeat( t, r, &iStartTrack ) )
continue;
in.SetTapNote( t, iStartTrack, TAP_EMPTY );
iTracksToRemove--;
}
}
@@ -782,14 +763,13 @@ static void GetTrackMapping( StepsType st, NoteDataUtil::TrackMapping tt, int Nu
static void SuperShuffleTaps( NoteData &inout, int iStartIndex, int iEndIndex )
{
// convert to 2sAnd3s before calling this
ASSERT( inout.GetNumHoldNotes() == 0 );
/* We already did the normal shuffling code above, which did a good job
/*
* We already did the normal shuffling code above, which did a good job
* of shuffling HoldNotes without creating impossible patterns.
* Now, go in and shuffle the TapNotes per-row.
*
* This is only called by NoteDataUtil::Turn. "in" is in 2sAnd3s. */
* This is only called by NoteDataUtil::Turn.
*/
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
{
for( int t1=0; t1<inout.GetNumTracks(); t1++ )
@@ -799,7 +779,6 @@ static void SuperShuffleTaps( NoteData &inout, int iStartIndex, int iEndIndex )
{
case TapNote::empty:
case TapNote::hold_head:
case TapNote::hold_tail:
continue;
}
@@ -834,12 +813,7 @@ void NoteDataUtil::Turn( NoteData &inout, StepsType st, TrackMapping tt, int iSt
tempNoteData.LoadTransformed( inout, inout.GetNumTracks(), iTakeFromTrack );
if( tt == super_shuffle )
{
NoteData tempNoteData2; // write into here as we tranform
tempNoteData2.To2sAnd3s( tempNoteData );
SuperShuffleTaps( tempNoteData2, iStartIndex, iEndIndex ); /* expects 2sAnd3s */
tempNoteData.From2sAnd3s( tempNoteData2 );
}
SuperShuffleTaps( tempNoteData, iStartIndex, iEndIndex );
inout.CopyAll( tempNoteData );
}
@@ -849,7 +823,6 @@ void NoteDataUtil::Backwards( NoteData &inout )
NoteData out;
out.SetNumTracks( inout.GetNumTracks() );
inout.ConvertHoldNotesTo2sAnd3s();
int max_row = inout.GetLastRow();
for( int t=0; t<inout.GetNumTracks(); t++ )
{
@@ -859,34 +832,14 @@ void NoteDataUtil::Backwards( NoteData &inout )
int iRowLater = max_row-r;
TapNote tnEarlier = inout.GetTapNote(t, iRowEarlier);
if( tnEarlier.type == TapNote::hold_head )
iRowLater -= tnEarlier.iDuration;
out.SetTapNote(t, iRowLater, tnEarlier);
}
}
/* Un-flip hold_head and hold_tail. */
for( int t=0; t<out.GetNumTracks(); t++ )
{
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( out, t, r, 0, out.GetLastRow() )
{
TapNote tn = out.GetTapNote( t, r );
if( tn.type != TapNote::hold_tail )
continue;
int iTailRow = r;
while( out.GetNextTapNoteRowForTrack(t,iTailRow) )
{
TapNote tn2 = out.GetTapNote( t, iTailRow );
if( tn2.type != TapNote::hold_head )
continue;
out.SetTapNote( t, r, tn2 );
out.SetTapNote( t, iTailRow, tn );
}
}
}
inout = out;
inout.Convert2sAnd3sToHoldNotes();
}
void NoteDataUtil::SwapSides( NoteData &inout )
@@ -908,27 +861,28 @@ void NoteDataUtil::Little( NoteData &inout, int iStartIndex, int iEndIndex )
{
// filter out all non-quarter notes
for( int t=0; t<inout.GetNumTracks(); t++ )
{
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( inout, t, i, iStartIndex, iEndIndex )
if( i % ROWS_PER_BEAT != 0 )
inout.SetTapNote( t, i, TAP_EMPTY );
{
if( i % ROWS_PER_BEAT == 0 )
continue;
for( int i=inout.GetNumHoldNotes()-1; i>=0; i-- )
if( fmodf(inout.GetHoldNote(i).GetStartBeat(),1) != 0 ) // doesn't start on a beat
inout.RemoveHoldNote( i );
inout.SetTapNote( t, i, TAP_EMPTY );
}
}
}
// Make all all quarter notes into jumps.
void NoteDataUtil::Wide( NoteData &inout, int iStartIndex, int iEndIndex )
{
// Make all all quarter notes into jumps.
inout.ConvertHoldNotesTo2sAnd3s();
/* Start on an even beat. */
iStartIndex = Quantize( iStartIndex, BeatToNoteRow(2.0f) );
iEndIndex = min( iEndIndex, inout.GetLastRow() );
for( int i=iStartIndex; i<iEndIndex; i+=ROWS_PER_BEAT*2 ) // every even beat
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, i, iStartIndex, iEndIndex )
{
if( i % BeatToNoteRow(2.0f) != 0 )
continue; // even beats only
bool bHoldNoteAtBeat = false;
for( int t = 0; !bHoldNoteAtBeat && t < inout.GetNumTracks(); ++t )
if( inout.IsHoldNoteAtBeat(t, i) )
@@ -968,8 +922,6 @@ void NoteDataUtil::Wide( NoteData &inout, int iStartIndex, int iEndIndex )
}
inout.SetTapNote(iTrackToAdd, i, TAP_ADDITION_TAP);
}
inout.Convert2sAnd3sToHoldNotes();
}
void NoteDataUtil::Big( NoteData &inout, int iStartIndex, int iEndIndex )
@@ -1010,8 +962,6 @@ void NoteDataUtil::InsertIntelligentTaps(
ASSERT( iInsertOffsetRows <= iWindowSizeRows );
ASSERT( iWindowSizeRows <= iWindowStrideRows );
inout.ConvertHoldNotesTo2sAnd3s();
bool bRequireNoteAtBeginningOfWindow = !bSkippy;
bool bRequireNoteAtEndOfWindow = true;
@@ -1085,9 +1035,72 @@ void NoteDataUtil::InsertIntelligentTaps(
done_looking_for_track_to_add:
inout.SetTapNote(iTrackOfNoteToAdd, iRowToAdd, TAP_ADDITION_TAP);
}
inout.Convert2sAnd3sToHoldNotes();
}
#if 0
class TrackIterator
{
public:
TrackIterator();
/* If called, iterate only over [iStart,iEnd]. */
void SetRange( int iStart, int iEnd )
{
}
/* If called, pay attention to iTrack only. */
void SetTrack( iTrack );
/* Extend iStart and iEnd to include hold notes overlapping the boundaries. Call SetRange()
* and SetTrack() first. */
void HoldInclusive();
/* Reduce iStart and iEnd to exclude hold notes overlapping the boundaries. Call SetRange()
* and SetTrack() first. */
void HoldExclusive();
/* If called, keep the iterator around. This results in much faster iteration. If used,
* ensure that the current row will always remain valid. SetTrack() must be called first. */
void Fast();
/* Retrieve an iterator for the current row. SetTrack() must be called first (but Fast()
* does not). */
TapNote::iterator Get();
int GetRow() const { return m_iCurrentRow; }
bool Prev();
bool Next();
private:
int m_iStart, m_iEnd;
int m_iTrack;
bool m_bFast;
int m_iCurrentRow;
NoteData::iterator m_Iterator;
/* m_bFast only: */
NoteData::iterator m_Begin, m_End;
};
bool TrackIterator::Next()
{
if( m_bFast )
{
if( m_Iterator ==
}
}
TrackIterator::TrackIterator()
{
m_iStart = 0;
m_iEnd = MAX_NOTE_ROW;
m_iTrack = -1;
}
#endif
void NoteDataUtil::AddMines( NoteData &inout, int iStartIndex, int iEndIndex )
{
@@ -1120,30 +1133,34 @@ void NoteDataUtil::AddMines( NoteData &inout, int iStartIndex, int iEndIndex )
}
// Place mines right after hold so player must lift their foot.
for( int i=0; i<inout.GetNumHoldNotes(); i++ )
for( int iTrack=0; iTrack<inout.GetNumTracks(); ++iTrack )
{
HoldNote &hn = inout.GetHoldNote(i);
int iHoldEndRow = hn.iEndRow;
int iMineRow = iHoldEndRow+BeatToNoteRow(0.5f);
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( inout, iTrack, r, iStartIndex, iEndIndex )
{
const TapNote &tn = inout.GetTapNote( iTrack, r );
if( tn.type != TapNote::hold_head )
continue;
if( iMineRow < iStartIndex || iMineRow > iEndIndex )
continue;
int iMineRow = r + tn.iDuration + BeatToNoteRow(0.5f);
if( iMineRow < iStartIndex || iMineRow > iEndIndex )
continue;
// Only place a mines if there's not another step nearby
int iMineRangeBegin = iMineRow - BeatToNoteRow( 0.5f ) + 1;
int iMineRangeEnd = iMineRow + BeatToNoteRow( 0.5f ) - 1;
if( !inout.IsRangeEmpty(hn.iTrack, iMineRangeBegin, iMineRangeEnd) )
continue;
// Add a mine right after the hold end.
inout.SetTapNote( hn.iTrack, iMineRow, TAP_ADDITION_MINE );
// Only place a mines if there's not another step nearby
int iMineRangeBegin = iMineRow - BeatToNoteRow( 0.5f ) + 1;
int iMineRangeEnd = iMineRow + BeatToNoteRow( 0.5f ) - 1;
if( !inout.IsRangeEmpty(iTrack, iMineRangeBegin, iMineRangeEnd) )
continue;
// Add a mine right after the hold end.
inout.SetTapNote( iTrack, iMineRow, TAP_ADDITION_MINE );
// Convert all notes inout this row to mines.
for( int t=0; t<inout.GetNumTracks(); t++ )
if( inout.GetTapNote(t,iMineRow).type == TapNote::tap )
inout.SetTapNote(t,iMineRow,TAP_ADDITION_MINE);
// Convert all notes in this row to mines.
for( int t=0; t<inout.GetNumTracks(); t++ )
if( inout.GetTapNote(t,iMineRow).type == TapNote::tap )
inout.SetTapNote(t,iMineRow,TAP_ADDITION_MINE);
iRowCount = 0;
iRowCount = 0;
}
}
}
@@ -1152,14 +1169,15 @@ void NoteDataUtil::Echo( NoteData &inout, int iStartIndex, int iEndIndex )
// add 8th note tap "echos" after all taps
int iEchoTrack = -1;
iStartIndex = Quantize( iStartIndex, BeatToNoteRow(0.5f) );
iEndIndex = min( iEndIndex, inout.GetLastRow() );
const int rows_per_interval = BeatToNoteRow( 0.5f );
iStartIndex = Quantize( iStartIndex, rows_per_interval );
// window is one beat wide and slides 1/2 a beat at a time
for( int r=iStartIndex; r<iEndIndex; r+=rows_per_interval )
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
{
if( r % rows_per_interval != 0 )
continue; // 8th notes only
const int iRowWindowBegin = r;
const int iRowWindowEnd = r + rows_per_interval*2;
@@ -1211,7 +1229,6 @@ void NoteDataUtil::Twister( NoteData &inout, int iStartIndex, int iEndIndex )
void NoteDataUtil::ConvertTapsToHolds( NoteData &inout, int iSimultaneousHolds, int iStartIndex, int iEndIndex )
{
// Convert all taps to freezes.
iEndIndex = min( iEndIndex, inout.GetLastRow() );
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
{
int iTrackAddedThisRow = 0;
@@ -1250,7 +1267,7 @@ void NoteDataUtil::ConvertTapsToHolds( NoteData &inout, int iSimultaneousHolds,
if( r2 == r+1 )
r2 = r+BeatToNoteRow(1);
inout.AddHoldNote( HoldNote(t,r,r2) );
inout.AddHoldNote( t, r, r2, TAP_ORIGINAL_HOLD_HEAD );
iTrackAddedThisRow++;
}
dont_add_hold:
@@ -1263,10 +1280,6 @@ dont_add_hold:
void NoteDataUtil::Stomp( NoteData &inout, StepsType st, int iStartIndex, int iEndIndex )
{
// Make all non jumps with ample space around them into jumps.
iEndIndex = min( iEndIndex, inout.GetLastRow() );
int iTrackMapping[MAX_NOTE_TRACKS];
GetTrackMapping( st, stomp, inout.GetNumTracks(), iTrackMapping );
@@ -1315,8 +1328,6 @@ void NoteDataUtil::SnapToNearestNoteType( NoteData &inout, NoteType nt1, NoteTyp
if( nt2 != NOTE_TYPE_INVALID )
fSnapInterval2 = NoteTypeToBeat( nt2 );
inout.ConvertHoldNotesTo2sAnd3s();
// iterate over all TapNotes in the interval and snap them
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, iOldIndex, iStartIndex, iEndIndex )
{
@@ -1338,21 +1349,16 @@ void NoteDataUtil::SnapToNearestNoteType( NoteData &inout, NoteType nt1, NoteTyp
inout.SetTapNote(c, iOldIndex, TAP_EMPTY);
const TapNote &tnOld = inout.GetTapNote(c, iNewIndex);
if( tnNew.type == TapNote::tap &&
(tnOld.type == TapNote::hold_head || tnOld.type == TapNote::hold_tail) )
if( tnNew.type == TapNote::tap && tnOld.type == TapNote::hold_head )
continue; // HoldNotes override TapNotes
/* If two hold tnNew boundaries are getting snapped together,
* merge them. */
if( (tnNew.type == TapNote::hold_head && tnOld.type == TapNote::hold_tail) ||
(tnNew.type == TapNote::hold_tail && tnOld.type == TapNote::hold_head) )
tnNew = TAP_EMPTY;
int iNewDuration = Quantize( tnNew.iDuration, BeatToNoteRow(fSnapInterval1) );
if( iNewDuration == 0 )
continue;
inout.SetTapNote(c, iNewIndex, tnNew );
}
}
inout.Convert2sAnd3sToHoldNotes();
}
@@ -1408,7 +1414,6 @@ void NoteDataUtil::ClearRight( NoteData &inout )
void NoteDataUtil::CollapseToOne( NoteData &inout )
{
inout.ConvertHoldNotesTo2sAnd3s();
FOREACH_NONEMPTY_ROW_ALL_TRACKS( inout, r )
for( int t=0; t<inout.GetNumTracks(); t++ )
if( inout.GetTapNote(t,r).type != TapNote::empty )
@@ -1417,12 +1422,10 @@ void NoteDataUtil::CollapseToOne( NoteData &inout )
inout.SetTapNote(t, r, TAP_EMPTY);
inout.SetTapNote(0, r, tn);
}
inout.Convert2sAnd3sToHoldNotes();
}
void NoteDataUtil::CollapseLeft( NoteData &inout )
{
inout.ConvertHoldNotesTo2sAnd3s();
FOREACH_NONEMPTY_ROW_ALL_TRACKS( inout, r )
{
int iNumTracksFilled = 0;
@@ -1440,7 +1443,6 @@ void NoteDataUtil::CollapseLeft( NoteData &inout )
}
}
}
inout.Convert2sAnd3sToHoldNotes();
}
void NoteDataUtil::ShiftTracks( NoteData &inout, int iShiftBy )
@@ -1655,30 +1657,27 @@ void NoteDataUtil::Scale( NoteData &nd, float fScale )
void NoteDataUtil::ScaleRegion( NoteData &nd, float fScale, int iStartIndex, int iEndIndex )
{
// convert to 2sAnd3s before calling this
ASSERT( nd.GetNumHoldNotes() == 0 );
ASSERT( fScale > 0 );
ASSERT( iStartIndex < iEndIndex );
ASSERT( iStartIndex >= 0 );
NoteData temp1, temp2;
temp1.Config( nd );
temp2.Config( nd );
const int iFirstRowAtEndOfRegion = min( nd.GetLastRow(), iEndIndex );
const int iScaledFirstRowAfterRegion = int(iStartIndex + (iEndIndex - iStartIndex) * fScale);
temp1.SetNumTracks( nd.GetNumTracks() );
temp2.SetNumTracks( nd.GetNumTracks() );
if( iStartIndex != 0 )
temp1.CopyRange( nd, 0, iStartIndex );
if( nd.GetLastRow() > iFirstRowAtEndOfRegion )
temp1.CopyRange( nd, iFirstRowAtEndOfRegion, nd.GetLastRow(), iScaledFirstRowAfterRegion);
temp2.CopyRange( nd, iStartIndex, iFirstRowAtEndOfRegion );
if( iEndIndex != MAX_NOTE_ROW )
{
const int iScaledFirstRowAfterRegion = int(iStartIndex + (iEndIndex - iStartIndex) * fScale);
temp1.CopyRange( nd, iEndIndex, MAX_NOTE_ROW, iScaledFirstRowAfterRegion );
}
temp2.CopyRange( nd, iStartIndex, iEndIndex );
nd.ClearAll();
for( int r=0; r<=temp2.GetLastRow(); r++ )
for( int t=0; t<temp2.GetNumTracks(); t++ )
{
for( int t=0; t<temp2.GetNumTracks(); t++ )
FOREACH_NONEMPTY_ROW_IN_TRACK( temp2, t, r )
{
TapNote tn = temp2.GetTapNote( t, r );
if( tn.type != TapNote::empty )
@@ -1696,8 +1695,6 @@ void NoteDataUtil::ScaleRegion( NoteData &nd, float fScale, int iStartIndex, int
void NoteDataUtil::ShiftRows( NoteData &nd, int iStartIndex, int iRowsToShift )
{
NoteData temp;
temp.SetNumTracks( nd.GetNumTracks() );
int iTakeFromRow = iStartIndex;
int iPasteAtRow = iStartIndex;
@@ -1706,9 +1703,11 @@ void NoteDataUtil::ShiftRows( NoteData &nd, int iStartIndex, int iRowsToShift )
else // delete rows
iTakeFromRow -= iRowsToShift;
temp.CopyRange( nd, iTakeFromRow, nd.GetLastRow() );
nd.ClearRange( min(iTakeFromRow,iPasteAtRow), nd.GetLastRow() );
nd.CopyRange( temp, 0, temp.GetLastRow(), iPasteAtRow );
NoteData temp;
temp.SetNumTracks( nd.GetNumTracks() );
temp.CopyRange( nd, iTakeFromRow, MAX_NOTE_ROW );
nd.ClearRange( min(iTakeFromRow,iPasteAtRow), MAX_NOTE_ROW );
nd.CopyRange( temp, 0, MAX_NOTE_ROW, iPasteAtRow );
}
void NoteDataUtil::RemoveAllTapsOfType( NoteData& ndInOut, TapNote::Type typeToRemove )
+20 -14
View File
@@ -47,17 +47,24 @@ int GetNumNWithScore( const NoteData &in, TapNoteScore tns, int MinTaps, int iSt
return iNumSuccessfulDoubles;
}
int GetNumHoldNotesWithScore( const NoteData &in, HoldNoteScore hns, int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW )
int GetNumHoldNotesWithScore( const NoteData &in, HoldNoteScore hns )
{
int iNumSuccessfulHolds = 0;
for( int i=0; i<in.GetNumHoldNotes(); i++ )
for( int t=0; t<in.GetNumTracks(); ++t )
{
const HoldNote &hn = in.GetHoldNote(i);
if( !hn.ContainedByRange(iStartIndex, iEndIndex) )
continue;
if( hn.result.hns == hns )
iNumSuccessfulHolds++;
NoteData::TrackMap::const_iterator begin, end;
in.GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
for( ; begin != end; ++begin )
{
const TapNote &tn = begin->second;
if( tn.type != TapNote::hold_head )
continue;
if( tn.HoldResult.hns == hns )
++iNumSuccessfulHolds;
}
}
return iNumSuccessfulHolds;
}
@@ -89,7 +96,7 @@ int GetSuccessfulHands( const NoteData &in, int iStartIndex = 0, int iEndIndex =
bool Missed = false;
for( int t=0; t<in.GetNumTracks(); t++ )
{
TapNote tn = in.GetTapNote(t, i);
const TapNote &tn = in.GetTapNote(t, i);
if( tn.type == TapNote::empty )
continue;
if( tn.type == TapNote::mine ) // mines don't count
@@ -102,20 +109,19 @@ int GetSuccessfulHands( const NoteData &in, int iStartIndex = 0, int iEndIndex =
continue;
/* Check hold scores. */
for( int j=0; j<in.GetNumHoldNotes(); j++ )
for( int t=0; t<in.GetNumTracks(); ++t )
{
const HoldNote &hn = in.GetHoldNote(j);
/* Check if the row we're checking is in range. */
if( !hn.RowIsInRange(i) )
int iHeadRow;
if( !in.IsHoldNoteAtBeat( t, i, &iHeadRow ) )
continue;
const TapNote &tn = in.GetTapNote(t, i);
/* If a hold is released *after* a hands containing it, the hands is
* still good. So, ignore the judgement and only examine iLastHeldRow
* to be sure that the hold was still held at the point of this row.
* (Note that if the hold head tap was missed, then iLastHeldRow == i
* and this won't fail--but the tap check above will have already failed.) */
if( hn.result.iLastHeldRow < i )
if( tn.HoldResult.iLastHeldRow < i )
Missed = true;
}
+50 -43
View File
@@ -291,7 +291,7 @@ void NoteField::DrawAreaHighlight( int iStartBeat, int iEndBeat )
fYStartPos = max( fYStartPos, -1000 );
fYEndPos = min( fYEndPos, +5000 );
m_rectAreaHighlight.StretchTo( RectF(-GetWidth()/2, fYStartPos-ARROW_SIZE/2, GetWidth()/2, fYEndPos+ARROW_SIZE/2) );
m_rectAreaHighlight.StretchTo( RectF(-GetWidth()/2, fYStartPos, GetWidth()/2, fYEndPos) );
m_rectAreaHighlight.SetDiffuse( RageColor(1,0,0,0.3f) );
m_rectAreaHighlight.Draw();
}
@@ -558,46 +558,55 @@ void NoteField::DrawPrimitives()
NDMap::iterator CurDisplay = m_BeatToNoteDisplays.begin();
ASSERT( CurDisplay != m_BeatToNoteDisplays.end() );
NDMap::iterator NextDisplay = CurDisplay; ++NextDisplay;
for( int i=0; i < m_pNoteData->GetNumHoldNotes(); i++ )
{
const HoldNote &hn = m_pNoteData->GetHoldNote(i);
if( hn.iTrack != c ) // this HoldNote doesn't belong to this column
continue;
NoteData::TrackMap::const_iterator begin, end;
m_pNoteData->GetTapNoteRangeInclusive( c, iFirstIndexToDraw, iLastIndexToDraw, begin, end );
const HoldNoteResult &Result = hn.result;
if( Result.hns == HNS_OK ) // if this HoldNote was completed
continue; // don't draw anything
for( ; begin != end; ++begin )
{
const TapNote &tn = begin->second; //m_pNoteData->GetTapNote(c, i);
if( tn.type != TapNote::hold_head )
continue; // skip
// If no part of this HoldNote is on the screen, skip it
if( !hn.RangeOverlaps(iFirstIndexToDraw, iLastIndexToDraw) )
continue; // skip
const HoldNoteResult &Result = tn.HoldResult;
if( Result.hns == HNS_OK ) // if this HoldNote was completed
continue; // don't draw anything
// TRICKY: If boomerang is on, then all notes in the range
// [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible.
// Test every note to make sure it's on screen before drawing
float fYStartOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(hn.iStartRow) );
float fYEndOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(hn.iEndRow) );
if( !( iFirstPixelToDraw <= fYEndOffset && fYEndOffset <= iLastPixelToDraw ||
iFirstPixelToDraw <= fYStartOffset && fYStartOffset <= iLastPixelToDraw ||
fYStartOffset < iFirstPixelToDraw && fYEndOffset > iLastPixelToDraw ) )
{
continue; // skip
int iStartRow = begin->first;
int iEndRow = iStartRow + tn.iDuration;
// TRICKY: If boomerang is on, then all notes in the range
// [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible.
// Test every note to make sure it's on screen before drawing
float fYStartOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iStartRow) );
float fYEndOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iEndRow) );
if( !( iFirstPixelToDraw <= fYEndOffset && fYEndOffset <= iLastPixelToDraw ||
iFirstPixelToDraw <= fYStartOffset && fYStartOffset <= iLastPixelToDraw ||
fYStartOffset < iFirstPixelToDraw && fYEndOffset > iLastPixelToDraw ) )
{
continue; // skip
}
const bool bIsActive = tn.HoldResult.bActive;
const bool bIsHoldingNote = tn.HoldResult.bHeld;
if( bIsActive )
SearchForSongBeat()->m_GhostArrowRow.SetHoldIsActive( c );
ASSERT_M( NoteRowToBeat(iStartRow) > -2000, ssprintf("%i %i %i", iStartRow, iEndRow, c) );
SearchForBeat( CurDisplay, NextDisplay, NoteRowToBeat(iStartRow) );
bool bIsInSelectionRange = false;
if( m_iBeginMarker!=-1 && m_iEndMarker!=-1 )
bIsInSelectionRange = (m_iBeginMarker <= iStartRow && iEndRow < m_iEndMarker);
NoteDisplayCols *nd = CurDisplay->second;
HoldNote hn(c, iStartRow, iEndRow);
hn.result = tn.HoldResult;
nd->display[c].DrawHold( hn, bIsHoldingNote, bIsActive, Result, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, false, m_fYReverseOffsetPixels );
}
const bool bIsActive = hn.result.bActive;
const bool bIsHoldingNote = hn.result.bHeld;
if( bIsActive )
SearchForSongBeat()->m_GhostArrowRow.SetHoldIsActive( hn.iTrack );
ASSERT_M( NoteRowToBeat(hn.iStartRow) > -2000, ssprintf("%i %i %i", hn.iStartRow, hn.iEndRow, hn.iTrack) );
SearchForBeat( CurDisplay, NextDisplay, NoteRowToBeat(hn.iStartRow) );
bool bIsInSelectionRange = false;
if( m_iBeginMarker!=-1 && m_iEndMarker!=-1 )
bIsInSelectionRange = hn.ContainedByRange( m_iBeginMarker, m_iEndMarker );
NoteDisplayCols *nd = CurDisplay->second;
nd->display[c].DrawHold( hn, bIsHoldingNote, bIsActive, Result, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, false, m_fYReverseOffsetPixels );
}
@@ -615,11 +624,12 @@ void NoteField::DrawPrimitives()
{
int i = begin->first;
const TapNote &tn = begin->second; //m_pNoteData->GetTapNote(c, i);
if( tn.type == TapNote::empty ) // no note here
continue; // skip
if( tn.type == TapNote::hold_head ) // this is a HoldNote begin marker. Grade it, but don't draw
switch( tn.type )
{
case TapNote::empty: // no note here
case TapNote::hold_head:
continue; // skip
}
/* Don't draw hidden (fully judged) steps. */
if( tn.result.bHidden )
@@ -656,10 +666,7 @@ void NoteField::DrawPrimitives()
bool bIsInSelectionRange = false;
if( m_iBeginMarker!=-1 && m_iEndMarker!=-1 )
{
int iBeat = i;
bIsInSelectionRange = m_iBeginMarker<=iBeat && iBeat<=m_iEndMarker;
}
bIsInSelectionRange = m_iBeginMarker<=i && i<m_iEndMarker;
bool bIsAddition = (tn.source == TapNote::addition);
bool bIsMine = (tn.type == TapNote::mine);
+5 -2
View File
@@ -21,7 +21,6 @@ struct TapNoteResult
bool bHidden;
};
/* This HoldNote data is persisted when converted to 2sAnd3s. */
struct HoldNoteResult
{
HoldNoteScore hns;
@@ -112,13 +111,17 @@ struct TapNote
COMPARE(fAttackDurationSeconds);
COMPARE(bKeysound);
COMPARE(iKeysoundIndex);
COMPARE(iDuration);
#undef COMPARE
return true;
}
/* hold_head only: */
int iDuration;
TapNoteResult result;
/* This is valid for hold_head when in 2sAnd3s. */
/* hold_head only: */
HoldNoteResult HoldResult;
};
+25 -8
View File
@@ -280,7 +280,6 @@ bool DWILoader::LoadFromDWITokens(
newNoteData.SetTapNote(iCol1, iIndex, TAP_ORIGINAL_HOLD_HEAD);
if( iCol2 != -1 )
newNoteData.SetTapNote(iCol2, iIndex, TAP_ORIGINAL_HOLD_HEAD);
}
}
while( jump );
@@ -291,8 +290,29 @@ bool DWILoader::LoadFromDWITokens(
}
}
// this will expand the HoldNote begin markers we wrote into actual HoldNotes
newNoteData.Convert2sAnd3sToHoldNotes();
/* Fill in iDuration. */
for( int t=0; t<newNoteData.GetNumTracks(); ++t )
{
FOREACH_NONEMPTY_ROW_IN_TRACK( newNoteData, t, iHeadRow )
{
TapNote tn = newNoteData.GetTapNote( t, iHeadRow );
if( tn.type != TapNote::hold_head )
continue;
int iTailRow = iHeadRow;
while( newNoteData.GetNextTapNoteRowForTrack(t, iTailRow) )
{
const TapNote &TailTap = newNoteData.GetTapNote( t, iTailRow );
if( TailTap.type == TapNote::empty )
continue;
newNoteData.SetTapNote( t, iTailRow, TAP_EMPTY );
tn.iDuration = iTailRow - iHeadRow;
newNoteData.SetTapNote( t, iHeadRow, tn );
break;
}
}
}
ASSERT( newNoteData.GetNumTracks() > 0 );
@@ -303,11 +323,8 @@ bool DWILoader::LoadFromDWITokens(
return true;
}
/* STUPID ALERT!
* This value can be in either "HH:MM:SS.sssss", "MM:SS.sssss", "SSS.sssss"
* or milliseconds.
* What's even more dumb is that the value can contain a ':'. Colon is supposed to be a parameter separator!
*/
/* This value can be in either "HH:MM:SS.sssss", "MM:SS.sssss", "SSS.sssss"
* or milliseconds. */
float DWILoader::ParseBrokenDWITimestamp(const CString &arg1, const CString &arg2, const CString &arg3)
{
if(arg1.empty()) return 0;
+5 -12
View File
@@ -28,14 +28,13 @@ void KSFLoader::RemoveHoles( NoteData &out, const Song &song )
// else
// LastRow = out.GetLastRow();
NoteData tmp;
tmp.SetNumTracks(out.GetNumTracks());
tmp.CopyRange( &out, FromRow, out.GetLastRow() );
out.ClearRange( FromRow, out.GetLastRow() );
out.CopyRange( &tmp, 0, tmp.GetLastRow(), ToRow );
tmp.SetNumTracks( out.GetNumTracks() );
tmp.CopyRange( &out, FromRow, MAX_NOTE_ROW );
out.ClearRange( FromRow, MAX_NOTE_ROW );
out.CopyRange( &tmp, 0, MAX_NOTE_ROW, ToRow );
}
/*
out.ConvertHoldNotesTo2sAnd3s();
for( t = 0; t < notedata.GetNumTracks(); ++t )
{
const float CurBPM = song.GetBPMAtBeat( NoteRowToBeat(row) );
@@ -54,7 +53,6 @@ void KSFLoader::RemoveHoles( NoteData &out, const Song &song )
notedata.SetTapNote( t, row, TAP_EMPTY );
}
}
out.Convert2sAnd3sToHoldNotes();
*/
}
#endif
@@ -188,12 +186,7 @@ bool KSFLoader::LoadFromKSFFile( const CString &sPath, Steps &out, const Song &s
if( iHoldStartRow[t] != -1 ) // this ends the hold
{
HoldNote hn (
t, /* button */
BeatToNoteRow(iHoldStartRow[t]/(float)iTickCount), /* start */
BeatToNoteRow((r-1)/(float)iTickCount) /* end */
);
notedata.AddHoldNote( hn );
notedata.AddHoldNote( t, BeatToNoteRow(iHoldStartRow[t]/(float)iTickCount), BeatToNoteRow((r-1)/(float)iTickCount), TAP_ORIGINAL_HOLD_HEAD );
iHoldStartRow[t] = -1;
}
-1
View File
@@ -190,7 +190,6 @@ void NotesWriterDWI::WriteDWINotesField( RageFile &f, const Steps &out, int star
{
NoteData notedata;
out.GetNoteData( notedata );
notedata.ConvertHoldNotesTo2sAnd3s();
const int iLastMeasure = int( notedata.GetLastBeat()/BEATS_PER_MEASURE );
for( int m=0; m<=iLastMeasure; m++ ) // foreach measure
+95 -97
View File
@@ -338,106 +338,112 @@ void Player::Update( float fDeltaTime )
//
// update HoldNotes logic
//
for( int i=0; i < m_NoteData.GetNumHoldNotes(); i++ ) // for each HoldNote
for( int iTrack=0; iTrack<m_NoteData.GetNumTracks(); ++iTrack )
{
HoldNote &hn = m_NoteData.GetHoldNote(i);
// set hold flags so NoteField can do intelligent drawing
hn.result.bHeld = false;
hn.result.bActive = false;
HoldNoteScore hns = hn.result.hns;
if( hns != HNS_NONE ) // if this HoldNote already has a result
continue; // we don't need to update the logic for this one
if( iSongRow < hn.iStartRow )
continue; // hold hasn't happened yet
// TODO: Remove use of PlayerNumber.
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
const StyleInput StyleI( pn, hn.iTrack );
const GameInput GameI = GAMESTATE->GetCurrentStyle()->StyleInputToGameInput( StyleI );
// if they got a bad score or haven't stepped on the corresponding tap yet
const TapNoteScore tns = m_NoteData.GetTapNote( hn.iTrack, hn.iStartRow ).result.tns;
const bool bSteppedOnTapNote = tns != TNS_NONE && tns != TNS_MISS; // did they step on the start of this hold?
float fLife = hn.result.fLife;
bool bIsHoldingButton = INPUTMAPPER->IsButtonDown( GameI );
// TODO: Make the CPU miss sometimes.
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
bIsHoldingButton = true;
if( bSteppedOnTapNote && fLife != 0 )
// Since this is being called every frame, let's not check the whole array every time.
// Instead, only check 1 beat back. Even 1 is overkill.
const int iStartCheckingAt = max( 0, iSongRow-BeatToNoteRow(1) );
NoteData::iterator begin, end;
m_NoteData.GetTapNoteRangeInclusive( iTrack, iStartCheckingAt, iSongRow, begin, end );
for( ; begin != end; ++begin )
{
/* This hold note is not judged and we stepped on its head. Update iLastHeldRow.
* Do this even if we're a little beyond the end of the hold note, to make sure
* iLastHeldRow is clamped to iEndRow if the hold note is held all the way. */
hn.result.iLastHeldRow = min( iSongRow, hn.iEndRow );
}
TapNote &tn = begin->second;
if( tn.type != TapNote::hold_head )
continue;
int iRow = begin->first;
// If the song beat is in the range of this hold:
if( hn.RowIsInRange(iSongRow) )
{
// set hold flag so NoteField can do intelligent drawing
hn.result.bHeld = bIsHoldingButton && bSteppedOnTapNote;
hn.result.bActive = bSteppedOnTapNote;
// set hold flags so NoteField can do intelligent drawing
tn.HoldResult.bHeld = false;
tn.HoldResult.bActive = false;
if( bSteppedOnTapNote && bIsHoldingButton )
HoldNoteScore hns = tn.HoldResult.hns;
if( hns != HNS_NONE ) // if this HoldNote already has a result
continue; // we don't need to update the logic for this one
// TODO: Remove use of PlayerNumber.
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
// if they got a bad score or haven't stepped on the corresponding tap yet
const TapNoteScore tns = tn.result.tns;
const bool bSteppedOnTapNote = tns != TNS_NONE && tns != TNS_MISS; // did they step on the start of this hold?
bool bIsHoldingButton;
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
{
// Increase life
fLife = 1;
if( m_pNoteField )
m_pNoteField->DidHoldNote( hn.iTrack ); // update the "electric ghost" effect
// TODO: Make the CPU miss sometimes.
bIsHoldingButton = true;
}
else
{
/* What is this conditional for? It causes a problem: if a hold note
* begins on a freeze, you can tap it and then release it for the
* duration of the freeze; life doesn't count down until we're
* past the first beat. */
// if( fSongBeat-hn.fStartBeat > GAMESTATE->m_fCurBPS * GetMaxStepDistanceSeconds() )
// {
const StyleInput StyleI( pn, iTrack );
const GameInput GameI = GAMESTATE->GetCurrentStyle()->StyleInputToGameInput( StyleI );
bIsHoldingButton = INPUTMAPPER->IsButtonDown( GameI );
}
int iEndRow = iRow + tn.iDuration;
float fLife = tn.HoldResult.fLife;
if( bSteppedOnTapNote && fLife != 0 )
{
/* This hold note is not judged and we stepped on its head. Update iLastHeldRow.
* Do this even if we're a little beyond the end of the hold note, to make sure
* iLastHeldRow is clamped to iEndRow if the hold note is held all the way. */
tn.HoldResult.iLastHeldRow = min( iSongRow, iEndRow );
}
// If the song beat is in the range of this hold:
if( iRow <= iSongRow && iRow <= iEndRow )
{
// set hold flag so NoteField can do intelligent drawing
tn.HoldResult.bHeld = bIsHoldingButton && bSteppedOnTapNote;
tn.HoldResult.bActive = bSteppedOnTapNote;
if( bSteppedOnTapNote && bIsHoldingButton )
{
// Increase life
fLife = 1;
if( m_pNoteField )
m_pNoteField->DidHoldNote( iTrack ); // update the "electric ghost" effect
}
else
{
// Decrease life
fLife -= fDeltaTime/ADJUSTED_WINDOW(OK);
fLife = max( fLife, 0 ); // clamp
// }
}
}
/* check for NG. If the head was missed completely, don't count an NG. */
if( bSteppedOnTapNote && fLife == 0 ) // the player has not pressed the button for a long time!
hns = HNS_NG;
// check for OK
if( iSongRow >= iEndRow && bSteppedOnTapNote && fLife > 0 ) // if this HoldNote is in the past
{
fLife = 1;
hns = HNS_OK;
if( m_pNoteField )
m_pNoteField->DidTapNote( iTrack, TNS_PERFECT, true ); // bright ghost flash
}
if( hns != HNS_NONE )
{
/* this note has been judged */
HandleHoldScore( hns, tns );
m_HoldJudgment[iTrack].SetHoldJudgment( hns );
int ms_error = (hns == HNS_OK)? 0:MAX_PRO_TIMING_ERROR;
if( m_pPlayerStageStats )
m_pPlayerStageStats->iTotalError += ms_error;
if( hns == HNS_NG ) /* don't show a 0 for an OK */
m_ProTimingDisplay.SetJudgment( ms_error, TNS_MISS );
}
tn.HoldResult.fLife = fLife;
tn.HoldResult.hns = hns;
}
/* check for NG. If the head was missed completely, don't count
* an NG. */
if( bSteppedOnTapNote && fLife == 0 ) // the player has not pressed the button for a long time!
hns = HNS_NG;
// check for OK
if( iSongRow >= hn.iEndRow && bSteppedOnTapNote && fLife > 0 ) // if this HoldNote is in the past
{
fLife = 1;
hns = HNS_OK;
if( m_pNoteField )
m_pNoteField->DidTapNote( StyleI.col, TNS_PERFECT, true ); // bright ghost flash
}
if( hns != HNS_NONE )
{
/* this note has been judged */
HandleHoldScore( hns, tns );
m_HoldJudgment[hn.iTrack].SetHoldJudgment( hns );
int ms_error = (hns == HNS_OK)? 0:MAX_PRO_TIMING_ERROR;
if( m_pPlayerStageStats )
m_pPlayerStageStats->iTotalError += ms_error;
if( hns == HNS_NG ) /* don't show a 0 for an OK */
m_ProTimingDisplay.SetJudgment( ms_error, TNS_MISS );
}
hn.result.fLife = fLife;
hn.result.hns = hns;
}
// TODO: Remove use of PlayerNumber.
@@ -475,7 +481,6 @@ void Player::Update( float fDeltaTime )
}
}
// process transforms that are waiting to be applied
ApplyWaitingTransforms();
@@ -1095,7 +1100,7 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
}
// Since this is being called every frame, let's not check the whole array every time.
// Instead, only check 1 beat back. Even 10 is overkill.
// Instead, only check 1 beat back. Even 1 is overkill.
const int iStartCheckingAt = max( 0, iMissIfOlderThanThisIndex-BeatToNoteRow(1) );
//LOG->Trace( "iStartCheckingAt: %d iMissIfOlderThanThisIndex: %d", iStartCheckingAt, iMissIfOlderThanThisIndex );
@@ -1217,14 +1222,7 @@ void Player::RandomizeNotes( int iNoteRow )
continue;
/* Make sure the destination row isn't in the middle of a hold. */
bool bSkip = false;
for( int i = 0; !bSkip && i < m_NoteData.GetNumHoldNotes(); ++i )
{
const HoldNote &hn = m_NoteData.GetHoldNote(i);
if( hn.iTrack == iSwapWith && hn.RowIsInRange(iNewNoteRow) )
bSkip = true;
}
if( bSkip )
if( m_NoteData.IsHoldNoteAtBeat(iSwapWith, iNoteRow) )
continue;
m_NoteData.SetTapNote( t, iNewNoteRow, t2 );
+15 -29
View File
@@ -682,8 +682,7 @@ void ScreenEdit::Update( float fDeltaTime )
fEndBeat = Quantize( fEndBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) );
// create a new hold note
HoldNote newHN( t, BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat) );
m_NoteDataRecord.AddHoldNote( newHN );
m_NoteDataRecord.AddHoldNote( t, BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat), TAP_ORIGINAL_HOLD_HEAD );
}
}
}
@@ -910,13 +909,11 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
break;
// check for to see if the user intended to remove a HoldNote
for( int i=0; i<m_NoteDataEdit.GetNumHoldNotes(); i++ ) // for each HoldNote
{
const HoldNote &hn = m_NoteDataEdit.GetHoldNote(i);
if( iCol == hn.iTrack && // the notes correspond
hn.RowIsInRange(iSongIndex) ) // the cursor lies within this HoldNote
int iHeadRow;
if( m_NoteDataEdit.IsHoldNoteAtBeat( iCol, iSongIndex, &iHeadRow ) )
{
m_NoteDataEdit.RemoveHoldNote( i );
m_NoteDataEdit.SetTapNote( iCol, iHeadRow, TAP_EMPTY );
return;
}
}
@@ -1003,7 +1000,9 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
}
const float fStartBeat = GAMESTATE->m_fSongBeat;
const float fEndBeat = GAMESTATE->m_fSongBeat + fBeatsToMove;
float fEndBeat = max( GAMESTATE->m_fSongBeat + fBeatsToMove, 0 );
fEndBeat = Quantize( fEndBeat , NoteTypeToBeat(m_SnapDisplay.GetNoteType()) );
GAMESTATE->m_fSongBeat = fEndBeat;
// check to see if they're holding a button
for( int n=0; n<=9; n++ ) // for each number key
@@ -1022,12 +1021,13 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
continue;
// create a new hold note
HoldNote newHN( iCol, BeatToNoteRow(min(fStartBeat, fEndBeat)), BeatToNoteRow(max(fStartBeat, fEndBeat)) );
int iStartRow = BeatToNoteRow( min(fStartBeat, fEndBeat) );
int iEndRow = BeatToNoteRow( max(fStartBeat, fEndBeat) );
newHN.iStartRow = max(newHN.iStartRow, 0);
newHN.iEndRow = max(newHN.iEndRow, 0);
iStartRow = max( iStartRow, 0 );
iEndRow = max( iEndRow, 0 );
m_NoteDataEdit.AddHoldNote( newHN );
m_NoteDataEdit.AddHoldNote( iCol, iStartRow, iEndRow, TAP_ORIGINAL_HOLD_HEAD );
}
if( EditIsBeingPressed(EDIT_BUTTON_SCROLL_SELECT) )
@@ -1054,9 +1054,6 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
}
}
GAMESTATE->m_fSongBeat += fBeatsToMove;
GAMESTATE->m_fSongBeat = max( GAMESTATE->m_fSongBeat, 0 );
GAMESTATE->m_fSongBeat = Quantize( GAMESTATE->m_fSongBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) );
m_soundChangeLine.Play();
}
break;
@@ -1947,16 +1944,14 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, int* iAnswers )
break;
case paste_at_current_beat:
{
int iSrcFirstRow = 0;
int iSrcLastRow = BeatToNoteRow( m_Clipboard.GetLastBeat() );
int iDestFirstRow = BeatToNoteRow( GAMESTATE->m_fSongBeat );
m_NoteDataEdit.CopyRange( m_Clipboard, iSrcFirstRow, iSrcLastRow, iDestFirstRow );
m_NoteDataEdit.CopyRange( m_Clipboard, 0, MAX_NOTE_ROW, iDestFirstRow );
}
break;
case paste_at_begin_marker:
{
ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 );
m_NoteDataEdit.CopyRange( m_Clipboard, 0, m_Clipboard.GetLastRow(), m_NoteFieldEdit.m_iBeginMarker );
m_NoteDataEdit.CopyRange( m_Clipboard, 0, MAX_NOTE_ROW, m_NoteFieldEdit.m_iBeginMarker );
}
break;
case clear:
@@ -2069,8 +2064,6 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, int* iAnswers )
default: ASSERT(0);
}
m_Clipboard.ConvertHoldNotesTo2sAnd3s();
switch( at )
{
case compress_2x: NoteDataUtil::Scale( m_Clipboard, fScale ); break;
@@ -2082,8 +2075,6 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, int* iAnswers )
default: ASSERT(0);
}
m_Clipboard.Convert2sAnd3sToHoldNotes();
int iOldClipboardBeats = m_NoteFieldEdit.m_iEndMarker - m_NoteFieldEdit.m_iBeginMarker;
int iNewClipboardBeats = lrintf( iOldClipboardBeats * fScale );
int iDeltaBeats = iNewClipboardBeats - iOldClipboardBeats;
@@ -2094,7 +2085,6 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, int* iAnswers )
HandleAreaMenuChoice( paste_at_begin_marker, NULL );
const vector<Steps*> sIter = m_pSong->GetAllSteps();
NoteData ndTemp;
CString sTempStyle, sTempDiff;
for( unsigned i = 0; i < sIter.size(); i++ )
{
@@ -2106,10 +2096,9 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, int* iAnswers )
(sIter[i]->GetDifficulty() == GAMESTATE->m_pCurSteps[PLAYER_1]->GetDifficulty()) )
continue;
NoteData ndTemp;
sIter[i]->GetNoteData( ndTemp );
ndTemp.ConvertHoldNotesTo2sAnd3s();
NoteDataUtil::ScaleRegion( ndTemp, fScale, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker );
ndTemp.Convert2sAnd3sToHoldNotes();
sIter[i]->SetNoteData( ndTemp );
}
@@ -2286,11 +2275,8 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, int* iAnswers )
fStopLength *= fBPMatPause;
fStopLength /= 60;
// don't move the step from where it is, just move everything later
m_NoteDataEdit.ConvertHoldNotesTo2sAnd3s();
NoteDataUtil::ShiftRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopLength) );
m_pSong->m_Timing.ShiftRows( BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopLength) );
m_NoteDataEdit.Convert2sAnd3sToHoldNotes();
}
// Hello and welcome to I FEEL STUPID :-)
break;
+3 -7
View File
@@ -1114,9 +1114,6 @@ void ScreenGameplay::LoadNextSong()
NoteDataUtil::LoadTransformedLights( TapNoteData, m_CabinetLightsNoteData, GameManager::StepsTypeToNumTracks(STEPS_TYPE_LIGHTS_CABINET) );
}
}
// Convert to 2sAnd3s so that we can check if we're inside a hold with IsHoldNoteAtBeat.
m_CabinetLightsNoteData.ConvertHoldNotesTo2sAnd3s();
}
}
@@ -1615,12 +1612,11 @@ void ScreenGameplay::UpdateLights()
FOREACH_EnabledPlayer( pn )
{
// check if a hold should be active
for( int i=0; i < m_Player[pn].m_NoteData.GetNumHoldNotes(); i++ ) // for each HoldNote
for( int t=0; t < m_Player[pn].m_NoteData.GetNumTracks(); ++t )
{
const HoldNote &hn = m_Player[pn].m_NoteData.GetHoldNote(i);
if( hn.iStartRow <= iSongRow && iSongRow <= hn.iEndRow )
if( m_Player[pn].m_NoteData.IsHoldNoteAtBeat( t, iSongRow ) )
{
StyleInput si( pn, hn.iTrack );
StyleInput si( pn, t );
GameInput gi = pStyle->StyleInputToGameInput( si );
bBlinkGameButton[gi.controller][gi.button] |= true;
}