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:
Chris Danford
2004-10-23 23:41:49 +00:00
parent 056b85f83e
commit 3ac1dba044
23 changed files with 395 additions and 499 deletions
+18 -2
View File
@@ -14,10 +14,26 @@ struct Attack
bool bOn; // for GAMESTATE bool bOn; // for GAMESTATE
bool bGlobal; // true for song-wide course mods 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; void GetAttackBeats( const Song *song, PlayerNumber pn, float &fStartBeat, float &fEndBeat ) const;
bool IsBlank() { return sModifier.empty(); } bool IsBlank() { return sModifier.empty(); }
void MakeBlank() { sModifier=""; }
Attack() { fStartSecond = -1; bOn = false; bGlobal = false; }
bool operator== ( const Attack &rhs ) const; bool operator== ( const Attack &rhs ) const;
bool ContainsTransformOrTurn() const; bool ContainsTransformOrTurn() const;
}; };
+7 -5
View File
@@ -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 /* 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. */ * 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 ); 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, /* 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 * 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 * so it can be removed later. For m_ModsToApply, leave the -1 in, so Player::Update
* knows to apply attack transforms correctly. (yuck) */ * knows to apply attack transforms correctly. (yuck) */
m_ModsToApply[target].push_back( a ); m_ModsToApply[target].push_back( attack );
if( a.fStartSecond == -1 ) if( attack.fStartSecond == -1 )
a.fStartSecond = this->m_fMusicSeconds; attack.fStartSecond = this->m_fMusicSeconds;
m_ActiveAttacks[target].push_back( a ); m_ActiveAttacks[target].push_back( attack );
this->RebuildPlayerOptionsFromActiveAttacks( target ); this->RebuildPlayerOptionsFromActiveAttacks( target );
} }
+1 -1
View File
@@ -198,7 +198,7 @@ public:
bool m_bAttackBeganThisUpdate[NUM_PLAYERS]; // flag for other objects to watch (play sounds) 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) 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 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 RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn );
void RemoveAllActiveAttacks(); // called on end of song void RemoveAllActiveAttacks(); // called on end of song
void RemoveActiveAttacksForPlayer( PlayerNumber pn, AttackLevel al=NUM_ATTACK_LEVELS /*all*/ ); void RemoveActiveAttacksForPlayer( PlayerNumber pn, AttackLevel al=NUM_ATTACK_LEVELS /*all*/ );
+8 -74
View File
@@ -77,20 +77,11 @@ void NoteData::CopyRange( const NoteData& from, int iFromIndexBegin, int iFromIn
for( int t=0; t<GetNumTracks(); t++ ) 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; int iTo = iToIndexBegin + iFrom - iFromIndexBegin;
TapNote tn = From.GetTapNote( t, iFrom );
TapNote tn = From.GetTapNote( t, from ); To.SetTapNote( t, iTo, tn );
if( tn.type == TapNote::attack )
{
Attack attack = From.GetAttackAt( t, from );
To.SetTapAttackNote( t, to, attack );
}
else
{
To.SetTapNote( t, to, tn );
}
} }
} }
@@ -110,7 +101,6 @@ void NoteData::CopyAll( const NoteData& from )
for( int c=0; c<GetNumTracks(); c++ ) for( int c=0; c<GetNumTracks(); c++ )
m_TapNotes[c] = from.m_TapNotes[c]; m_TapNotes[c] = from.m_TapNotes[c];
m_HoldNotes = from.m_HoldNotes; m_HoldNotes = from.m_HoldNotes;
m_AttackMap = from.m_AttackMap;
} }
bool NoteData::IsRowEmpty( int index ) const bool NoteData::IsRowEmpty( int index ) const
@@ -264,71 +254,17 @@ 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(); TapNote tn(
// 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::attack,
TapNote::original, TapNote::original,
true, sModifiers,
(uint8_t)i, fDurationSeconds,
false, false,
0 ); 0 );
SetTapNote( track, row, tn ); SetTapNote( track, row, tn );
return;
} }
}
// TODO: need to increase MAX_NUM_ATTACKS or handle "no more room" case
ASSERT(0);
}
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 NoteData::GetFirstRow() const
{ {
@@ -718,8 +654,6 @@ void NoteData::LoadTransformed( const NoteData& original, int iNewNumTracks, con
} }
Convert4sToHoldNotes(); Convert4sToHoldNotes();
m_AttackMap = Original.GetAttackMap();
} }
void NoteData::PadTapNotes(int rows) void NoteData::PadTapNotes(int rows)
+3 -11
View File
@@ -26,8 +26,6 @@ class NoteData
vector<HoldNote> m_HoldNotes; vector<HoldNote> m_HoldNotes;
map<unsigned,Attack> m_AttackMap;
/* Pad m_TapNotes so it includes the row "rows". */ /* Pad m_TapNotes so it includes the row "rows". */
void PadTapNotes(int rows); void PadTapNotes(int rows);
@@ -44,10 +42,6 @@ public:
int GetNumTracks() const; int GetNumTracks() const;
void SetNumTracks( int iNewNumTracks ); 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 /* Return the note at the given track and row. Row may be out of
* range; pretend the song goes on with TAP_EMPTYs indefinitely. */ * range; pretend the song goes on with TAP_EMPTYs indefinitely. */
inline TapNote GetTapNote(unsigned track, int row) const inline TapNote GetTapNote(unsigned track, int row) const
@@ -75,7 +69,7 @@ public:
bool GetNextTapNoteRowForAllTracks( int &rowInOut ) const; bool GetNextTapNoteRowForAllTracks( int &rowInOut ) const;
void MoveTapNoteTrack( int dest, int src ); void MoveTapNoteTrack( int dest, int src );
void SetTapNote(int track, int row, TapNote t); void SetTapNote( int track, int row, TapNote tn );
void ClearRange( int iNoteIndexBegin, int iNoteIndexEnd ); void ClearRange( int iNoteIndexBegin, int iNoteIndexEnd );
void ClearAll(); void ClearAll();
@@ -112,10 +106,8 @@ public:
const HoldNote &GetHoldNote( int index ) const { 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]; }
int GetMatchingHoldNote( const HoldNote &hn ) const; int GetMatchingHoldNote( const HoldNote &hn ) const;
void SetTapAttackNote( int track, int row, Attack attack ); // remove me
void PruneUnusedAttacksFromMap(); // slow void SetTapAttackNote( int track, int row, CString sModifiers, float fDurationSeconds );
const Attack& GetAttackAt( int track, int row );
// remove Attacks with SetTapNote(TAP_EMPTY)
// //
// statistics // statistics
+40 -90
View File
@@ -45,8 +45,7 @@ NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMe
return nt; return nt;
} }
void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData, CString sSMAttackData ) void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData )
{
{ {
// //
// Load note data // Load note data
@@ -108,40 +107,55 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData,
// Don't be loose with the definition. Use only 'M' since // Don't be loose with the definition. Use only 'M' since
// that's what we've been writing to disk. -Chris // that's what we've been writing to disk. -Chris
case 'M': tn = TAP_ORIGINAL_MINE; break; case 'M': tn = TAP_ORIGINAL_MINE; break;
case 'A': tn = TAP_ORIGINAL_ATTACK; break;
default: 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 /* 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 * 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 * due to invalid data. We should probably check for this when
* we load SM data for the first time ... */ * we load SM data for the first time ... */
// ASSERT(0); // ASSERT(0);
tn = TAP_EMPTY; tn = TAP_EMPTY;
}
break; break;
} }
p++; 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]") // look for optional keysound index (e.g. "[123]")
if( *p == '[' ) if( *p == '[' )
{ {
p++; p++;
unsigned uKeysoundIndex = 0; int iKeysoundIndex = 0;
if( 1 == sscanf( p, "%u]", &uKeysoundIndex ) ) // not fatal if this fails due to malformed data if( 1 == sscanf( p, "%d]", &iKeysoundIndex ) ) // not fatal if this fails due to malformed data
{ {
tn.bKeysound = true; tn.bKeysound = true;
tn.keysoundIndex = (uint16_t)uKeysoundIndex; tn.iKeysoundIndex = iKeysoundIndex;
} }
// skip past the ']' // skip past the ']'
@@ -165,46 +179,7 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData,
out.Convert2sAnd3sToHoldNotes(); out.Convert2sAnd3sToHoldNotes();
} }
{ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString &notes_out )
//
// Load attack data
//
CStringArray asLines;
split( sSMAttackData, ",", asLines, true );
for( unsigned i=0; i<asLines.size(); i++ )
{
CString& sLine = asLines[i];
TrimLeft( sLine );
TrimRight( sLine );
if( sLine.empty() )
continue; // skip
CStringArray asBits;
split( sLine, "=", asBits, true );
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 &notes_out, CString &attacks_out )
{
{ {
// //
// Get note data // Get note data
@@ -249,7 +224,7 @@ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString &notes_out,
case TapNote::hold_head: c = '2'; break; case TapNote::hold_head: c = '2'; break;
case TapNote::hold_tail: c = '3'; break; case TapNote::hold_tail: c = '3'; break;
case TapNote::mine: c = 'M'; break; case TapNote::mine: c = 'M'; break;
case TapNote::attack: c = 'a' + (char)tn.attackIndex ; break; case TapNote::attack: c = 'A'; break;
default: default:
ASSERT(0); ASSERT(0);
c = '0'; c = '0';
@@ -257,9 +232,13 @@ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString &notes_out,
} }
sRet.append(1, c); sRet.append(1, c);
if( tn.type == TapNote::attack )
{
sRet.append( ssprintf("{%s:%.2f}",tn.sAttackModifiers.c_str(), tn.fAttackDurationSeconds) );
}
if( tn.bKeysound ) if( tn.bKeysound )
{ {
sRet.append( ssprintf("[%u]",tn.keysoundIndex) ); sRet.append( ssprintf("[%d]",tn.iKeysoundIndex) );
} }
} }
@@ -268,28 +247,6 @@ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString &notes_out,
} }
} }
{
//
// Get attack data
//
CStringArray asLines;
for( map<unsigned,Attack>::const_iterator iter = in_.GetAttackMap().begin();
iter != in_.GetAttackMap().end();
iter++ )
{
int attackIndex = iter->first;
char ch = 'a' + (char)attackIndex;
Attack attack = iter->second;
attack.sModifier.Replace( ',', '.' ); // comma is the map item separator
asLines.push_back( ssprintf("%c=%s=%f,\n", ch, attack.sModifier.c_str(), attack.fSecsRemaining) );
}
attacks_out = join( ",", asLines );
}
}
void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks ) void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks )
{ {
// reset all notes // reset all notes
@@ -354,8 +311,6 @@ void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &o
} }
out.Convert4sToHoldNotes(); out.Convert4sToHoldNotes();
out.GetAttackMap() = Original.GetAttackMap();
} }
void NoteDataUtil::LoadOverlapped( const NoteData &input, NoteData &out, int iNewNumTracks ) 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 ); float fBeat = pSong->GetBeatFromElapsedTime( sec );
int iBeat = (int)fBeat; int iBeat = (int)fBeat;
int iTrack = iBeat % nd.GetNumTracks(); // deterministically calculates track int iTrack = iBeat % nd.GetNumTracks(); // deterministically calculates track
Attack attack; nd.SetTapAttackNote( iTrack, BeatToNoteRow(fBeat), szAttacks[rand()%ARRAYSIZE(szAttacks)], 15 );
attack.fStartSecond = -1;
attack.fSecsRemaining = 15;
attack.sModifier = szAttacks[rand()%ARRAYSIZE(szAttacks)];
attack.level = ATTACK_LEVEL_1;
nd.SetTapAttackNote( iTrack, BeatToNoteRow(fBeat), attack );
} }
} }
+2 -2
View File
@@ -17,8 +17,8 @@ struct RadarValues;
namespace NoteDataUtil namespace NoteDataUtil
{ {
NoteType GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex ); NoteType GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex );
void LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData, CString sSMAttackData ); void LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData );
void GetSMNoteDataString( const NoteData &in, CString &notes_out, CString &attacks_out ); void GetSMNoteDataString( const NoteData &in, CString &notes_out );
void LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks ); void LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks );
void LoadOverlapped( 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 ); void LoadTransformedLights( const NoteData &in, NoteData &out, int iNewNumTracks );
+1 -2
View File
@@ -621,9 +621,8 @@ void NoteField::DrawPrimitives()
NoteDisplayCols *nd = CurDisplay->second; NoteDisplayCols *nd = CurDisplay->second;
if( bIsAttack ) if( bIsAttack )
{ {
const Attack& attack = GetAttackAt( c, i );
Sprite sprite; Sprite sprite;
sprite.Load( THEME->GetPathToG("NoteField attack "+attack.sModifier) ); sprite.Load( THEME->GetPathToG("NoteField attack "+tn.sAttackModifiers) );
float fBeat = NoteRowToBeat(i); float fBeat = NoteRowToBeat(i);
SearchForBeat( CurDisplay, NextDisplay, fBeat ); SearchForBeat( CurDisplay, NextDisplay, fBeat );
NoteDisplayCols *nd = CurDisplay->second; NoteDisplayCols *nd = CurDisplay->second;
+9 -8
View File
@@ -1,14 +1,15 @@
#include "global.h" #include "global.h"
#include "NoteTypes.h" #include "NoteTypes.h"
TapNote TAP_EMPTY = { TapNote::empty, TapNote::original, 0 }; TapNote TAP_EMPTY ( TapNote::empty, TapNote::original, "", 0, false, 0 );
TapNote TAP_ORIGINAL_TAP = { TapNote::tap, TapNote::original, 0 }; TapNote TAP_ORIGINAL_TAP ( TapNote::tap, TapNote::original, "", 0, false, 0 );
TapNote TAP_ORIGINAL_HOLD_HEAD = { TapNote::hold_head, TapNote::original, 0 }; // '2' TapNote TAP_ORIGINAL_HOLD_HEAD ( TapNote::hold_head, TapNote::original, "", 0, false, 0 );
TapNote TAP_ORIGINAL_HOLD_TAIL = { TapNote::hold_tail, TapNote::original, 0 }; // '3' TapNote TAP_ORIGINAL_HOLD_TAIL ( TapNote::hold_tail, TapNote::original, "", 0, false, 0 );
TapNote TAP_ORIGINAL_HOLD = { TapNote::hold, TapNote::original, 0 }; // '4' TapNote TAP_ORIGINAL_HOLD ( TapNote::hold, TapNote::original, "", 0, false, 0 );
TapNote TAP_ORIGINAL_MINE = { TapNote::mine, TapNote::original, 0 }; TapNote TAP_ORIGINAL_MINE ( TapNote::mine, TapNote::original, "", 0, false, 0 );
TapNote TAP_ADDITION_TAP = { TapNote::tap, TapNote::addition, 0 }; TapNote TAP_ORIGINAL_ATTACK ( TapNote::attack, TapNote::original, "", 0, false, 0 );
TapNote TAP_ADDITION_MINE = { TapNote::mine, TapNote::addition, 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 ) float NoteTypeToBeat( NoteType nt )
{ {
+20 -22
View File
@@ -4,15 +4,14 @@
struct TapNote struct TapNote
{ {
enum Type { enum Type {
empty, empty, // no note here
tap, tap,
hold_head, // graded like a TAP_TAP hold_head, // graded like a TAP_TAP
hold_tail, /* In 2sand3s mode, holds are deleted and TAP_HOLD_END is added: */ 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: */ hold, /* In 4s mode, holds and TAP_HOLD_HEAD are deleted and TAP_HOLD is added: */
mine, // don't step! mine, // don't step!
attack, attack,
}; } type;
unsigned type : 3; // no unsigned enum support in VC++
enum Source { enum Source {
original, // part of the original NoteData original, // part of the original NoteData
addition, // additional note added by a transform addition, // additional note added by a transform
@@ -27,44 +26,42 @@ struct TapNote
// then this is triggered automatically to keep the sound going // then this is triggered automatically to keep the sound going
// 2 - if we're NOT [anything else], we ignore this. // 2 - if we're NOT [anything else], we ignore this.
// Equivalent to all 4s aside from the first one. // Equivalent to all 4s aside from the first one.
}; } source;
unsigned source : 2; // only valid if type!=empty
bool bAttack : 1; // true if this note causes an attack when hit // Only valid if type == attack.
bool bKeysound : 1; // true if this note plays a keysound when hit CString sAttackModifiers;
float fAttackDurationSeconds;
// CAREFUL: small fields grouped together for alignment. bool bKeysound; // true if this note plays a keysound when hit
uint8_t attackIndex; // index into NoteData's vector of attacks int iKeysoundIndex; // index into Song's vector of keysound files.
// Only valid if bAttack.
uint16_t keysoundIndex; // index into Song's vector of keysound files.
// Only valid if bKeysound. // Only valid if bKeysound.
// Some songs have > 256 keysounds.
void Set( TapNote() {}
TapNote(
Type type_, Type type_,
Source source_, Source source_,
bool bAttack_, CString sAttackModifiers_,
uint8_t attackIndex_, float fAttackDurationSeconds_,
bool bKeysound_, bool bKeysound_,
uint16_t keysoundIndex_ ) int iKeysoundIndex_ )
{ {
type = type_; type = type_;
source = source_; source = source_;
bAttack = bAttack_; sAttackModifiers = sAttackModifiers_;
attackIndex = attackIndex_; fAttackDurationSeconds = fAttackDurationSeconds_;
bKeysound = bKeysound_; bKeysound = bKeysound_;
keysoundIndex = keysoundIndex_; iKeysoundIndex = iKeysoundIndex_;
} }
bool operator==( const TapNote &other ) bool operator==( const TapNote &other )
{ {
#define COMPARE(x) if(x!=other.x) return false; #define COMPARE(x) if(x!=other.x) return false;
COMPARE(type); COMPARE(type);
COMPARE(source); COMPARE(source);
COMPARE(bAttack); COMPARE(sAttackModifiers);
COMPARE(fAttackDurationSeconds);
COMPARE(bKeysound); COMPARE(bKeysound);
COMPARE(attackIndex); COMPARE(iKeysoundIndex);
COMPARE(keysoundIndex);
#undef COMPARE #undef COMPARE
return true; 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_TAIL; // '3'
extern TapNote TAP_ORIGINAL_HOLD; // '4' extern TapNote TAP_ORIGINAL_HOLD; // '4'
extern TapNote TAP_ORIGINAL_MINE; // 'M' extern TapNote TAP_ORIGINAL_MINE; // 'M'
extern TapNote TAP_ORIGINAL_ATTACK; // 'A'
extern TapNote TAP_ADDITION_TAP; extern TapNote TAP_ADDITION_TAP;
extern TapNote TAP_ADDITION_MINE; extern TapNote TAP_ADDITION_MINE;
+21 -17
View File
@@ -65,6 +65,7 @@ enum BmsTrack
BMS_P2_TURN, BMS_P2_TURN,
BMS_P2_KEY6, BMS_P2_KEY6,
BMS_P2_KEY7, BMS_P2_KEY7,
BMS_AUTO_KEYSOUND,
NUM_BMS_TRACKS, NUM_BMS_TRACKS,
BMS_TRACK_INVALID, BMS_TRACK_INVALID,
}; };
@@ -83,6 +84,7 @@ static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, bool
switch( iRawTrack ) switch( iRawTrack )
{ {
case 1: bmsTrackOut = BMS_AUTO_KEYSOUND; break;
case 11: bmsTrackOut = BMS_P1_KEY1; break; case 11: bmsTrackOut = BMS_P1_KEY1; break;
case 12: bmsTrackOut = BMS_P1_KEY2; break; case 12: bmsTrackOut = BMS_P1_KEY2; break;
case 13: bmsTrackOut = BMS_P1_KEY3; break; case 13: bmsTrackOut = BMS_P1_KEY3; break;
@@ -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() ); 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. // BMS player code. Fill in below and use to determine StepsType.
int iPlayer = -1; int iPlayer = -1;
NoteData* pNoteData = new NoteData; NoteData ndNotes;
pNoteData->SetNumTracks( NUM_BMS_TRACKS ); ndNotes.SetNumTracks( NUM_BMS_TRACKS );
RageFile file; RageFile file;
if( !file.Open(sPath) ) if( !file.Open(sPath) )
RageException::Throw( "Failed to open \"%s\" for reading: %s", sPath.c_str(), file.GetError().c_str() ); RageException::Throw( "Failed to open \"%s\" for reading: %s", sPath.c_str(), file.GetError().c_str() );
while( !file.AtEOF() ) while( !file.AtEOF() )
{ {
CString line; CString line;
if( file.GetLine( line ) == -1 ) if( file.GetLine( line ) == -1 )
{ {
LOG->Warn( "Error reading \"%s\": %s", sPath.c_str(), file.GetError().c_str() ); LOG->Warn( "Error reading \"%s\": %s", sPath.c_str(), file.GetError().c_str() );
delete pNoteData;
return false; return false;
} }
@@ -276,11 +278,11 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CSt
if( sNoteId != "00" ) if( sNoteId != "00" )
{ {
TapNote tn = TAP_ORIGINAL_TAP; 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() ) if( it != mapWavIdToKeysoundIndex.end() )
{ {
tn.bKeysound = true; tn.bKeysound = true;
tn.keysoundIndex = (uint16_t)it->second; tn.iKeysoundIndex = it->second;
} }
vTapNotes.push_back( tn ); vTapNotes.push_back( tn );
} }
@@ -303,23 +305,28 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CSt
BmsTrack bmsTrack; BmsTrack bmsTrack;
bool bIsHold; bool bIsHold;
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, bIsHold) ) if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, bIsHold) )
{
if( bmsTrack == BMS_AUTO_KEYSOUND )
{
}
else
{ {
TapNote tn = vTapNotes[j]; TapNote tn = vTapNotes[j];
tn.type = bIsHold ? TapNote::hold_head : TapNote::tap; tn.type = bIsHold ? TapNote::hold_head : TapNote::tap;
pNoteData->SetTapNote(bmsTrack, iNoteIndex, tn); 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 // we're done reading in all of the BMS values
if( out.m_StepsType == STEPS_TYPE_INVALID ) if( out.m_StepsType == STEPS_TYPE_INVALID )
{ {
LOG->Warn( "Couldn't determine note type of file '%s'", sPath.c_str() ); LOG->Warn( "Couldn't determine note type of file '%s'", sPath.c_str() );
delete pNoteData;
return false; return false;
} }
@@ -425,14 +432,11 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CSt
ASSERT(0); ASSERT(0);
} }
NoteData* pNoteData2 = new NoteData; NoteData noteData2;
pNoteData2->SetNumTracks( iNumNewTracks ); noteData2.SetNumTracks( iNumNewTracks );
pNoteData2->LoadTransformed( *pNoteData, iNumNewTracks, iTransformNewToOld ); noteData2.LoadTransformed( ndNotes, iNumNewTracks, iTransformNewToOld );
out.SetNoteData(*pNoteData2); out.SetNoteData( noteData2 );
delete pNoteData;
delete pNoteData2;
out.TidyUpData(); 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 // 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. // 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]; CString sPath = out.GetSongDir() + arrayBMSFileNames[0];
+1 -1
View File
@@ -9,7 +9,7 @@ class Steps;
class BMSLoader: public NotesLoader 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 ); void SlideDuplicateDifficulties( Song &p );
+5 -10
View File
@@ -17,16 +17,12 @@ void SMLoader::LoadFromSMTokens(
CString sMeter, CString sMeter,
CString sRadarValues, CString sRadarValues,
CString sNoteData, CString sNoteData,
CString sAttackData,
Steps &out Steps &out
) )
{ {
TrimLeft(sStepsType); TrimLeft(sStepsType); TrimRight(sStepsType);
TrimRight(sStepsType); TrimLeft(sDescription); TrimRight(sDescription);
TrimLeft(sDescription); TrimLeft(sDifficulty); TrimRight(sDifficulty);
TrimRight(sDescription);
TrimLeft(sDifficulty);
TrimRight(sDifficulty);
// LOG->Trace( "Steps::LoadFromSMTokens()" ); // LOG->Trace( "Steps::LoadFromSMTokens()" );
@@ -55,7 +51,7 @@ void SMLoader::LoadFromSMTokens(
out.SetRadarValues( v ); out.SetRadarValues( v );
} }
out.SetSMNoteData(sNoteData, sAttackData); out.SetSMNoteData(sNoteData);
out.TidyUpData(); out.TidyUpData();
} }
@@ -364,7 +360,6 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
sParams[4], sParams[4],
sParams[5], sParams[5],
sParams[6], sParams[6],
(iNumParams>=8)?sParams[7]:CString(""),
*pNewNotes ); *pNewNotes );
out.AddSteps( pNewNotes ); out.AddSteps( pNewNotes );
@@ -462,7 +457,7 @@ bool SMLoader::LoadEdit( CString sEditFilePath, ProfileSlot slot )
} }
LoadFromSMTokens( 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);
pNewNotes->SetLoadedFromProfile( slot ); pNewNotes->SetLoadedFromProfile( slot );
-1
View File
@@ -18,7 +18,6 @@ class SMLoader: public NotesLoader
CString sMeter, CString sMeter,
CString sRadarValues, CString sRadarValues,
CString sNoteData, CString sNoteData,
CString sAttackData,
Steps &out); Steps &out);
bool FromCache; bool FromCache;
+14 -14
View File
@@ -9,20 +9,20 @@
#include "RageFile.h" #include "RageFile.h"
/* Output is an angle bracket expression without angle brackets, eg. "468". */ /* 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' }; const char dirs[] = { '4', 'C', '2', '8', 'D', '6' };
CString taps, holds, ret; CString taps, holds, ret;
for( int col = 0; col < 6; ++col ) for( int col = 0; col < 6; ++col )
{ {
switch( cNoteCols[col].type ) switch( tnCols[col].type )
{ {
case TapNote::empty: case TapNote::empty:
case TapNote::mine: case TapNote::mine:
continue; continue;
} }
if( cNoteCols[col].type == TapNote::hold_head ) if( tnCols[col].type == TapNote::hold_head )
holds += dirs[col]; holds += dirs[col];
else else
taps += dirs[col]; taps += dirs[col];
@@ -81,21 +81,21 @@ CString NotesWriterDWI::NotesToDWIString( const TapNote cNoteCols[6] )
return '0';*/ 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]; TapNote tnCols[6];
cNoteCols[0] = cNoteCol1; tnCols[0] = tnCol1;
cNoteCols[1] = cNoteCol2; tnCols[1] = tnCol2;
cNoteCols[2] = cNoteCol3; tnCols[2] = tnCol3;
cNoteCols[3] = cNoteCol4; tnCols[3] = tnCol4;
cNoteCols[4] = cNoteCol5; tnCols[4] = tnCol5;
cNoteCols[5] = cNoteCol6; tnCols[5] = tnCol6;
return NotesToDWIString( cNoteCols ); 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 ) char NotesWriterDWI::OptimizeDWIPair( char c1, char c2 )
+1 -14
View File
@@ -157,26 +157,13 @@ void NotesWriterSM::WriteSMNotesTag( const Song &song, const Steps &in, RageFile
f.Write( ssprintf( " %s:", join(",",asRadarValues).c_str() ) ); f.Write( ssprintf( " %s:", join(",",asRadarValues).c_str() ) );
CString sNoteData; CString sNoteData;
CString sAttackData; in.GetSMNoteData( sNoteData );
in.GetSMNoteData( sNoteData, sAttackData );
vector<CString> lines; vector<CString> lines;
split( sNoteData, "\n", lines, false ); split( sNoteData, "\n", lines, false );
WriteLineList( f, lines, true, true ); WriteLineList( f, lines, true, true );
if( sAttackData.empty() )
f.PutLine( ";" ); f.PutLine( ";" );
else
{
f.PutLine( ":" );
lines.clear();
split( sAttackData, "\n", lines, false );
WriteLineList( f, lines, true, true );
f.PutLine( ";" );
}
} }
bool NotesWriterSM::Write(CString sPath, const Song &out, bool bSavingCache) bool NotesWriterSM::Write(CString sPath, const Song &out, bool bSavingCache)
+37 -3
View File
@@ -110,7 +110,6 @@ void PlayerMinus::Load(
NoteField* pNoteField ) NoteField* pNoteField )
{ {
m_iDCState = AS2D_IDLE; m_iDCState = AS2D_IDLE;
//LOG->Trace( "PlayerMinus::Load()", );
GAMESTATE->ResetNoteSkinsForPlayer( pn ); GAMESTATE->ResetNoteSkinsForPlayer( pn );
@@ -251,6 +250,21 @@ void PlayerMinus::Load(
m_soundMine.SetParams( p ); m_soundMine.SetParams( p );
m_soundAttackLaunch.SetParams( p ); m_soundAttackLaunch.SetParams( p );
m_soundAttackEnding.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 ) void PlayerMinus::Update( float fDeltaTime )
@@ -713,8 +727,16 @@ void PlayerMinus::Step( int col, RageTimer tm )
if( fSecondsFromPerfect <= ADJUSTED_WINDOW(Attack) ) if( fSecondsFromPerfect <= ADJUSTED_WINDOW(Attack) )
{ {
m_soundAttackLaunch.Play(); m_soundAttackLaunch.Play();
// put attack in effect // 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 ); GAMESTATE->LaunchAttack( OPPOSITE_PLAYER[m_PlayerNumber], attack );
// remove all TapAttacks on this row // 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 score = TNS_NONE; // don't score this as anything
// put attack in effect // 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 ); GAMESTATE->LaunchAttack( OPPOSITE_PLAYER[m_PlayerNumber], attack );
// remove all TapAttacks on this row // remove all TapAttacks on this row
@@ -857,6 +886,11 @@ void PlayerMinus::Step( int col, RageTimer tm )
if( score != TNS_NONE ) if( score != TNS_NONE )
m_NoteData.SetTapNoteOffset(col, iIndexOverlappingNote, -fNoteOffset); 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 && if( GAMESTATE->m_PlayerController[m_PlayerNumber] == PC_HUMAN &&
score >= TNS_GREAT ) score >= TNS_GREAT )
HandleAutosync(fNoteOffset); HandleAutosync(fNoteOffset);
+2
View File
@@ -107,6 +107,8 @@ protected:
RageSound m_soundMine; RageSound m_soundMine;
RageSound m_soundAttackLaunch; RageSound m_soundAttackLaunch;
RageSound m_soundAttackEnding; RageSound m_soundAttackEnding;
vector<RageSound> m_vKeysounds;
}; };
class Player : public PlayerMinus class Player : public PlayerMinus
+2 -7
View File
@@ -1399,14 +1399,9 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM )
{ {
PlayerOptions poChosen = GAMESTATE->m_PlayerOptions[PLAYER_1]; PlayerOptions poChosen = GAMESTATE->m_PlayerOptions[PLAYER_1];
CString sMods = poChosen.GetString(); CString sMods = poChosen.GetString();
const int iSongIndex = BeatToNoteRow( GAMESTATE->m_fSongBeat ); const int row = BeatToNoteRow( GAMESTATE->m_fSongBeat );
Attack attack; m_NoteFieldEdit.SetTapAttackNote( g_iLastInsertAttackTrack, row, sMods, g_fLastInsertAttackDurationSeconds );
attack.level = ATTACK_LEVEL_1; // does this matter?
attack.fSecsRemaining = g_fLastInsertAttackDurationSeconds;
attack.sModifier = sMods;
m_NoteFieldEdit.SetTapAttackNote( g_iLastInsertAttackTrack, iSongIndex, attack );
GAMESTATE->RestoreSelectedOptions(); // restore the edit and playback options GAMESTATE->RestoreSelectedOptions(); // restore the edit and playback options
} }
break; break;
+10 -1
View File
@@ -816,7 +816,16 @@ void ScreenGameplay::SetupSong( PlayerNumber p, int iSongIndex )
const Style* pStyle = GAMESTATE->GetCurrentStyle(); const Style* pStyle = GAMESTATE->GetCurrentStyle();
NoteData newNoteData; NoteData newNoteData;
pStyle->GetTransformedNoteDataForStyle( p, originalNoteData, 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 // Put course options into effect. Do this after Player::Load so
+4 -4
View File
@@ -324,10 +324,10 @@ void Song::DeleteDuplicateSteps( vector<Steps*> &vSteps )
if( s1->GetMeter() != s2->GetMeter() ) if( s1->GetMeter() != s2->GetMeter() )
continue; continue;
/* Compare, ignoring whitespace. */ /* Compare, ignoring whitespace. */
CString sSMNoteData1, sSMAttackData1; CString sSMNoteData1;
s1->GetSMNoteData( sSMNoteData1, sSMAttackData1 ); s1->GetSMNoteData( sSMNoteData1 );
CString sSMNoteData2, sSMAttackData2; CString sSMNoteData2;
s2->GetSMNoteData( sSMNoteData2, sSMAttackData2 ); s2->GetSMNoteData( sSMNoteData2 );
if( RemoveInitialWhitespace(sSMNoteData1) != RemoveInitialWhitespace(sSMNoteData2) ) if( RemoveInitialWhitespace(sSMNoteData1) != RemoveInitialWhitespace(sSMNoteData2) )
continue; continue;
+20 -37
View File
@@ -42,14 +42,13 @@ Steps::Steps()
m_iMeter = 0; m_iMeter = 0;
notes = NULL; notes = NULL;
notes_comp = NULL; notes_comp = "";
parent = NULL; parent = NULL;
} }
Steps::~Steps() Steps::~Steps()
{ {
delete notes; SAFE_DELETE( notes );
delete notes_comp;
} }
void Steps::SetNoteData( const NoteData& noteDataNew ) void Steps::SetNoteData( const NoteData& noteDataNew )
@@ -58,13 +57,11 @@ void Steps::SetNoteData( const NoteData& noteDataNew )
DeAutogen(); DeAutogen();
delete notes; SAFE_DELETE( notes );
notes = new NoteData( noteDataNew ); notes = new NoteData( noteDataNew );
delete notes_comp; NoteDataUtil::GetSMNoteDataString( *notes, notes_comp );
notes_comp = new CompressedNoteData; m_uHash = GetHashForString( notes_comp );
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp->notes, notes_comp->attacks );
m_uHash = GetHashForString( notes_comp->notes );
} }
void Steps::GetNoteData( NoteData& noteDataOut ) const void Steps::GetNoteData( NoteData& noteDataOut ) const
@@ -82,36 +79,29 @@ void Steps::GetNoteData( NoteData& noteDataOut ) const
} }
} }
void Steps::SetSMNoteData( const CString &notes_comp_, const CString &attacks_comp_ ) void Steps::SetSMNoteData( const CString &notes_comp_ )
{ {
delete notes; SAFE_DELETE( notes );
notes = NULL;
if(!notes_comp) notes_comp = notes_comp_;
notes_comp = new CompressedNoteData; m_uHash = GetHashForString( notes_comp );
notes_comp->notes = notes_comp_;
notes_comp->attacks = attacks_comp_;
m_uHash = GetHashForString( notes_comp->notes );
} }
void Steps::GetSMNoteData( CString &notes_comp_out, CString &attacks_comp_out ) const void Steps::GetSMNoteData( CString &notes_comp_out ) const
{ {
if(!notes_comp) if( !notes_comp.empty() )
{ {
if( !notes ) if( !notes )
{ {
/* no data is no data */ /* no data is no data */
notes_comp_out = attacks_comp_out = ""; notes_comp_out = "";
return; return;
} }
notes_comp = new CompressedNoteData; NoteDataUtil::GetSMNoteDataString( *notes, notes_comp );
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp->notes, notes_comp->attacks );
} }
notes_comp_out = notes_comp->notes; notes_comp_out = notes_comp;
attacks_comp_out = notes_comp->attacks;
} }
float Steps::PredictMeter() const float Steps::PredictMeter() const
@@ -209,8 +199,7 @@ void Steps::Decompress() const
return; return;
} }
notes_comp = new CompressedNoteData; pSteps->GetSMNoteData( notes_comp );
pSteps->GetSMNoteData( notes_comp->notes, notes_comp->attacks );
} }
if( notes_comp == NULL ) if( notes_comp == NULL )
@@ -223,7 +212,7 @@ void Steps::Decompress() const
notes = new NoteData; notes = new NoteData;
notes->SetNumTracks( GameManager::StepsTypeToNumTracks(m_StepsType) ); 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() ) if( !m_sFilename.empty() )
{ {
/* We have a file on disk; clear all data in memory. */ /* We have a file on disk; clear all data in memory. */
delete notes; SAFE_DELETE( notes );
notes = NULL;
delete notes_comp;
notes_comp = NULL;
return;
} }
if(!notes_comp) if( notes_comp.empty() )
{ {
if(!notes) return; /* no data is no data */ if(!notes) return; /* no data is no data */
notes_comp = new CompressedNoteData; NoteDataUtil::GetSMNoteDataString( *notes, notes_comp );
NoteDataUtil::GetSMNoteDataString( *notes, notes_comp->notes, notes_comp->attacks );
} }
delete notes; SAFE_DELETE( notes );
notes = NULL;
} }
/* Copy our parent's data. This is done when we're being changed from autogen /* Copy our parent's data. This is done when we're being changed from autogen
+3 -7
View File
@@ -49,8 +49,8 @@ public:
void GetNoteData( NoteData& noteDataOut ) const; void GetNoteData( NoteData& noteDataOut ) const;
void SetNoteData( const NoteData& noteDataNew ); void SetNoteData( const NoteData& noteDataNew );
void SetSMNoteData( const CString &notes_comp, const CString &attacks_comp ); void SetSMNoteData( const CString &notes_comp );
void GetSMNoteData( CString &notes_comp_out, CString &attacks_comp_out ) const; void GetSMNoteData( CString &notes_comp_out ) const;
void TidyUpData(); void TidyUpData();
@@ -63,11 +63,7 @@ protected:
* Call Compress() to force us to only have notes_comp; otherwise, creation of * Call Compress() to force us to only have notes_comp; otherwise, creation of
* these is transparent. */ * these is transparent. */
mutable NoteData *notes; mutable NoteData *notes;
struct CompressedNoteData mutable CString notes_comp;
{
CString notes, attacks;
};
mutable CompressedNoteData *notes_comp;
const Steps *Real() const; const Steps *Real() const;