simplify TapAttack storage:
store attack info in TapNote in the SMNoteData string, store the attack params inline (like with keysounds) play keysounds when hit still doesn't play keysounds on a miss still doesn't play autoKeysounds
This commit is contained in:
+18
-2
@@ -14,10 +14,26 @@ struct Attack
|
||||
bool bOn; // for GAMESTATE
|
||||
bool bGlobal; // true for song-wide course mods
|
||||
|
||||
void MakeBlank() { sModifier=""; fStartSecond = -1; bOn = false; bGlobal = false; }
|
||||
Attack() { MakeBlank(); }
|
||||
Attack(
|
||||
AttackLevel level_,
|
||||
float fStartSecond_,
|
||||
float fSecsRemaining_,
|
||||
CString sModifier_,
|
||||
bool bOn_,
|
||||
bool bGlobal_ )
|
||||
{
|
||||
level = level_;
|
||||
fStartSecond = fStartSecond_;
|
||||
fSecsRemaining = fSecsRemaining_;
|
||||
sModifier = sModifier_;
|
||||
bOn = bOn_;
|
||||
bGlobal = bGlobal_;
|
||||
}
|
||||
|
||||
void GetAttackBeats( const Song *song, PlayerNumber pn, float &fStartBeat, float &fEndBeat ) const;
|
||||
bool IsBlank() { return sModifier.empty(); }
|
||||
void MakeBlank() { sModifier=""; }
|
||||
Attack() { fStartSecond = -1; bOn = false; bGlobal = false; }
|
||||
bool operator== ( const Attack &rhs ) const;
|
||||
bool ContainsTransformOrTurn() const;
|
||||
};
|
||||
|
||||
@@ -1207,18 +1207,20 @@ void GameState::SetNoteSkinForBeatRange( PlayerNumber pn, CString sNoteSkin, flo
|
||||
|
||||
/* This is called to launch an attack, or to queue an attack if a.fStartSecond
|
||||
* is set. This is also called by GameState::Update when activating a queued attack. */
|
||||
void GameState::LaunchAttack( PlayerNumber target, Attack a )
|
||||
void GameState::LaunchAttack( PlayerNumber target, const Attack& a )
|
||||
{
|
||||
LOG->Trace( "Launch attack '%s' against P%d at %f", a.sModifier.c_str(), target+1, a.fStartSecond );
|
||||
|
||||
Attack attack = a;
|
||||
|
||||
/* If fStartSecond is -1, it means "launch as soon as possible". For m_ActiveAttacks,
|
||||
* mark the real time it's starting (now), so Update() can know when the attack started
|
||||
* so it can be removed later. For m_ModsToApply, leave the -1 in, so Player::Update
|
||||
* knows to apply attack transforms correctly. (yuck) */
|
||||
m_ModsToApply[target].push_back( a );
|
||||
if( a.fStartSecond == -1 )
|
||||
a.fStartSecond = this->m_fMusicSeconds;
|
||||
m_ActiveAttacks[target].push_back( a );
|
||||
m_ModsToApply[target].push_back( attack );
|
||||
if( attack.fStartSecond == -1 )
|
||||
attack.fStartSecond = this->m_fMusicSeconds;
|
||||
m_ActiveAttacks[target].push_back( attack );
|
||||
|
||||
this->RebuildPlayerOptionsFromActiveAttacks( target );
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public:
|
||||
bool m_bAttackBeganThisUpdate[NUM_PLAYERS]; // flag for other objects to watch (play sounds)
|
||||
bool m_bAttackEndedThisUpdate[NUM_PLAYERS]; // flag for other objects to watch (play sounds)
|
||||
void GetUndisplayedBeats( PlayerNumber pn, float TotalSeconds, float &StartBeat, float &EndBeat ) const; // only meaningful when a NoteField is in use
|
||||
void LaunchAttack( PlayerNumber target, Attack aa );
|
||||
void LaunchAttack( PlayerNumber target, const Attack& a );
|
||||
void RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn );
|
||||
void RemoveAllActiveAttacks(); // called on end of song
|
||||
void RemoveActiveAttacksForPlayer( PlayerNumber pn, AttackLevel al=NUM_ATTACK_LEVELS /*all*/ );
|
||||
|
||||
+13
-79
@@ -77,20 +77,11 @@ void NoteData::CopyRange( const NoteData& from, int iFromIndexBegin, int iFromIn
|
||||
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( From, t, from, iFromIndexBegin, iFromIndexEnd )
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( From, t, iFrom, iFromIndexBegin, iFromIndexEnd )
|
||||
{
|
||||
int to = iToIndexBegin + from - iFromIndexBegin;
|
||||
|
||||
TapNote tn = From.GetTapNote( t, from );
|
||||
if( tn.type == TapNote::attack )
|
||||
{
|
||||
Attack attack = From.GetAttackAt( t, from );
|
||||
To.SetTapAttackNote( t, to, attack );
|
||||
}
|
||||
else
|
||||
{
|
||||
To.SetTapNote( t, to, tn );
|
||||
}
|
||||
int iTo = iToIndexBegin + iFrom - iFromIndexBegin;
|
||||
TapNote tn = From.GetTapNote( t, iFrom );
|
||||
To.SetTapNote( t, iTo, tn );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +101,6 @@ void NoteData::CopyAll( const NoteData& from )
|
||||
for( int c=0; c<GetNumTracks(); c++ )
|
||||
m_TapNotes[c] = from.m_TapNotes[c];
|
||||
m_HoldNotes = from.m_HoldNotes;
|
||||
m_AttackMap = from.m_AttackMap;
|
||||
}
|
||||
|
||||
bool NoteData::IsRowEmpty( int index ) const
|
||||
@@ -264,72 +254,18 @@ int NoteData::GetMatchingHoldNote( const HoldNote &hn ) const
|
||||
}
|
||||
|
||||
|
||||
void NoteData::SetTapAttackNote( int track, int row, Attack attack )
|
||||
void NoteData::SetTapAttackNote( int track, int row, CString sModifiers, float fDurationSeconds )
|
||||
{
|
||||
PruneUnusedAttacksFromMap();
|
||||
|
||||
// find first unused attack index
|
||||
for( unsigned i=0; i<MAX_NUM_ATTACKS; i++ )
|
||||
{
|
||||
if( m_AttackMap.find(i) == m_AttackMap.end() ) // this index is free to use
|
||||
{
|
||||
m_AttackMap[i] = attack;
|
||||
TapNote tn;
|
||||
tn.Set(
|
||||
TapNote::attack,
|
||||
TapNote::original,
|
||||
true,
|
||||
(uint8_t)i,
|
||||
false,
|
||||
0 );
|
||||
SetTapNote( track, row, tn );
|
||||
return;
|
||||
}
|
||||
}
|
||||
// TODO: need to increase MAX_NUM_ATTACKS or handle "no more room" case
|
||||
ASSERT(0);
|
||||
TapNote tn(
|
||||
TapNote::attack,
|
||||
TapNote::original,
|
||||
sModifiers,
|
||||
fDurationSeconds,
|
||||
false,
|
||||
0 );
|
||||
SetTapNote( track, row, tn );
|
||||
}
|
||||
|
||||
void NoteData::PruneUnusedAttacksFromMap()
|
||||
{
|
||||
// Add all used AttackNote index values to a map.
|
||||
set<unsigned> setUsedIndices;
|
||||
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK( *this, t, r )
|
||||
{
|
||||
TapNote tn = GetTapNote(t, r);
|
||||
if( tn.type == TapNote::attack )
|
||||
setUsedIndices.insert( tn.attackIndex );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all items from m_AttackMap that don't have corresponding
|
||||
// TapNotes in use.
|
||||
for( unsigned i=0; i<MAX_NUM_ATTACKS; i++ )
|
||||
{
|
||||
bool bInAttackMap = m_AttackMap.find(i) != m_AttackMap.end();
|
||||
bool bActuallyUsed = setUsedIndices.find(i) != setUsedIndices.end();
|
||||
|
||||
if( bActuallyUsed && !bInAttackMap )
|
||||
ASSERT(0); // something earlier than us didn't enforce consistency
|
||||
|
||||
if( bInAttackMap && !bActuallyUsed )
|
||||
m_AttackMap.erase( i );
|
||||
}
|
||||
}
|
||||
|
||||
const Attack& NoteData::GetAttackAt( int track, int row )
|
||||
{
|
||||
TapNote tn = GetTapNote(track, row);
|
||||
ASSERT( tn.type == TapNote::attack ); // don't call this if the TapNote here isn't an attack
|
||||
map<unsigned,Attack>::iterator iter = m_AttackMap.find( tn.attackIndex );
|
||||
ASSERT( iter != m_AttackMap.end() );
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
|
||||
int NoteData::GetFirstRow() const
|
||||
{
|
||||
int iEarliestRowFoundSoFar = -1;
|
||||
@@ -718,8 +654,6 @@ void NoteData::LoadTransformed( const NoteData& original, int iNewNumTracks, con
|
||||
}
|
||||
|
||||
Convert4sToHoldNotes();
|
||||
|
||||
m_AttackMap = Original.GetAttackMap();
|
||||
}
|
||||
|
||||
void NoteData::PadTapNotes(int rows)
|
||||
|
||||
@@ -26,8 +26,6 @@ class NoteData
|
||||
|
||||
vector<HoldNote> m_HoldNotes;
|
||||
|
||||
map<unsigned,Attack> m_AttackMap;
|
||||
|
||||
/* Pad m_TapNotes so it includes the row "rows". */
|
||||
void PadTapNotes(int rows);
|
||||
|
||||
@@ -44,10 +42,6 @@ public:
|
||||
int GetNumTracks() const;
|
||||
void SetNumTracks( int iNewNumTracks );
|
||||
|
||||
// TODO: Think of better accessors
|
||||
const map<unsigned,Attack>& GetAttackMap() const { return m_AttackMap; }
|
||||
map<unsigned,Attack>& GetAttackMap() { return m_AttackMap; }
|
||||
|
||||
/* Return the note at the given track and row. Row may be out of
|
||||
* range; pretend the song goes on with TAP_EMPTYs indefinitely. */
|
||||
inline TapNote GetTapNote(unsigned track, int row) const
|
||||
@@ -74,8 +68,8 @@ public:
|
||||
bool GetNextTapNoteRowForTrack( int track, int &rowInOut ) const;
|
||||
bool GetNextTapNoteRowForAllTracks( int &rowInOut ) const;
|
||||
|
||||
void MoveTapNoteTrack(int dest, int src);
|
||||
void SetTapNote(int track, int row, TapNote t);
|
||||
void MoveTapNoteTrack( int dest, int src );
|
||||
void SetTapNote( int track, int row, TapNote tn );
|
||||
|
||||
void ClearRange( int iNoteIndexBegin, int iNoteIndexEnd );
|
||||
void ClearAll();
|
||||
@@ -112,10 +106,8 @@ public:
|
||||
const HoldNote &GetHoldNote( int index ) const { ASSERT( index < (int) m_HoldNotes.size() ); return m_HoldNotes[index]; }
|
||||
int GetMatchingHoldNote( const HoldNote &hn ) const;
|
||||
|
||||
void SetTapAttackNote( int track, int row, Attack attack );
|
||||
void PruneUnusedAttacksFromMap(); // slow
|
||||
const Attack& GetAttackAt( int track, int row );
|
||||
// remove Attacks with SetTapNote(TAP_EMPTY)
|
||||
// remove me
|
||||
void SetTapAttackNote( int track, int row, CString sModifiers, float fDurationSeconds );
|
||||
|
||||
//
|
||||
// statistics
|
||||
|
||||
+177
-227
@@ -45,248 +45,205 @@ NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMe
|
||||
return nt;
|
||||
}
|
||||
|
||||
void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData, CString sSMAttackData )
|
||||
void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData )
|
||||
{
|
||||
//
|
||||
// Load note data
|
||||
//
|
||||
|
||||
/* Clear notes, but keep the same number of tracks. */
|
||||
int iNumTracks = out.GetNumTracks();
|
||||
out.Init();
|
||||
out.SetNumTracks( iNumTracks );
|
||||
|
||||
// strip comments out of sSMNoteData
|
||||
while( sSMNoteData.Find("//") != -1 )
|
||||
{
|
||||
//
|
||||
// Load note data
|
||||
//
|
||||
|
||||
/* Clear notes, but keep the same number of tracks. */
|
||||
int iNumTracks = out.GetNumTracks();
|
||||
out.Init();
|
||||
out.SetNumTracks( iNumTracks );
|
||||
|
||||
// strip comments out of sSMNoteData
|
||||
while( sSMNoteData.Find("//") != -1 )
|
||||
{
|
||||
int iIndexCommentStart = sSMNoteData.Find("//");
|
||||
int iIndexCommentEnd = sSMNoteData.Find("\n", iIndexCommentStart);
|
||||
if( iIndexCommentEnd == -1 ) // comment doesn't have an end?
|
||||
sSMNoteData.erase( iIndexCommentStart, 2 );
|
||||
else
|
||||
sSMNoteData.erase( iIndexCommentStart, iIndexCommentEnd-iIndexCommentStart );
|
||||
}
|
||||
|
||||
CStringArray asMeasures;
|
||||
split( sSMNoteData, ",", asMeasures, true ); // ignore empty is important
|
||||
for( unsigned m=0; m<asMeasures.size(); m++ ) // foreach measure
|
||||
{
|
||||
CString &sMeasureString = asMeasures[m];
|
||||
TrimLeft(sMeasureString);
|
||||
TrimRight(sMeasureString);
|
||||
|
||||
CStringArray asMeasureLines;
|
||||
split( sMeasureString, "\n", asMeasureLines, true ); // ignore empty is important
|
||||
|
||||
for( unsigned l=0; l<asMeasureLines.size(); l++ )
|
||||
{
|
||||
CString &sMeasureLine = asMeasureLines[l];
|
||||
TrimLeft(sMeasureLine);
|
||||
TrimRight(sMeasureLine);
|
||||
|
||||
const float fPercentIntoMeasure = l/(float)asMeasureLines.size();
|
||||
const float fBeat = (m + fPercentIntoMeasure) * BEATS_PER_MEASURE;
|
||||
const int iIndex = BeatToNoteRow( fBeat );
|
||||
|
||||
// if( m_iNumTracks != sMeasureLine.GetLength() )
|
||||
// RageException::Throw( "Actual number of note columns (%d) is different from the StepsType (%d).", m_iNumTracks, sMeasureLine.GetLength() );
|
||||
|
||||
const char *p = sMeasureLine;
|
||||
int iTrack = 0;
|
||||
while( *p )
|
||||
{
|
||||
TapNote tn;
|
||||
char ch = *p;
|
||||
switch( ch )
|
||||
{
|
||||
case '0': tn = TAP_EMPTY; break;
|
||||
case '1': tn = TAP_ORIGINAL_TAP; break;
|
||||
case '2': tn = TAP_ORIGINAL_HOLD_HEAD; break;
|
||||
case '3': tn = TAP_ORIGINAL_HOLD_TAIL; break;
|
||||
// case 'm':
|
||||
// Don't be loose with the definition. Use only 'M' since
|
||||
// that's what we've been writing to disk. -Chris
|
||||
case 'M': tn = TAP_ORIGINAL_MINE; break;
|
||||
default:
|
||||
if( ch >= 'a' && ch <= 'z' )
|
||||
{
|
||||
tn.Set(
|
||||
TapNote::attack,
|
||||
TapNote::original,
|
||||
true,
|
||||
ch - 'a',
|
||||
false,
|
||||
0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Invalid data. We don't want to assert, since there might
|
||||
* simply be invalid data in an .SM, and we don't want to die
|
||||
* due to invalid data. We should probably check for this when
|
||||
* we load SM data for the first time ... */
|
||||
// ASSERT(0);
|
||||
tn = TAP_EMPTY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
// look for optional keysound index (e.g. "[123]")
|
||||
if( *p == '[' )
|
||||
{
|
||||
p++;
|
||||
unsigned uKeysoundIndex = 0;
|
||||
if( 1 == sscanf( p, "%u]", &uKeysoundIndex ) ) // not fatal if this fails due to malformed data
|
||||
{
|
||||
tn.bKeysound = true;
|
||||
tn.keysoundIndex = (uint16_t)uKeysoundIndex;
|
||||
}
|
||||
|
||||
// skip past the ']'
|
||||
while( *p )
|
||||
{
|
||||
if( *p == ']' )
|
||||
{
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
out.SetTapNote( iTrack, iIndex, tn );
|
||||
|
||||
iTrack++;
|
||||
}
|
||||
}
|
||||
}
|
||||
out.Convert2sAnd3sToHoldNotes();
|
||||
int iIndexCommentStart = sSMNoteData.Find("//");
|
||||
int iIndexCommentEnd = sSMNoteData.Find("\n", iIndexCommentStart);
|
||||
if( iIndexCommentEnd == -1 ) // comment doesn't have an end?
|
||||
sSMNoteData.erase( iIndexCommentStart, 2 );
|
||||
else
|
||||
sSMNoteData.erase( iIndexCommentStart, iIndexCommentEnd-iIndexCommentStart );
|
||||
}
|
||||
|
||||
CStringArray asMeasures;
|
||||
split( sSMNoteData, ",", asMeasures, true ); // ignore empty is important
|
||||
for( unsigned m=0; m<asMeasures.size(); m++ ) // foreach measure
|
||||
{
|
||||
//
|
||||
// Load attack data
|
||||
//
|
||||
CStringArray asLines;
|
||||
split( sSMAttackData, ",", asLines, true );
|
||||
CString &sMeasureString = asMeasures[m];
|
||||
TrimLeft(sMeasureString);
|
||||
TrimRight(sMeasureString);
|
||||
|
||||
for( unsigned i=0; i<asLines.size(); i++ )
|
||||
CStringArray asMeasureLines;
|
||||
split( sMeasureString, "\n", asMeasureLines, true ); // ignore empty is important
|
||||
|
||||
for( unsigned l=0; l<asMeasureLines.size(); l++ )
|
||||
{
|
||||
CString& sLine = asLines[i];
|
||||
TrimLeft( sLine );
|
||||
TrimRight( sLine );
|
||||
CString &sMeasureLine = asMeasureLines[l];
|
||||
TrimLeft(sMeasureLine);
|
||||
TrimRight(sMeasureLine);
|
||||
|
||||
if( sLine.empty() )
|
||||
continue; // skip
|
||||
const float fPercentIntoMeasure = l/(float)asMeasureLines.size();
|
||||
const float fBeat = (m + fPercentIntoMeasure) * BEATS_PER_MEASURE;
|
||||
const int iIndex = BeatToNoteRow( fBeat );
|
||||
|
||||
CStringArray asBits;
|
||||
split( sLine, "=", asBits, true );
|
||||
// if( m_iNumTracks != sMeasureLine.GetLength() )
|
||||
// RageException::Throw( "Actual number of note columns (%d) is different from the StepsType (%d).", m_iNumTracks, sMeasureLine.GetLength() );
|
||||
|
||||
if( asBits.size() < 3 )
|
||||
continue;
|
||||
|
||||
if( asBits[0].empty() )
|
||||
continue;
|
||||
|
||||
int attack_index = asBits[0][0] - 'a';
|
||||
|
||||
Attack attack;
|
||||
attack.level = ATTACK_LEVEL_1;
|
||||
attack.sModifier = asBits[1];
|
||||
attack.sModifier.Replace( '.', ',' ); // we couldn't use comma here because the map item separator is a comma
|
||||
attack.fSecsRemaining = strtof( asBits[2], NULL );
|
||||
|
||||
out.GetAttackMap()[attack_index] = attack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString ¬es_out, CString &attacks_out )
|
||||
{
|
||||
{
|
||||
//
|
||||
// Get note data
|
||||
//
|
||||
NoteData in;
|
||||
in.To2sAnd3s( in_ );
|
||||
|
||||
float fLastBeat = in.GetLastBeat();
|
||||
int iLastMeasure = int( fLastBeat/BEATS_PER_MEASURE );
|
||||
|
||||
CString &sRet = notes_out;
|
||||
|
||||
sRet = "";
|
||||
|
||||
for( int m=0; m<=iLastMeasure; m++ ) // foreach measure
|
||||
{
|
||||
if( m )
|
||||
sRet.append( 1, ',' );
|
||||
|
||||
NoteType nt = GetSmallestNoteTypeForMeasure( in, m );
|
||||
int iRowSpacing;
|
||||
if( nt == NOTE_TYPE_INVALID )
|
||||
iRowSpacing = 1;
|
||||
else
|
||||
iRowSpacing = int(roundf( NoteTypeToBeat(nt) * ROWS_PER_BEAT ));
|
||||
|
||||
sRet += ssprintf(" // measure %d\n", m+1);
|
||||
|
||||
const int iMeasureStartRow = m * ROWS_PER_MEASURE;
|
||||
const int iMeasureLastRow = (m+1) * ROWS_PER_MEASURE - 1;
|
||||
|
||||
for( int r=iMeasureStartRow; r<=iMeasureLastRow; r+=iRowSpacing )
|
||||
const char *p = sMeasureLine;
|
||||
int iTrack = 0;
|
||||
while( *p )
|
||||
{
|
||||
for( int t=0; t<in.GetNumTracks(); t++ )
|
||||
TapNote tn;
|
||||
char ch = *p;
|
||||
switch( ch )
|
||||
{
|
||||
TapNote tn = in.GetTapNote(t, r);
|
||||
char c;
|
||||
switch( tn.type )
|
||||
{
|
||||
case TapNote::empty: c = '0'; break;
|
||||
case TapNote::tap: c = '1'; break;
|
||||
case TapNote::hold_head: c = '2'; break;
|
||||
case TapNote::hold_tail: c = '3'; break;
|
||||
case TapNote::mine: c = 'M'; break;
|
||||
case TapNote::attack: c = 'a' + (char)tn.attackIndex ; break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
c = '0';
|
||||
break;
|
||||
}
|
||||
sRet.append(1, c);
|
||||
|
||||
if( tn.bKeysound )
|
||||
{
|
||||
sRet.append( ssprintf("[%u]",tn.keysoundIndex) );
|
||||
}
|
||||
case '0': tn = TAP_EMPTY; break;
|
||||
case '1': tn = TAP_ORIGINAL_TAP; break;
|
||||
case '2': tn = TAP_ORIGINAL_HOLD_HEAD; break;
|
||||
case '3': tn = TAP_ORIGINAL_HOLD_TAIL; break;
|
||||
// case 'm':
|
||||
// Don't be loose with the definition. Use only 'M' since
|
||||
// that's what we've been writing to disk. -Chris
|
||||
case 'M': tn = TAP_ORIGINAL_MINE; break;
|
||||
case 'A': tn = TAP_ORIGINAL_ATTACK; break;
|
||||
default:
|
||||
/* Invalid data. We don't want to assert, since there might
|
||||
* simply be invalid data in an .SM, and we don't want to die
|
||||
* due to invalid data. We should probably check for this when
|
||||
* we load SM data for the first time ... */
|
||||
// ASSERT(0);
|
||||
tn = TAP_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
sRet.append(1, '\n');
|
||||
p++;
|
||||
|
||||
// look for optional attack info (e.g. "{tipsy,50% drunk:15.2}")
|
||||
if( *p == '{' )
|
||||
{
|
||||
p++;
|
||||
|
||||
// TODO: this buffer could overflow
|
||||
char szModifiers[256] = "";
|
||||
float fDurationSeconds = 0;
|
||||
if( 2 == sscanf( p, "%[^:]:%f}", szModifiers, &fDurationSeconds ) ) // not fatal if this fails due to malformed data
|
||||
{
|
||||
tn.type = TapNote::attack;
|
||||
tn.sAttackModifiers = szModifiers;
|
||||
tn.fAttackDurationSeconds = fDurationSeconds;
|
||||
}
|
||||
|
||||
// skip past the '}'
|
||||
while( *p )
|
||||
{
|
||||
if( *p == '}' )
|
||||
{
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
// look for optional keysound index (e.g. "[123]")
|
||||
if( *p == '[' )
|
||||
{
|
||||
p++;
|
||||
int iKeysoundIndex = 0;
|
||||
if( 1 == sscanf( p, "%d]", &iKeysoundIndex ) ) // not fatal if this fails due to malformed data
|
||||
{
|
||||
tn.bKeysound = true;
|
||||
tn.iKeysoundIndex = iKeysoundIndex;
|
||||
}
|
||||
|
||||
// skip past the ']'
|
||||
while( *p )
|
||||
{
|
||||
if( *p == ']' )
|
||||
{
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
out.SetTapNote( iTrack, iIndex, tn );
|
||||
|
||||
iTrack++;
|
||||
}
|
||||
}
|
||||
}
|
||||
out.Convert2sAnd3sToHoldNotes();
|
||||
}
|
||||
|
||||
void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString ¬es_out )
|
||||
{
|
||||
//
|
||||
// Get note data
|
||||
//
|
||||
NoteData in;
|
||||
in.To2sAnd3s( in_ );
|
||||
|
||||
float fLastBeat = in.GetLastBeat();
|
||||
int iLastMeasure = int( fLastBeat/BEATS_PER_MEASURE );
|
||||
|
||||
CString &sRet = notes_out;
|
||||
|
||||
sRet = "";
|
||||
|
||||
for( int m=0; m<=iLastMeasure; m++ ) // foreach measure
|
||||
{
|
||||
//
|
||||
// Get attack data
|
||||
//
|
||||
CStringArray asLines;
|
||||
if( m )
|
||||
sRet.append( 1, ',' );
|
||||
|
||||
for( map<unsigned,Attack>::const_iterator iter = in_.GetAttackMap().begin();
|
||||
iter != in_.GetAttackMap().end();
|
||||
iter++ )
|
||||
NoteType nt = GetSmallestNoteTypeForMeasure( in, m );
|
||||
int iRowSpacing;
|
||||
if( nt == NOTE_TYPE_INVALID )
|
||||
iRowSpacing = 1;
|
||||
else
|
||||
iRowSpacing = int(roundf( NoteTypeToBeat(nt) * ROWS_PER_BEAT ));
|
||||
|
||||
sRet += ssprintf(" // measure %d\n", m+1);
|
||||
|
||||
const int iMeasureStartRow = m * ROWS_PER_MEASURE;
|
||||
const int iMeasureLastRow = (m+1) * ROWS_PER_MEASURE - 1;
|
||||
|
||||
for( int r=iMeasureStartRow; r<=iMeasureLastRow; r+=iRowSpacing )
|
||||
{
|
||||
int attackIndex = iter->first;
|
||||
char ch = 'a' + (char)attackIndex;
|
||||
Attack attack = iter->second;
|
||||
attack.sModifier.Replace( ',', '.' ); // comma is the map item separator
|
||||
for( int t=0; t<in.GetNumTracks(); t++ )
|
||||
{
|
||||
TapNote tn = in.GetTapNote(t, r);
|
||||
char c;
|
||||
switch( tn.type )
|
||||
{
|
||||
case TapNote::empty: c = '0'; break;
|
||||
case TapNote::tap: c = '1'; break;
|
||||
case TapNote::hold_head: c = '2'; break;
|
||||
case TapNote::hold_tail: c = '3'; break;
|
||||
case TapNote::mine: c = 'M'; break;
|
||||
case TapNote::attack: c = 'A'; break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
c = '0';
|
||||
break;
|
||||
}
|
||||
sRet.append(1, c);
|
||||
|
||||
asLines.push_back( ssprintf("%c=%s=%f,\n", ch, attack.sModifier.c_str(), attack.fSecsRemaining) );
|
||||
if( tn.type == TapNote::attack )
|
||||
{
|
||||
sRet.append( ssprintf("{%s:%.2f}",tn.sAttackModifiers.c_str(), tn.fAttackDurationSeconds) );
|
||||
}
|
||||
if( tn.bKeysound )
|
||||
{
|
||||
sRet.append( ssprintf("[%d]",tn.iKeysoundIndex) );
|
||||
}
|
||||
}
|
||||
|
||||
sRet.append(1, '\n');
|
||||
}
|
||||
|
||||
attacks_out = join( ",", asLines );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,8 +311,6 @@ void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &o
|
||||
}
|
||||
|
||||
out.Convert4sToHoldNotes();
|
||||
|
||||
out.GetAttackMap() = Original.GetAttackMap();
|
||||
}
|
||||
|
||||
void NoteDataUtil::LoadOverlapped( const NoteData &input, NoteData &out, int iNewNumTracks )
|
||||
@@ -1672,12 +1627,7 @@ void NoteDataUtil::AddTapAttacks( NoteData &nd, Song* pSong )
|
||||
float fBeat = pSong->GetBeatFromElapsedTime( sec );
|
||||
int iBeat = (int)fBeat;
|
||||
int iTrack = iBeat % nd.GetNumTracks(); // deterministically calculates track
|
||||
Attack attack;
|
||||
attack.fStartSecond = -1;
|
||||
attack.fSecsRemaining = 15;
|
||||
attack.sModifier = szAttacks[rand()%ARRAYSIZE(szAttacks)];
|
||||
attack.level = ATTACK_LEVEL_1;
|
||||
nd.SetTapAttackNote( iTrack, BeatToNoteRow(fBeat), attack );
|
||||
nd.SetTapAttackNote( iTrack, BeatToNoteRow(fBeat), szAttacks[rand()%ARRAYSIZE(szAttacks)], 15 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ struct RadarValues;
|
||||
namespace NoteDataUtil
|
||||
{
|
||||
NoteType GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex );
|
||||
void LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData, CString sSMAttackData );
|
||||
void GetSMNoteDataString( const NoteData &in, CString ¬es_out, CString &attacks_out );
|
||||
void LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData );
|
||||
void GetSMNoteDataString( const NoteData &in, CString ¬es_out );
|
||||
void LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks );
|
||||
void LoadOverlapped( const NoteData &in, NoteData &out, int iNewNumTracks );
|
||||
void LoadTransformedLights( const NoteData &in, NoteData &out, int iNewNumTracks );
|
||||
|
||||
@@ -621,9 +621,8 @@ void NoteField::DrawPrimitives()
|
||||
NoteDisplayCols *nd = CurDisplay->second;
|
||||
if( bIsAttack )
|
||||
{
|
||||
const Attack& attack = GetAttackAt( c, i );
|
||||
Sprite sprite;
|
||||
sprite.Load( THEME->GetPathToG("NoteField attack "+attack.sModifier) );
|
||||
sprite.Load( THEME->GetPathToG("NoteField attack "+tn.sAttackModifiers) );
|
||||
float fBeat = NoteRowToBeat(i);
|
||||
SearchForBeat( CurDisplay, NextDisplay, fBeat );
|
||||
NoteDisplayCols *nd = CurDisplay->second;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#include "global.h"
|
||||
#include "NoteTypes.h"
|
||||
|
||||
TapNote TAP_EMPTY = { TapNote::empty, TapNote::original, 0 };
|
||||
TapNote TAP_ORIGINAL_TAP = { TapNote::tap, TapNote::original, 0 };
|
||||
TapNote TAP_ORIGINAL_HOLD_HEAD = { TapNote::hold_head, TapNote::original, 0 }; // '2'
|
||||
TapNote TAP_ORIGINAL_HOLD_TAIL = { TapNote::hold_tail, TapNote::original, 0 }; // '3'
|
||||
TapNote TAP_ORIGINAL_HOLD = { TapNote::hold, TapNote::original, 0 }; // '4'
|
||||
TapNote TAP_ORIGINAL_MINE = { TapNote::mine, TapNote::original, 0 };
|
||||
TapNote TAP_ADDITION_TAP = { TapNote::tap, TapNote::addition, 0 };
|
||||
TapNote TAP_ADDITION_MINE = { TapNote::mine, TapNote::addition, 0 };
|
||||
TapNote TAP_EMPTY ( TapNote::empty, TapNote::original, "", 0, false, 0 );
|
||||
TapNote TAP_ORIGINAL_TAP ( TapNote::tap, TapNote::original, "", 0, false, 0 );
|
||||
TapNote TAP_ORIGINAL_HOLD_HEAD ( TapNote::hold_head, TapNote::original, "", 0, false, 0 );
|
||||
TapNote TAP_ORIGINAL_HOLD_TAIL ( TapNote::hold_tail, TapNote::original, "", 0, false, 0 );
|
||||
TapNote TAP_ORIGINAL_HOLD ( TapNote::hold, TapNote::original, "", 0, false, 0 );
|
||||
TapNote TAP_ORIGINAL_MINE ( TapNote::mine, TapNote::original, "", 0, false, 0 );
|
||||
TapNote TAP_ORIGINAL_ATTACK ( TapNote::attack, TapNote::original, "", 0, false, 0 );
|
||||
TapNote TAP_ADDITION_TAP ( TapNote::tap, TapNote::addition, "", 0, false, 0 );
|
||||
TapNote TAP_ADDITION_MINE ( TapNote::mine, TapNote::addition, "", 0, false, 0 );
|
||||
|
||||
float NoteTypeToBeat( NoteType nt )
|
||||
{
|
||||
|
||||
+21
-23
@@ -4,15 +4,14 @@
|
||||
struct TapNote
|
||||
{
|
||||
enum Type {
|
||||
empty,
|
||||
empty, // no note here
|
||||
tap,
|
||||
hold_head, // graded like a TAP_TAP
|
||||
hold_tail, /* In 2sand3s mode, holds are deleted and TAP_HOLD_END is added: */
|
||||
hold, /* In 4s mode, holds and TAP_HOLD_HEAD are deleted and TAP_HOLD is added: */
|
||||
mine, // don't step!
|
||||
attack,
|
||||
};
|
||||
unsigned type : 3; // no unsigned enum support in VC++
|
||||
} type;
|
||||
enum Source {
|
||||
original, // part of the original NoteData
|
||||
addition, // additional note added by a transform
|
||||
@@ -27,44 +26,42 @@ struct TapNote
|
||||
// then this is triggered automatically to keep the sound going
|
||||
// 2 - if we're NOT [anything else], we ignore this.
|
||||
// Equivalent to all 4s aside from the first one.
|
||||
};
|
||||
unsigned source : 2; // only valid if type!=empty
|
||||
} source;
|
||||
|
||||
bool bAttack : 1; // true if this note causes an attack when hit
|
||||
bool bKeysound : 1; // true if this note plays a keysound when hit
|
||||
|
||||
// CAREFUL: small fields grouped together for alignment.
|
||||
// Only valid if type == attack.
|
||||
CString sAttackModifiers;
|
||||
float fAttackDurationSeconds;
|
||||
|
||||
uint8_t attackIndex; // index into NoteData's vector of attacks
|
||||
// Only valid if bAttack.
|
||||
uint16_t keysoundIndex; // index into Song's vector of keysound files.
|
||||
bool bKeysound; // true if this note plays a keysound when hit
|
||||
|
||||
int iKeysoundIndex; // index into Song's vector of keysound files.
|
||||
// Only valid if bKeysound.
|
||||
// Some songs have > 256 keysounds.
|
||||
|
||||
void Set(
|
||||
TapNote() {}
|
||||
TapNote(
|
||||
Type type_,
|
||||
Source source_,
|
||||
bool bAttack_,
|
||||
uint8_t attackIndex_,
|
||||
CString sAttackModifiers_,
|
||||
float fAttackDurationSeconds_,
|
||||
bool bKeysound_,
|
||||
uint16_t keysoundIndex_ )
|
||||
int iKeysoundIndex_ )
|
||||
{
|
||||
type = type_;
|
||||
source = source_;
|
||||
bAttack = bAttack_;
|
||||
attackIndex = attackIndex_;
|
||||
sAttackModifiers = sAttackModifiers_;
|
||||
fAttackDurationSeconds = fAttackDurationSeconds_;
|
||||
bKeysound = bKeysound_;
|
||||
keysoundIndex = keysoundIndex_;
|
||||
iKeysoundIndex = iKeysoundIndex_;
|
||||
}
|
||||
bool operator==( const TapNote &other )
|
||||
{
|
||||
#define COMPARE(x) if(x!=other.x) return false;
|
||||
COMPARE(type);
|
||||
COMPARE(source);
|
||||
COMPARE(bAttack);
|
||||
COMPARE(sAttackModifiers);
|
||||
COMPARE(fAttackDurationSeconds);
|
||||
COMPARE(bKeysound);
|
||||
COMPARE(attackIndex);
|
||||
COMPARE(keysoundIndex);
|
||||
COMPARE(iKeysoundIndex);
|
||||
#undef COMPARE
|
||||
return true;
|
||||
}
|
||||
@@ -78,6 +75,7 @@ extern TapNote TAP_ORIGINAL_HOLD_HEAD; // '2'
|
||||
extern TapNote TAP_ORIGINAL_HOLD_TAIL; // '3'
|
||||
extern TapNote TAP_ORIGINAL_HOLD; // '4'
|
||||
extern TapNote TAP_ORIGINAL_MINE; // 'M'
|
||||
extern TapNote TAP_ORIGINAL_ATTACK; // 'A'
|
||||
extern TapNote TAP_ADDITION_TAP;
|
||||
extern TapNote TAP_ADDITION_MINE;
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ enum BmsTrack
|
||||
BMS_P2_TURN,
|
||||
BMS_P2_KEY6,
|
||||
BMS_P2_KEY7,
|
||||
BMS_AUTO_KEYSOUND,
|
||||
NUM_BMS_TRACKS,
|
||||
BMS_TRACK_INVALID,
|
||||
};
|
||||
@@ -83,22 +84,23 @@ static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, bool
|
||||
|
||||
switch( iRawTrack )
|
||||
{
|
||||
case 11: bmsTrackOut = BMS_P1_KEY1; break;
|
||||
case 12: bmsTrackOut = BMS_P1_KEY2; break;
|
||||
case 13: bmsTrackOut = BMS_P1_KEY3; break;
|
||||
case 14: bmsTrackOut = BMS_P1_KEY4; break;
|
||||
case 15: bmsTrackOut = BMS_P1_KEY5; break;
|
||||
case 16: bmsTrackOut = BMS_P1_TURN; break;
|
||||
case 18: bmsTrackOut = BMS_P1_KEY6; break;
|
||||
case 19: bmsTrackOut = BMS_P1_KEY7; break;
|
||||
case 21: bmsTrackOut = BMS_P2_KEY1; break;
|
||||
case 22: bmsTrackOut = BMS_P2_KEY2; break;
|
||||
case 23: bmsTrackOut = BMS_P2_KEY3; break;
|
||||
case 24: bmsTrackOut = BMS_P2_KEY4; break;
|
||||
case 25: bmsTrackOut = BMS_P2_KEY5; break;
|
||||
case 26: bmsTrackOut = BMS_P2_TURN; break;
|
||||
case 28: bmsTrackOut = BMS_P2_KEY6; break;
|
||||
case 29: bmsTrackOut = BMS_P2_KEY7; break;
|
||||
case 1: bmsTrackOut = BMS_AUTO_KEYSOUND; break;
|
||||
case 11: bmsTrackOut = BMS_P1_KEY1; break;
|
||||
case 12: bmsTrackOut = BMS_P1_KEY2; break;
|
||||
case 13: bmsTrackOut = BMS_P1_KEY3; break;
|
||||
case 14: bmsTrackOut = BMS_P1_KEY4; break;
|
||||
case 15: bmsTrackOut = BMS_P1_KEY5; break;
|
||||
case 16: bmsTrackOut = BMS_P1_TURN; break;
|
||||
case 18: bmsTrackOut = BMS_P1_KEY6; break;
|
||||
case 19: bmsTrackOut = BMS_P1_KEY7; break;
|
||||
case 21: bmsTrackOut = BMS_P2_KEY1; break;
|
||||
case 22: bmsTrackOut = BMS_P2_KEY2; break;
|
||||
case 23: bmsTrackOut = BMS_P2_KEY3; break;
|
||||
case 24: bmsTrackOut = BMS_P2_KEY4; break;
|
||||
case 25: bmsTrackOut = BMS_P2_KEY5; break;
|
||||
case 26: bmsTrackOut = BMS_P2_TURN; break;
|
||||
case 28: bmsTrackOut = BMS_P2_KEY6; break;
|
||||
case 29: bmsTrackOut = BMS_P2_KEY7; break;
|
||||
default: // unknown track
|
||||
return false;
|
||||
}
|
||||
@@ -176,7 +178,7 @@ static StepsType DetermineStepsType( int iPlayer, const NoteData &nd )
|
||||
}
|
||||
}
|
||||
|
||||
bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CString,unsigned> &mapWavIdToKeysoundIndex )
|
||||
bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CString,int> &mapWavIdToKeysoundIndex )
|
||||
{
|
||||
LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() );
|
||||
|
||||
@@ -185,19 +187,19 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CSt
|
||||
// BMS player code. Fill in below and use to determine StepsType.
|
||||
int iPlayer = -1;
|
||||
|
||||
NoteData* pNoteData = new NoteData;
|
||||
pNoteData->SetNumTracks( NUM_BMS_TRACKS );
|
||||
NoteData ndNotes;
|
||||
ndNotes.SetNumTracks( NUM_BMS_TRACKS );
|
||||
|
||||
RageFile file;
|
||||
if( !file.Open(sPath) )
|
||||
RageException::Throw( "Failed to open \"%s\" for reading: %s", sPath.c_str(), file.GetError().c_str() );
|
||||
|
||||
while( !file.AtEOF() )
|
||||
{
|
||||
CString line;
|
||||
if( file.GetLine( line ) == -1 )
|
||||
{
|
||||
LOG->Warn( "Error reading \"%s\": %s", sPath.c_str(), file.GetError().c_str() );
|
||||
delete pNoteData;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -276,11 +278,11 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CSt
|
||||
if( sNoteId != "00" )
|
||||
{
|
||||
TapNote tn = TAP_ORIGINAL_TAP;
|
||||
map<CString,unsigned>::const_iterator it = mapWavIdToKeysoundIndex.find(sNoteId);
|
||||
map<CString,int>::const_iterator it = mapWavIdToKeysoundIndex.find(sNoteId);
|
||||
if( it != mapWavIdToKeysoundIndex.end() )
|
||||
{
|
||||
tn.bKeysound = true;
|
||||
tn.keysoundIndex = (uint16_t)it->second;
|
||||
tn.iKeysoundIndex = it->second;
|
||||
}
|
||||
vTapNotes.push_back( tn );
|
||||
}
|
||||
@@ -304,22 +306,27 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CSt
|
||||
bool bIsHold;
|
||||
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, bIsHold) )
|
||||
{
|
||||
TapNote tn = vTapNotes[j];
|
||||
tn.type = bIsHold ? TapNote::hold_head : TapNote::tap;
|
||||
pNoteData->SetTapNote(bmsTrack, iNoteIndex, tn);
|
||||
if( bmsTrack == BMS_AUTO_KEYSOUND )
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
TapNote tn = vTapNotes[j];
|
||||
tn.type = bIsHold ? TapNote::hold_head : TapNote::tap;
|
||||
ndNotes.SetTapNote( bmsTrack, iNoteIndex, tn );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.m_StepsType = DetermineStepsType( iPlayer, *pNoteData );
|
||||
out.m_StepsType = DetermineStepsType( iPlayer, ndNotes );
|
||||
|
||||
// we're done reading in all of the BMS values
|
||||
if( out.m_StepsType == STEPS_TYPE_INVALID )
|
||||
{
|
||||
LOG->Warn( "Couldn't determine note type of file '%s'", sPath.c_str() );
|
||||
delete pNoteData;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -425,14 +432,11 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CSt
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
NoteData* pNoteData2 = new NoteData;
|
||||
pNoteData2->SetNumTracks( iNumNewTracks );
|
||||
pNoteData2->LoadTransformed( *pNoteData, iNumNewTracks, iTransformNewToOld );
|
||||
NoteData noteData2;
|
||||
noteData2.SetNumTracks( iNumNewTracks );
|
||||
noteData2.LoadTransformed( ndNotes, iNumNewTracks, iTransformNewToOld );
|
||||
|
||||
out.SetNoteData(*pNoteData2);
|
||||
|
||||
delete pNoteData;
|
||||
delete pNoteData2;
|
||||
out.SetNoteData( noteData2 );
|
||||
|
||||
out.TidyUpData();
|
||||
|
||||
@@ -460,7 +464,7 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out )
|
||||
|
||||
// This maps from a BMS wav ID (e.g. "1A") to an entry in the Song's
|
||||
// keysound vector. Fill this in below while parsing the song data.
|
||||
map<CString,unsigned> mapWavIdToKeysoundIndex;
|
||||
map<CString,int> mapWavIdToKeysoundIndex;
|
||||
|
||||
CString sPath = out.GetSongDir() + arrayBMSFileNames[0];
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class Steps;
|
||||
|
||||
class BMSLoader: public NotesLoader
|
||||
{
|
||||
bool LoadFromBMSFile( const CString &sPath, Steps &out1, const map<CString,unsigned> &mapWavIdToKeysoundIndex );
|
||||
bool LoadFromBMSFile( const CString &sPath, Steps &out1, const map<CString,int> &mapWavIdToKeysoundIndex );
|
||||
|
||||
void SlideDuplicateDifficulties( Song &p );
|
||||
|
||||
|
||||
@@ -17,16 +17,12 @@ void SMLoader::LoadFromSMTokens(
|
||||
CString sMeter,
|
||||
CString sRadarValues,
|
||||
CString sNoteData,
|
||||
CString sAttackData,
|
||||
Steps &out
|
||||
)
|
||||
{
|
||||
TrimLeft(sStepsType);
|
||||
TrimRight(sStepsType);
|
||||
TrimLeft(sDescription);
|
||||
TrimRight(sDescription);
|
||||
TrimLeft(sDifficulty);
|
||||
TrimRight(sDifficulty);
|
||||
TrimLeft(sStepsType); TrimRight(sStepsType);
|
||||
TrimLeft(sDescription); TrimRight(sDescription);
|
||||
TrimLeft(sDifficulty); TrimRight(sDifficulty);
|
||||
|
||||
|
||||
// LOG->Trace( "Steps::LoadFromSMTokens()" );
|
||||
@@ -55,7 +51,7 @@ void SMLoader::LoadFromSMTokens(
|
||||
out.SetRadarValues( v );
|
||||
}
|
||||
|
||||
out.SetSMNoteData(sNoteData, sAttackData);
|
||||
out.SetSMNoteData(sNoteData);
|
||||
|
||||
out.TidyUpData();
|
||||
}
|
||||
@@ -363,8 +359,7 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
|
||||
sParams[3],
|
||||
sParams[4],
|
||||
sParams[5],
|
||||
sParams[6],
|
||||
(iNumParams>=8)?sParams[7]:CString(""),
|
||||
sParams[6],
|
||||
*pNewNotes );
|
||||
|
||||
out.AddSteps( pNewNotes );
|
||||
@@ -462,7 +457,7 @@ bool SMLoader::LoadEdit( CString sEditFilePath, ProfileSlot slot )
|
||||
}
|
||||
|
||||
LoadFromSMTokens(
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], (iNumParams>=8)?sParams[7]:CString(""),
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6],
|
||||
*pNewNotes);
|
||||
|
||||
pNewNotes->SetLoadedFromProfile( slot );
|
||||
|
||||
@@ -18,7 +18,6 @@ class SMLoader: public NotesLoader
|
||||
CString sMeter,
|
||||
CString sRadarValues,
|
||||
CString sNoteData,
|
||||
CString sAttackData,
|
||||
Steps &out);
|
||||
|
||||
bool FromCache;
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
#include "RageFile.h"
|
||||
|
||||
/* Output is an angle bracket expression without angle brackets, eg. "468". */
|
||||
CString NotesWriterDWI::NotesToDWIString( const TapNote cNoteCols[6] )
|
||||
CString NotesWriterDWI::NotesToDWIString( const TapNote tnCols[6] )
|
||||
{
|
||||
const char dirs[] = { '4', 'C', '2', '8', 'D', '6' };
|
||||
CString taps, holds, ret;
|
||||
for( int col = 0; col < 6; ++col )
|
||||
{
|
||||
switch( cNoteCols[col].type )
|
||||
switch( tnCols[col].type )
|
||||
{
|
||||
case TapNote::empty:
|
||||
case TapNote::mine:
|
||||
continue;
|
||||
}
|
||||
|
||||
if( cNoteCols[col].type == TapNote::hold_head )
|
||||
if( tnCols[col].type == TapNote::hold_head )
|
||||
holds += dirs[col];
|
||||
else
|
||||
taps += dirs[col];
|
||||
@@ -81,21 +81,21 @@ CString NotesWriterDWI::NotesToDWIString( const TapNote cNoteCols[6] )
|
||||
return '0';*/
|
||||
}
|
||||
|
||||
CString NotesWriterDWI::NotesToDWIString( TapNote cNoteCol1, TapNote cNoteCol2, TapNote cNoteCol3, TapNote cNoteCol4, TapNote cNoteCol5, TapNote cNoteCol6 )
|
||||
CString NotesWriterDWI::NotesToDWIString( TapNote tnCol1, TapNote tnCol2, TapNote tnCol3, TapNote tnCol4, TapNote tnCol5, TapNote tnCol6 )
|
||||
{
|
||||
TapNote cNoteCols[6];
|
||||
cNoteCols[0] = cNoteCol1;
|
||||
cNoteCols[1] = cNoteCol2;
|
||||
cNoteCols[2] = cNoteCol3;
|
||||
cNoteCols[3] = cNoteCol4;
|
||||
cNoteCols[4] = cNoteCol5;
|
||||
cNoteCols[5] = cNoteCol6;
|
||||
return NotesToDWIString( cNoteCols );
|
||||
TapNote tnCols[6];
|
||||
tnCols[0] = tnCol1;
|
||||
tnCols[1] = tnCol2;
|
||||
tnCols[2] = tnCol3;
|
||||
tnCols[3] = tnCol4;
|
||||
tnCols[4] = tnCol5;
|
||||
tnCols[5] = tnCol6;
|
||||
return NotesToDWIString( tnCols );
|
||||
}
|
||||
|
||||
CString NotesWriterDWI::NotesToDWIString( TapNote cNoteCol1, TapNote cNoteCol2, TapNote cNoteCol3, TapNote cNoteCol4 )
|
||||
CString NotesWriterDWI::NotesToDWIString( TapNote tnCol1, TapNote tnCol2, TapNote tnCol3, TapNote tnCol4 )
|
||||
{
|
||||
return NotesToDWIString( cNoteCol1, TAP_EMPTY, cNoteCol2, cNoteCol3, TAP_EMPTY, cNoteCol4 );
|
||||
return NotesToDWIString( tnCol1, TAP_EMPTY, tnCol2, tnCol3, TAP_EMPTY, tnCol4 );
|
||||
}
|
||||
|
||||
char NotesWriterDWI::OptimizeDWIPair( char c1, char c2 )
|
||||
|
||||
@@ -157,26 +157,13 @@ void NotesWriterSM::WriteSMNotesTag( const Song &song, const Steps &in, RageFile
|
||||
f.Write( ssprintf( " %s:", join(",",asRadarValues).c_str() ) );
|
||||
|
||||
CString sNoteData;
|
||||
CString sAttackData;
|
||||
in.GetSMNoteData( sNoteData, sAttackData );
|
||||
in.GetSMNoteData( sNoteData );
|
||||
|
||||
vector<CString> lines;
|
||||
|
||||
split( sNoteData, "\n", lines, false );
|
||||
WriteLineList( f, lines, true, true );
|
||||
|
||||
if( sAttackData.empty() )
|
||||
f.PutLine( ";" );
|
||||
else
|
||||
{
|
||||
f.PutLine( ":" );
|
||||
|
||||
lines.clear();
|
||||
split( sAttackData, "\n", lines, false );
|
||||
WriteLineList( f, lines, true, true );
|
||||
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
bool NotesWriterSM::Write(CString sPath, const Song &out, bool bSavingCache)
|
||||
|
||||
@@ -110,7 +110,6 @@ void PlayerMinus::Load(
|
||||
NoteField* pNoteField )
|
||||
{
|
||||
m_iDCState = AS2D_IDLE;
|
||||
//LOG->Trace( "PlayerMinus::Load()", );
|
||||
|
||||
GAMESTATE->ResetNoteSkinsForPlayer( pn );
|
||||
|
||||
@@ -251,6 +250,21 @@ void PlayerMinus::Load(
|
||||
m_soundMine.SetParams( p );
|
||||
m_soundAttackLaunch.SetParams( p );
|
||||
m_soundAttackEnding.SetParams( p );
|
||||
|
||||
//
|
||||
// Load keysounds
|
||||
//
|
||||
Song* pSong = GAMESTATE->m_pCurSong;
|
||||
CString sSongDir = pSong->GetSongDir();
|
||||
m_vKeysounds.clear();
|
||||
m_vKeysounds.resize( pSong->m_vsKeysoundFile.size() );
|
||||
for( unsigned i=0; i<m_vKeysounds.size(); i++ )
|
||||
{
|
||||
CString sKeysoundFilePath = sSongDir + pSong->m_vsKeysoundFile[i];
|
||||
RageSound& sound = m_vKeysounds[i];
|
||||
sound.Load( sKeysoundFilePath );
|
||||
sound.SetParams( p );
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerMinus::Update( float fDeltaTime )
|
||||
@@ -684,7 +698,7 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
const float fSecondsFromPerfect = fabsf( fNoteOffset );
|
||||
|
||||
|
||||
TapNote tn = m_NoteData.GetTapNote(col,iIndexOverlappingNote);
|
||||
TapNote tn = m_NoteData.GetTapNote( col, iIndexOverlappingNote );
|
||||
|
||||
switch( GAMESTATE->m_PlayerController[m_PlayerNumber] )
|
||||
{
|
||||
@@ -713,8 +727,16 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
if( fSecondsFromPerfect <= ADJUSTED_WINDOW(Attack) )
|
||||
{
|
||||
m_soundAttackLaunch.Play();
|
||||
|
||||
// put attack in effect
|
||||
Attack attack = m_NoteData.GetAttackAt( col, iIndexOverlappingNote );
|
||||
Attack attack(
|
||||
ATTACK_LEVEL_1,
|
||||
-1, // now
|
||||
tn.fAttackDurationSeconds,
|
||||
tn.sAttackModifiers,
|
||||
true,
|
||||
false
|
||||
);
|
||||
GAMESTATE->LaunchAttack( OPPOSITE_PLAYER[m_PlayerNumber], attack );
|
||||
|
||||
// remove all TapAttacks on this row
|
||||
@@ -801,7 +823,14 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
score = TNS_NONE; // don't score this as anything
|
||||
|
||||
// put attack in effect
|
||||
Attack attack = m_NoteData.GetAttackAt( col, iIndexOverlappingNote );
|
||||
Attack attack(
|
||||
ATTACK_LEVEL_1,
|
||||
-1, // now
|
||||
tn.fAttackDurationSeconds,
|
||||
tn.sAttackModifiers,
|
||||
true,
|
||||
false
|
||||
);
|
||||
GAMESTATE->LaunchAttack( OPPOSITE_PLAYER[m_PlayerNumber], attack );
|
||||
|
||||
// remove all TapAttacks on this row
|
||||
@@ -857,6 +886,11 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
if( score != TNS_NONE )
|
||||
m_NoteData.SetTapNoteOffset(col, iIndexOverlappingNote, -fNoteOffset);
|
||||
|
||||
if( score != TNS_NONE && tn.bKeysound )
|
||||
{
|
||||
m_vKeysounds[tn.iKeysoundIndex].Play();
|
||||
}
|
||||
|
||||
if( GAMESTATE->m_PlayerController[m_PlayerNumber] == PC_HUMAN &&
|
||||
score >= TNS_GREAT )
|
||||
HandleAutosync(fNoteOffset);
|
||||
|
||||
@@ -107,6 +107,8 @@ protected:
|
||||
RageSound m_soundMine;
|
||||
RageSound m_soundAttackLaunch;
|
||||
RageSound m_soundAttackEnding;
|
||||
|
||||
vector<RageSound> m_vKeysounds;
|
||||
};
|
||||
|
||||
class Player : public PlayerMinus
|
||||
|
||||
@@ -1399,14 +1399,9 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM )
|
||||
{
|
||||
PlayerOptions poChosen = GAMESTATE->m_PlayerOptions[PLAYER_1];
|
||||
CString sMods = poChosen.GetString();
|
||||
const int iSongIndex = BeatToNoteRow( GAMESTATE->m_fSongBeat );
|
||||
const int row = BeatToNoteRow( GAMESTATE->m_fSongBeat );
|
||||
|
||||
Attack attack;
|
||||
attack.level = ATTACK_LEVEL_1; // does this matter?
|
||||
attack.fSecsRemaining = g_fLastInsertAttackDurationSeconds;
|
||||
attack.sModifier = sMods;
|
||||
|
||||
m_NoteFieldEdit.SetTapAttackNote( g_iLastInsertAttackTrack, iSongIndex, attack );
|
||||
m_NoteFieldEdit.SetTapAttackNote( g_iLastInsertAttackTrack, row, sMods, g_fLastInsertAttackDurationSeconds );
|
||||
GAMESTATE->RestoreSelectedOptions(); // restore the edit and playback options
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -816,7 +816,16 @@ void ScreenGameplay::SetupSong( PlayerNumber p, int iSongIndex )
|
||||
const Style* pStyle = GAMESTATE->GetCurrentStyle();
|
||||
NoteData newNoteData;
|
||||
pStyle->GetTransformedNoteDataForStyle( p, originalNoteData, newNoteData );
|
||||
m_Player[p].Load( p, newNoteData, m_pLifeMeter[p], m_pCombinedLifeMeter, m_pPrimaryScoreDisplay[p], m_pSecondaryScoreDisplay[p], m_pInventory[p], m_pPrimaryScoreKeeper[p], m_pSecondaryScoreKeeper[p] );
|
||||
m_Player[p].Load(
|
||||
p,
|
||||
newNoteData,
|
||||
m_pLifeMeter[p],
|
||||
m_pCombinedLifeMeter,
|
||||
m_pPrimaryScoreDisplay[p],
|
||||
m_pSecondaryScoreDisplay[p],
|
||||
m_pInventory[p],
|
||||
m_pPrimaryScoreKeeper[p],
|
||||
m_pSecondaryScoreKeeper[p] );
|
||||
|
||||
|
||||
// Put course options into effect. Do this after Player::Load so
|
||||
|
||||
@@ -324,10 +324,10 @@ void Song::DeleteDuplicateSteps( vector<Steps*> &vSteps )
|
||||
if( s1->GetMeter() != s2->GetMeter() )
|
||||
continue;
|
||||
/* Compare, ignoring whitespace. */
|
||||
CString sSMNoteData1, sSMAttackData1;
|
||||
s1->GetSMNoteData( sSMNoteData1, sSMAttackData1 );
|
||||
CString sSMNoteData2, sSMAttackData2;
|
||||
s2->GetSMNoteData( sSMNoteData2, sSMAttackData2 );
|
||||
CString sSMNoteData1;
|
||||
s1->GetSMNoteData( sSMNoteData1 );
|
||||
CString sSMNoteData2;
|
||||
s2->GetSMNoteData( sSMNoteData2 );
|
||||
if( RemoveInitialWhitespace(sSMNoteData1) != RemoveInitialWhitespace(sSMNoteData2) )
|
||||
continue;
|
||||
|
||||
|
||||
+21
-38
@@ -42,14 +42,13 @@ Steps::Steps()
|
||||
m_iMeter = 0;
|
||||
|
||||
notes = NULL;
|
||||
notes_comp = NULL;
|
||||
notes_comp = "";
|
||||
parent = NULL;
|
||||
}
|
||||
|
||||
Steps::~Steps()
|
||||
{
|
||||
delete notes;
|
||||
delete notes_comp;
|
||||
SAFE_DELETE( notes );
|
||||
}
|
||||
|
||||
void Steps::SetNoteData( const NoteData& noteDataNew )
|
||||
@@ -58,13 +57,11 @@ void Steps::SetNoteData( const NoteData& noteDataNew )
|
||||
|
||||
DeAutogen();
|
||||
|
||||
delete notes;
|
||||
SAFE_DELETE( notes );
|
||||
notes = new NoteData( noteDataNew );
|
||||
|
||||
delete notes_comp;
|
||||
notes_comp = new CompressedNoteData;
|
||||
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp->notes, notes_comp->attacks );
|
||||
m_uHash = GetHashForString( notes_comp->notes );
|
||||
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp );
|
||||
m_uHash = GetHashForString( notes_comp );
|
||||
}
|
||||
|
||||
void Steps::GetNoteData( NoteData& noteDataOut ) const
|
||||
@@ -82,36 +79,29 @@ void Steps::GetNoteData( NoteData& noteDataOut ) const
|
||||
}
|
||||
}
|
||||
|
||||
void Steps::SetSMNoteData( const CString ¬es_comp_, const CString &attacks_comp_ )
|
||||
void Steps::SetSMNoteData( const CString ¬es_comp_ )
|
||||
{
|
||||
delete notes;
|
||||
notes = NULL;
|
||||
SAFE_DELETE( notes );
|
||||
|
||||
if(!notes_comp)
|
||||
notes_comp = new CompressedNoteData;
|
||||
|
||||
notes_comp->notes = notes_comp_;
|
||||
notes_comp->attacks = attacks_comp_;
|
||||
m_uHash = GetHashForString( notes_comp->notes );
|
||||
notes_comp = notes_comp_;
|
||||
m_uHash = GetHashForString( notes_comp );
|
||||
}
|
||||
|
||||
void Steps::GetSMNoteData( CString ¬es_comp_out, CString &attacks_comp_out ) const
|
||||
void Steps::GetSMNoteData( CString ¬es_comp_out ) const
|
||||
{
|
||||
if(!notes_comp)
|
||||
if( !notes_comp.empty() )
|
||||
{
|
||||
if(!notes)
|
||||
if( !notes )
|
||||
{
|
||||
/* no data is no data */
|
||||
notes_comp_out = attacks_comp_out = "";
|
||||
notes_comp_out = "";
|
||||
return;
|
||||
}
|
||||
|
||||
notes_comp = new CompressedNoteData;
|
||||
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp->notes, notes_comp->attacks );
|
||||
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp );
|
||||
}
|
||||
|
||||
notes_comp_out = notes_comp->notes;
|
||||
attacks_comp_out = notes_comp->attacks;
|
||||
notes_comp_out = notes_comp;
|
||||
}
|
||||
|
||||
float Steps::PredictMeter() const
|
||||
@@ -209,8 +199,7 @@ void Steps::Decompress() const
|
||||
return;
|
||||
}
|
||||
|
||||
notes_comp = new CompressedNoteData;
|
||||
pSteps->GetSMNoteData( notes_comp->notes, notes_comp->attacks );
|
||||
pSteps->GetSMNoteData( notes_comp );
|
||||
}
|
||||
|
||||
if( notes_comp == NULL )
|
||||
@@ -223,7 +212,7 @@ void Steps::Decompress() const
|
||||
notes = new NoteData;
|
||||
notes->SetNumTracks( GameManager::StepsTypeToNumTracks(m_StepsType) );
|
||||
|
||||
NoteDataUtil::LoadFromSMNoteDataString(*notes, notes_comp->notes, notes_comp->attacks );
|
||||
NoteDataUtil::LoadFromSMNoteDataString( *notes, notes_comp );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,22 +221,16 @@ void Steps::Compress() const
|
||||
if( !m_sFilename.empty() )
|
||||
{
|
||||
/* We have a file on disk; clear all data in memory. */
|
||||
delete notes;
|
||||
notes = NULL;
|
||||
delete notes_comp;
|
||||
notes_comp = NULL;
|
||||
return;
|
||||
SAFE_DELETE( notes );
|
||||
}
|
||||
|
||||
if(!notes_comp)
|
||||
if( notes_comp.empty() )
|
||||
{
|
||||
if(!notes) return; /* no data is no data */
|
||||
notes_comp = new CompressedNoteData;
|
||||
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp->notes, notes_comp->attacks );
|
||||
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp );
|
||||
}
|
||||
|
||||
delete notes;
|
||||
notes = NULL;
|
||||
SAFE_DELETE( notes );
|
||||
}
|
||||
|
||||
/* Copy our parent's data. This is done when we're being changed from autogen
|
||||
|
||||
@@ -49,8 +49,8 @@ public:
|
||||
|
||||
void GetNoteData( NoteData& noteDataOut ) const;
|
||||
void SetNoteData( const NoteData& noteDataNew );
|
||||
void SetSMNoteData( const CString ¬es_comp, const CString &attacks_comp );
|
||||
void GetSMNoteData( CString ¬es_comp_out, CString &attacks_comp_out ) const;
|
||||
void SetSMNoteData( const CString ¬es_comp );
|
||||
void GetSMNoteData( CString ¬es_comp_out ) const;
|
||||
|
||||
void TidyUpData();
|
||||
|
||||
@@ -63,11 +63,7 @@ protected:
|
||||
* Call Compress() to force us to only have notes_comp; otherwise, creation of
|
||||
* these is transparent. */
|
||||
mutable NoteData *notes;
|
||||
struct CompressedNoteData
|
||||
{
|
||||
CString notes, attacks;
|
||||
};
|
||||
mutable CompressedNoteData *notes_comp;
|
||||
mutable CString notes_comp;
|
||||
|
||||
const Steps *Real() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user