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:
+343
-344
@@ -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
@@ -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
@@ -181,7 +181,8 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData
|
||||
}
|
||||
}
|
||||
}
|
||||
out.Convert2sAnd3sToHoldNotes();
|
||||
|
||||
out.RemoveHoldTails();
|
||||
}
|
||||
|
||||
void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString ¬es_out )
|
||||
@@ -189,8 +190,8 @@ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString ¬es_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 )
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user