Just about all that can be done before Loading.
This next commit will be the big one it seems.
This commit is contained in:
+87
-96
@@ -258,7 +258,7 @@ static set<RString> BlacklistedImages;
|
|||||||
bool Song::LoadFromSongDir( RString sDir )
|
bool Song::LoadFromSongDir( RString sDir )
|
||||||
{
|
{
|
||||||
// LOG->Trace( "Song::LoadFromSongDir(%s)", sDir.c_str() );
|
// LOG->Trace( "Song::LoadFromSongDir(%s)", sDir.c_str() );
|
||||||
ASSERT( sDir != "" );
|
ASSERT_M( sDir != "", "Songs can't be loaded from an empty directory!" );
|
||||||
|
|
||||||
// make sure there is a trailing slash at the end of sDir
|
// make sure there is a trailing slash at the end of sDir
|
||||||
if( sDir.Right(1) != "/" )
|
if( sDir.Right(1) != "/" )
|
||||||
@@ -321,10 +321,14 @@ bool Song::LoadFromSongDir( RString sDir )
|
|||||||
LOG->UserLog( "Song", sDir, "has no music file either. Ignoring this song directory." );
|
LOG->UserLog( "Song", sDir, "has no music file either. Ignoring this song directory." );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Make sure we have a future filename figured out.
|
||||||
|
vector<RString> folders;
|
||||||
|
split(sDir, "/", folders);
|
||||||
|
RString songName = folders[2] + ".ssc";
|
||||||
|
this->m_sSongFileName = sDir + songName;
|
||||||
// Continue on with a blank Song so that people can make adjustments using the editor.
|
// Continue on with a blank Song so that people can make adjustments using the editor.
|
||||||
}
|
}
|
||||||
TidyUpData();
|
TidyUpData(false, true);
|
||||||
|
|
||||||
// save a cache file so we don't have to parse it all over again next time
|
// save a cache file so we don't have to parse it all over again next time
|
||||||
if( !SaveToCacheFile() )
|
if( !SaveToCacheFile() )
|
||||||
@@ -333,8 +337,6 @@ bool Song::LoadFromSongDir( RString sDir )
|
|||||||
|
|
||||||
FOREACH( Steps*, m_vpSteps, s )
|
FOREACH( Steps*, m_vpSteps, s )
|
||||||
{
|
{
|
||||||
(*s)->SetFilename( sCacheFilePath );
|
|
||||||
|
|
||||||
/* Compress all Steps. During initial caching, this will remove cached
|
/* Compress all Steps. During initial caching, this will remove cached
|
||||||
* NoteData; during cached loads, this will just remove cached SMData. */
|
* NoteData; during cached loads, this will just remove cached SMData. */
|
||||||
(*s)->Compress();
|
(*s)->Compress();
|
||||||
@@ -446,7 +448,7 @@ void FixupPath( RString &path, const RString &sSongPath )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Songs in BlacklistImages will never be autodetected as song images.
|
// Songs in BlacklistImages will never be autodetected as song images.
|
||||||
void Song::TidyUpData( bool bFromCache )
|
void Song::TidyUpData( bool fromCache, bool duringCache )
|
||||||
{
|
{
|
||||||
// We need to do this before calling any of HasMusic, HasHasCDTitle, etc.
|
// We need to do this before calling any of HasMusic, HasHasCDTitle, etc.
|
||||||
ASSERT_M( m_sSongDir.Left(3) != "../", m_sSongDir ); // meaningless
|
ASSERT_M( m_sSongDir.Left(3) != "../", m_sSongDir ); // meaningless
|
||||||
@@ -543,7 +545,7 @@ void Song::TidyUpData( bool bFromCache )
|
|||||||
|
|
||||||
/* Generate these before we autogen notes, so the new notes can inherit
|
/* Generate these before we autogen notes, so the new notes can inherit
|
||||||
* their source's values. */
|
* their source's values. */
|
||||||
ReCalculateRadarValuesAndLastSecond( bFromCache );
|
ReCalculateRadarValuesAndLastSecond( fromCache, true );
|
||||||
|
|
||||||
Trim( m_sMainTitle );
|
Trim( m_sMainTitle );
|
||||||
Trim( m_sSubTitle );
|
Trim( m_sSubTitle );
|
||||||
@@ -791,64 +793,6 @@ void Song::TidyUpData( bool bFromCache )
|
|||||||
* for edits). We should be able to use difficulty names as unique
|
* for edits). We should be able to use difficulty names as unique
|
||||||
* identifiers for steps. */
|
* identifiers for steps. */
|
||||||
SongUtil::AdjustDuplicateSteps( this );
|
SongUtil::AdjustDuplicateSteps( this );
|
||||||
|
|
||||||
{
|
|
||||||
/* Generated filename; this doesn't always point to a loadable file,
|
|
||||||
* but instead points to the file we should write changed files to,
|
|
||||||
* and will always be a .SSC.
|
|
||||||
*
|
|
||||||
* This is a little tricky. We can't always use the song title directly,
|
|
||||||
* since it might contain characters we can't store in filenames. Two
|
|
||||||
* easy options: we could manually filter out invalid characters, or we
|
|
||||||
* could use the name of the directory, which is always a valid filename
|
|
||||||
* and should always be the same as the song. The former might not catch
|
|
||||||
* everything--filename restrictions are platform-specific; we might even
|
|
||||||
* be on an 8.3 filesystem, so let's do the latter.
|
|
||||||
*
|
|
||||||
* We can't rely on searching for other data filenames; it works for DWIs,
|
|
||||||
* but not KSFs and BMSs.
|
|
||||||
*
|
|
||||||
* So, let's do this (by priority):
|
|
||||||
* 1. If there's a .SSC file, use that filename. No reason to use anything
|
|
||||||
* else; it's the filename in use.
|
|
||||||
* 2. If there's an .SM, use it with a changed extension.
|
|
||||||
* 3. If there's a .DWI, use it with a changed extension.
|
|
||||||
* 4. Otherwise, use the name of the directory, since it's definitely a valid
|
|
||||||
* filename, and should always be the title of the song (unlike KSFs). */
|
|
||||||
m_sSongFileName = m_sSongDir;
|
|
||||||
vector<RString> asFileNames;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
GetDirListing( m_sSongDir+"*.ssc", asFileNames );
|
|
||||||
if( !asFileNames.empty() )
|
|
||||||
{
|
|
||||||
m_sSongFileName += asFileNames[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetDirListing( m_sSongDir+"*.sma", asFileNames );
|
|
||||||
if (!asFileNames.empty() )
|
|
||||||
{
|
|
||||||
m_sSongFileName += SetExtension( asFileNames[0], "ssc" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetDirListing( m_sSongDir+"*.sm", asFileNames );
|
|
||||||
if( !asFileNames.empty() )
|
|
||||||
{
|
|
||||||
m_sSongFileName += SetExtension( asFileNames[0], "ssc" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetDirListing( m_sSongDir+"*.dwi", asFileNames );
|
|
||||||
if( !asFileNames.empty() ) {
|
|
||||||
m_sSongFileName += SetExtension( asFileNames[0], "ssc" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m_sSongFileName += Basename(m_sSongDir);
|
|
||||||
m_sSongFileName += ".ssc";
|
|
||||||
} while(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Song::TranslateTitles()
|
void Song::TranslateTitles()
|
||||||
@@ -863,9 +807,9 @@ void Song::TranslateTitles()
|
|||||||
m_sMainTitleTranslit, m_sSubTitleTranslit, m_sArtistTranslit );
|
m_sMainTitleTranslit, m_sSubTitleTranslit, m_sArtistTranslit );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Song::ReCalculateRadarValuesAndLastSecond( bool bFromCache )
|
void Song::ReCalculateRadarValuesAndLastSecond(bool fromCache, bool duringCache)
|
||||||
{
|
{
|
||||||
if( bFromCache && this->GetFirstSecond() >= 0 && this->GetLastSecond() > 0 )
|
if( fromCache && this->GetFirstSecond() >= 0 && this->GetLastSecond() > 0 )
|
||||||
{
|
{
|
||||||
// this is loaded from cache, then we just have to calculate the radar values.
|
// this is loaded from cache, then we just have to calculate the radar values.
|
||||||
for( unsigned i=0; i<m_vpSteps.size(); i++ )
|
for( unsigned i=0; i<m_vpSteps.size(); i++ )
|
||||||
@@ -883,36 +827,42 @@ void Song::ReCalculateRadarValuesAndLastSecond( bool bFromCache )
|
|||||||
|
|
||||||
pSteps->CalculateRadarValues( m_fMusicLengthSeconds );
|
pSteps->CalculateRadarValues( m_fMusicLengthSeconds );
|
||||||
|
|
||||||
|
// Must initialize before the gotos.
|
||||||
|
NoteData tempNoteData;
|
||||||
|
pSteps->GetNoteData( tempNoteData );
|
||||||
|
|
||||||
// calculate lastSecond
|
// calculate lastSecond
|
||||||
|
|
||||||
// If it's autogen, then first/last beat will come from the parent.
|
// If it's autogen, then first/last beat will come from the parent.
|
||||||
if( pSteps->IsAutogen() )
|
if( pSteps->IsAutogen() )
|
||||||
continue;
|
goto wipe_notedata;
|
||||||
|
|
||||||
/* Don't calculate with edits unless the song only contains an edit
|
/* Don't calculate with edits unless the song only contains an edit
|
||||||
* chart, like those in Mungyodance 3. Otherwise, edits installed on
|
* chart, like those in Mungyodance 3. Otherwise, edits installed on
|
||||||
* the machine could extend the length of the song. */
|
* the machine could extend the length of the song. */
|
||||||
if( pSteps->IsAnEdit() && m_vpSteps.size() > 1 )
|
if( pSteps->IsAnEdit() && m_vpSteps.size() > 1 )
|
||||||
continue;
|
goto wipe_notedata;
|
||||||
|
|
||||||
// Don't set first/last beat based on lights. They often start very
|
// Don't set first/last beat based on lights. They often start very
|
||||||
// early and end very late.
|
// early and end very late.
|
||||||
if( pSteps->m_StepsType == StepsType_lights_cabinet )
|
if( pSteps->m_StepsType == StepsType_lights_cabinet )
|
||||||
continue;
|
continue; // no need to wipe this.
|
||||||
|
|
||||||
NoteData tempNoteData;
|
|
||||||
pSteps->GetNoteData( tempNoteData );
|
|
||||||
|
|
||||||
/* Many songs have stray, empty song patterns. Ignore them, so they
|
/* Many songs have stray, empty song patterns. Ignore them, so they
|
||||||
* don't force the first beat of the whole song to 0. */
|
* don't force the first beat of the whole song to 0. */
|
||||||
if( tempNoteData.GetLastRow() == 0 )
|
if( tempNoteData.GetLastRow() != 0 )
|
||||||
continue;
|
{
|
||||||
|
|
||||||
localFirst = min(localFirst,
|
localFirst = min(localFirst,
|
||||||
pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat()));
|
pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat()));
|
||||||
localLast = max(localLast,
|
localLast = max(localLast,
|
||||||
pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat()));
|
pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat()));
|
||||||
}
|
}
|
||||||
|
wipe_notedata:
|
||||||
|
if (duringCache)
|
||||||
|
{
|
||||||
|
// update this when at that point.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->firstSecond = localFirst;
|
this->firstSecond = localFirst;
|
||||||
this->lastSecond = localLast;
|
this->lastSecond = localLast;
|
||||||
@@ -1005,11 +955,15 @@ bool Song::SaveToSMFile()
|
|||||||
|
|
||||||
bool Song::SaveToSSCFile( RString sPath, bool bSavingCache )
|
bool Song::SaveToSSCFile( RString sPath, bool bSavingCache )
|
||||||
{
|
{
|
||||||
LOG->Trace( "Song::SaveToSSCFile('%s')", sPath.c_str() );
|
RString path = sPath;
|
||||||
|
if (!bSavingCache)
|
||||||
|
path = SetExtension(sPath, "ssc");
|
||||||
|
|
||||||
|
LOG->Trace( "Song::SaveToSSCFile('%s')", path.c_str() );
|
||||||
|
|
||||||
// If the file exists, make a backup.
|
// If the file exists, make a backup.
|
||||||
if( !bSavingCache && IsAFile(sPath) )
|
if( !bSavingCache && IsAFile(path) )
|
||||||
FileCopy( sPath, sPath + ".old" );
|
FileCopy( path, path + ".old" );
|
||||||
|
|
||||||
vector<Steps*> vpStepsToSave;
|
vector<Steps*> vpStepsToSave;
|
||||||
FOREACH_CONST( Steps*, m_vpSteps, s )
|
FOREACH_CONST( Steps*, m_vpSteps, s )
|
||||||
@@ -1022,16 +976,23 @@ bool Song::SaveToSSCFile( RString sPath, bool bSavingCache )
|
|||||||
if( pSteps->WasLoadedFromProfile() )
|
if( pSteps->WasLoadedFromProfile() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!bSavingCache)
|
||||||
|
pSteps->SetFilename(path);
|
||||||
vpStepsToSave.push_back( pSteps );
|
vpStepsToSave.push_back( pSteps );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !NotesWriterSSC::Write(sPath, *this, vpStepsToSave, bSavingCache) )
|
if (bSavingCache)
|
||||||
|
{
|
||||||
|
return NotesWriterSSC::Write(path, *this, vpStepsToSave, bSavingCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !NotesWriterSSC::Write(path, *this, vpStepsToSave, bSavingCache) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if( !bSavingCache && g_BackUpAllSongSaves.Get() )
|
if( g_BackUpAllSongSaves.Get() )
|
||||||
{
|
{
|
||||||
RString sExt = GetExtension( sPath );
|
RString sExt = GetExtension( path );
|
||||||
RString sBackupFile = SetExtension( sPath, "" );
|
RString sBackupFile = SetExtension( path, "" );
|
||||||
|
|
||||||
time_t cur_time;
|
time_t cur_time;
|
||||||
time( &cur_time );
|
time( &cur_time );
|
||||||
@@ -1043,18 +1004,15 @@ bool Song::SaveToSSCFile( RString sPath, bool bSavingCache )
|
|||||||
sBackupFile = SetExtension( sBackupFile, sExt );
|
sBackupFile = SetExtension( sBackupFile, sExt );
|
||||||
sBackupFile += ssprintf( ".old" );
|
sBackupFile += ssprintf( ".old" );
|
||||||
|
|
||||||
if( FileCopy(sPath, sBackupFile) )
|
if( FileCopy(path, sBackupFile) )
|
||||||
LOG->Trace( "Backed up %s to %s", sPath.c_str(), sBackupFile.c_str() );
|
LOG->Trace( "Backed up %s to %s", path.c_str(), sBackupFile.c_str() );
|
||||||
else
|
else
|
||||||
LOG->Trace( "Failed to back up %s to %s", sPath.c_str(), sBackupFile.c_str() );
|
LOG->Trace( "Failed to back up %s to %s", path.c_str(), sBackupFile.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !bSavingCache )
|
|
||||||
{
|
|
||||||
// Mark these steps saved to disk.
|
// Mark these steps saved to disk.
|
||||||
FOREACH( Steps*, vpStepsToSave, s )
|
FOREACH( Steps*, vpStepsToSave, s )
|
||||||
(*s)->SetSavedToDisk( true );
|
(*s)->SetSavedToDisk( true );
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1063,13 +1021,7 @@ bool Song::SaveToCacheFile()
|
|||||||
{
|
{
|
||||||
SONGINDEX->AddCacheIndex(m_sSongDir, GetHashForDirectory(m_sSongDir));
|
SONGINDEX->AddCacheIndex(m_sSongDir, GetHashForDirectory(m_sSongDir));
|
||||||
const RString sPath = GetCacheFilePath();
|
const RString sPath = GetCacheFilePath();
|
||||||
if( !SaveToSSCFile(sPath, true) )
|
return SaveToSSCFile(sPath, true);
|
||||||
return false;
|
|
||||||
|
|
||||||
FOREACH( Steps*, m_vpSteps, pSteps )
|
|
||||||
(*pSteps)->SetFilename( sPath );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Song::SaveToDWIFile()
|
bool Song::SaveToDWIFile()
|
||||||
@@ -1295,6 +1247,45 @@ vector<BackgroundChange> &Song::GetForegroundChanges()
|
|||||||
return *m_ForegroundChanges.Get();
|
return *m_ForegroundChanges.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<RString> Song::GetChangesToVectorString(const vector<BackgroundChange> & changes) const
|
||||||
|
{
|
||||||
|
vector<RString> ret;
|
||||||
|
FOREACH_CONST( BackgroundChange, changes, bgc )
|
||||||
|
{
|
||||||
|
ret.push_back((*bgc).ToString());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<RString> Song::GetBGChanges1ToVectorString() const
|
||||||
|
{
|
||||||
|
return this->GetChangesToVectorString(this->GetBackgroundChanges(BACKGROUND_LAYER_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<RString> Song::GetBGChanges2ToVectorString() const
|
||||||
|
{
|
||||||
|
return this->GetChangesToVectorString(this->GetBackgroundChanges(BACKGROUND_LAYER_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<RString> Song::GetFGChanges1ToVectorString() const
|
||||||
|
{
|
||||||
|
return this->GetChangesToVectorString(this->GetForegroundChanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<RString> Song::GetInstrumentTracksToVectorString() const
|
||||||
|
{
|
||||||
|
vector<RString> ret;
|
||||||
|
FOREACH_ENUM(InstrumentTrack, it)
|
||||||
|
{
|
||||||
|
if (this->HasInstrumentTrack(it))
|
||||||
|
{
|
||||||
|
ret.push_back(InstrumentTrackToString(it)
|
||||||
|
+ "="
|
||||||
|
+ this->m_sInstrumentTrackFile[it]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
RString GetSongAssetPath( RString sPath, const RString &sSongPath )
|
RString GetSongAssetPath( RString sPath, const RString &sSongPath )
|
||||||
{
|
{
|
||||||
|
|||||||
+19
-4
@@ -91,14 +91,17 @@ public:
|
|||||||
// This one takes the effort to reuse Steps pointers as best as it can
|
// This one takes the effort to reuse Steps pointers as best as it can
|
||||||
bool ReloadFromSongDir( RString sDir );
|
bool ReloadFromSongDir( RString sDir );
|
||||||
|
|
||||||
/** @brief Call this after loading a song to clean up invalid data. */
|
/**
|
||||||
void TidyUpData( bool bFromCache = false );
|
* @brief Call this after loading a song to clean up invalid data.
|
||||||
|
* @param fromCache was this data loaded from the cache file?
|
||||||
|
* @param duringCache was this data loaded during the cache process? */
|
||||||
|
void TidyUpData( bool fromCache = false, bool duringCache = false );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the new radar values, and determine the last second at the same time.
|
* @brief Get the new radar values, and determine the last second at the same time.
|
||||||
*
|
*
|
||||||
* This is called by TidyUpData, after saving the Song. */
|
* This is called by TidyUpData, after saving the Song. */
|
||||||
void ReCalculateRadarValuesAndLastSecond( bool bFromCache = false );
|
void ReCalculateRadarValuesAndLastSecond(bool fromCache = false, bool duringCache = false);
|
||||||
/**
|
/**
|
||||||
* @brief Translate any titles that aren't in english.
|
* @brief Translate any titles that aren't in english.
|
||||||
*
|
*
|
||||||
@@ -180,7 +183,6 @@ public:
|
|||||||
RString GetDisplaySubTitle() const;
|
RString GetDisplaySubTitle() const;
|
||||||
RString GetDisplayArtist() const;
|
RString GetDisplayArtist() const;
|
||||||
|
|
||||||
// Returns the transliterated titles, if any; otherwise returns the main titles.
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the transliterated title, or the main title if there is no translit.
|
* @brief Retrieve the transliterated title, or the main title if there is no translit.
|
||||||
* @return the proper title. */
|
* @return the proper title. */
|
||||||
@@ -315,12 +317,20 @@ private:
|
|||||||
*
|
*
|
||||||
* This must be sorted before gameplay. */
|
* This must be sorted before gameplay. */
|
||||||
AutoPtrCopyOnWrite<VBackgroundChange> m_ForegroundChanges;
|
AutoPtrCopyOnWrite<VBackgroundChange> m_ForegroundChanges;
|
||||||
|
|
||||||
|
vector<RString> GetChangesToVectorString(const vector<BackgroundChange> & changes) const;
|
||||||
public:
|
public:
|
||||||
const vector<BackgroundChange> &GetBackgroundChanges( BackgroundLayer bl ) const;
|
const vector<BackgroundChange> &GetBackgroundChanges( BackgroundLayer bl ) const;
|
||||||
vector<BackgroundChange> &GetBackgroundChanges( BackgroundLayer bl );
|
vector<BackgroundChange> &GetBackgroundChanges( BackgroundLayer bl );
|
||||||
const vector<BackgroundChange> &GetForegroundChanges() const;
|
const vector<BackgroundChange> &GetForegroundChanges() const;
|
||||||
vector<BackgroundChange> &GetForegroundChanges();
|
vector<BackgroundChange> &GetForegroundChanges();
|
||||||
|
|
||||||
|
vector<RString> GetBGChanges1ToVectorString() const;
|
||||||
|
vector<RString> GetBGChanges2ToVectorString() const;
|
||||||
|
vector<RString> GetFGChanges1ToVectorString() const;
|
||||||
|
|
||||||
|
vector<RString> GetInstrumentTracksToVectorString() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The list of LyricSegments.
|
* @brief The list of LyricSegments.
|
||||||
*
|
*
|
||||||
@@ -426,6 +436,11 @@ public:
|
|||||||
|
|
||||||
CachedObject<Song> m_CachedObject;
|
CachedObject<Song> m_CachedObject;
|
||||||
|
|
||||||
|
RString GetAttackString() const
|
||||||
|
{
|
||||||
|
return join(":", this->m_sAttackString);
|
||||||
|
}
|
||||||
|
|
||||||
// Lua
|
// Lua
|
||||||
void PushSelf( lua_State *L );
|
void PushSelf( lua_State *L );
|
||||||
|
|
||||||
|
|||||||
@@ -213,6 +213,11 @@ public:
|
|||||||
|
|
||||||
void GetDisplayBpms( DisplayBpms &addTo) const;
|
void GetDisplayBpms( DisplayBpms &addTo) const;
|
||||||
|
|
||||||
|
RString GetAttackString() const
|
||||||
|
{
|
||||||
|
return join(":", this->m_sAttackString);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline const Steps *Real() const { return parent ? parent : this; }
|
inline const Steps *Real() const { return parent ? parent : this; }
|
||||||
void DeAutogen( bool bCopyNoteData = true ); /* If this Steps is autogenerated, make it a real Steps. */
|
void DeAutogen( bool bCopyNoteData = true ); /* If this Steps is autogenerated, make it a real Steps. */
|
||||||
|
|||||||
Reference in New Issue
Block a user