add multiple background layers

This commit is contained in:
Chris Danford
2005-05-26 09:35:57 +00:00
parent 87183c0551
commit 68a82faa6a
10 changed files with 275 additions and 182 deletions
+108 -74
View File
@@ -53,13 +53,17 @@ static RageColor GetBrightnessColor( float fBrightnessPercent )
}
Background::Background()
{
m_pDancingCharacters = NULL;
m_bInitted = false;
}
Background::Layer::Layer()
{
m_iCurBGChangeIndex = -1;
m_pCurrentBGA = NULL;
m_pFadingBGA = NULL;
m_fSecsLeftInFade = 0;
m_pDancingCharacters = NULL;
m_bInitted = false;
}
void Background::Init()
@@ -114,6 +118,14 @@ Background::~Background()
}
void Background::Unload()
{
for( int i=0; i<NUM_BACKGROUND_LAYERS; i++ )
m_Layer[i].Unload();
m_pSong = NULL;
m_fLastMusicSeconds = -9999;
}
void Background::Layer::Unload()
{
for( map<CString,Actor*>::iterator iter = m_BGAnimations.begin();
iter != m_BGAnimations.end();
@@ -125,10 +137,8 @@ void Background::Unload()
m_pCurrentBGA = NULL;
m_pFadingBGA = NULL;
m_pSong = NULL;
m_fSecsLeftInFade = 0;
m_iCurBGChangeIndex = -1;
m_fLastMusicSeconds = -9999;
}
Actor *MakeVisualization( const CString &sVisPath )
@@ -163,7 +173,7 @@ Actor *MakeMovie( const CString &sMoviePath )
return pSprite;
}
Actor *Background::CreateSongBGA( CString sBGName ) const
Actor *Background::Layer::CreateSongBGA( const Song *pSong, CString sBGName ) const
{
BGAnimation *pTempBGA;
@@ -173,7 +183,7 @@ Actor *Background::CreateSongBGA( CString sBGName ) const
CStringArray asFiles;
// Look for BGAnims in the song dir
GetDirListing( m_pSong->GetSongDir()+sBGName, asFiles, true, true );
GetDirListing( pSong->GetSongDir()+sBGName, asFiles, true, true );
if( !asFiles.empty() )
{
/* If default.xml exists, use the regular generic actor load. However,
@@ -187,7 +197,7 @@ Actor *Background::CreateSongBGA( CString sBGName ) const
return pTempBGA;
}
// Look for BG movies or static graphics in the song dir
GetDirListing( m_pSong->GetSongDir()+sBGName, asFiles, false, true );
GetDirListing( pSong->GetSongDir()+sBGName, asFiles, false, true );
if( !asFiles.empty() )
{
const CString sExt = GetExtension( asFiles[0]) ;
@@ -214,9 +224,9 @@ Actor *Background::CreateSongBGA( CString sBGName ) const
GetDirListing( BG_ANIMS_DIR+sBGName, asFiles, true, true );
if( !asFiles.empty() )
{
pTempBGA = new BGAnimation;
pTempBGA->LoadFromAniDir( asFiles[0] );
return pTempBGA;
Actor *pActor = ActorUtil::MakeActor( asFiles[0] );
ASSERT( pActor );
return pActor;
}
// Look for BGAnims in the BGAnims dir
@@ -228,7 +238,7 @@ Actor *Background::CreateSongBGA( CString sBGName ) const
return NULL;
}
CString Background::CreateRandomBGA( CString sPreferredSubDir )
CString Background::Layer::CreateRandomBGA( const Song *pSong, CString sPreferredSubDir )
{
if( sPreferredSubDir.Right(1) != "/" )
sPreferredSubDir += '/';
@@ -338,9 +348,9 @@ void Background::LoadFromRandom( float fFirstBeat, float fLastBeat, const Timing
//bool bFade = PREFSMAN->m_BackgroundMode==PrefsManager::BGMODE_RANDOMMOVIES ||
// PREFSMAN->m_BackgroundMode==PrefsManager::BGMODE_MOVIEVIS;
CString sBGName = CreateRandomBGA( sPreferredSubDir );
CString sBGName = m_Layer[0].CreateRandomBGA( m_pSong, sPreferredSubDir );
if( sBGName != "" )
m_aBGChanges.push_back( BackgroundChange(f,sBGName,1.f,bFade) );
m_Layer[0].m_aBGChanges.push_back( BackgroundChange(f,sBGName,1.f,bFade) );
}
// change BG every BPM change that is at the beginning of a measure
@@ -356,9 +366,9 @@ void Background::LoadFromRandom( float fFirstBeat, float fLastBeat, const Timing
if( bpmseg.m_iStartIndex < iStartIndex || bpmseg.m_iStartIndex > iEndIndex )
continue; // skip
CString sBGName = CreateRandomBGA( sPreferredSubDir );
CString sBGName = m_Layer[0].CreateRandomBGA( m_pSong, sPreferredSubDir );
if( sBGName != "" )
m_aBGChanges.push_back( BackgroundChange(NoteRowToBeat(bpmseg.m_iStartIndex),sBGName) );
m_Layer[0].m_aBGChanges.push_back( BackgroundChange(NoteRowToBeat(bpmseg.m_iStartIndex),sBGName) );
}
}
@@ -387,53 +397,68 @@ void Background::LoadFromSong( const Song* pSong )
if( pSong->HasBGChanges() )
{
// Load all song-specified backgrounds
for( unsigned i=0; i<pSong->m_BackgroundChanges.size(); i++ )
for( int i=0; i<NUM_BACKGROUND_LAYERS; i++ )
{
BackgroundChange change = pSong->m_BackgroundChanges[i];
CString &sBGName = change.m_sBGName;
bool bIsAlreadyLoaded = m_BGAnimations.find(sBGName) != m_BGAnimations.end();
Layer &layer = m_Layer[i];
if( sBGName.CompareNoCase("-random-")!=0 && !bIsAlreadyLoaded )
// Load all song-specified backgrounds
FOREACH_CONST( BackgroundChange, pSong->m_BackgroundChanges[i], bgc )
{
Actor *pTempBGA = CreateSongBGA( sBGName );
if( pTempBGA )
BackgroundChange change = *bgc;
CString &sBGName = change.m_sBGName;
bool bIsAlreadyLoaded = layer.m_BGAnimations.find(sBGName) != layer.m_BGAnimations.end();
if( sBGName.CompareNoCase("-random-")!=0 && !bIsAlreadyLoaded )
{
m_BGAnimations[sBGName] = pTempBGA;
}
else // the background was not found. Use a random one instead
{
sBGName = CreateRandomBGA( pSong->m_sGroupName );
if( sBGName == "" )
sBGName = STATIC_BACKGROUND;
Actor *pTempBGA = layer.CreateSongBGA( m_pSong, sBGName );
if( pTempBGA )
{
layer.m_BGAnimations[sBGName] = pTempBGA;
}
else // the background was not found. Use a random one instead
{
sBGName = layer.CreateRandomBGA( pSong, pSong->m_sGroupName );
if( sBGName == "" )
sBGName = STATIC_BACKGROUND;
}
}
layer.m_aBGChanges.push_back( change );
}
m_aBGChanges.push_back( change );
}
}
else // pSong doesn't have an animation plan
{
Layer &layer = m_Layer[0];
LoadFromRandom( pSong->m_fFirstBeat, pSong->m_fLastBeat, pSong->m_Timing, pSong->m_sGroupName );
// end showing the static song background
m_aBGChanges.push_back( BackgroundChange(pSong->m_fLastBeat,STATIC_BACKGROUND) );
layer.m_aBGChanges.push_back( BackgroundChange(pSong->m_fLastBeat,STATIC_BACKGROUND) );
}
// sort segments
SortBackgroundChangesArray( m_aBGChanges );
for( int i=0; i<NUM_BACKGROUND_LAYERS; i++ )
{
Layer &layer = m_Layer[i];
SortBackgroundChangesArray( layer.m_aBGChanges );
}
Layer &mainlayer = m_Layer[0];
/* If the first BGAnimation isn't negative, add a lead-in image showing the song
* background. */
if( m_aBGChanges.empty() || m_aBGChanges.front().m_fStartBeat >= 0 )
m_aBGChanges.insert( m_aBGChanges.begin(), BackgroundChange(-10000,STATIC_BACKGROUND) );
if( mainlayer.m_aBGChanges.empty() || mainlayer.m_aBGChanges.front().m_fStartBeat >= 0 )
mainlayer.m_aBGChanges.insert( mainlayer.m_aBGChanges.begin(), BackgroundChange(-10000,STATIC_BACKGROUND) );
// If any BGChanges use the background image, load it.
bool bStaticBackgroundUsed = false;
for( unsigned i=0; i<m_aBGChanges.size(); i++ )
if( m_aBGChanges[i].m_sBGName == STATIC_BACKGROUND )
for( unsigned i=0; i<mainlayer.m_aBGChanges.size(); i++ )
if( mainlayer.m_aBGChanges[i].m_sBGName == STATIC_BACKGROUND )
bStaticBackgroundUsed = true;
if( bStaticBackgroundUsed )
{
@@ -442,33 +467,33 @@ void Background::LoadFromSong( const Song* pSong )
Sprite* pSprite = new Sprite;
pSprite->LoadBG( sSongBGPath );
pSprite->StretchTo( FullScreenRectF );
m_BGAnimations[STATIC_BACKGROUND] = pSprite;
mainlayer.m_BGAnimations[STATIC_BACKGROUND] = pSprite;
}
// Look for the filename "Random", and replace the segment with LoadFromRandom.
for( unsigned i=0; i<m_aBGChanges.size(); i++ )
for( unsigned i=0; i<mainlayer.m_aBGChanges.size(); i++ )
{
BackgroundChange &change = m_aBGChanges[i];
BackgroundChange &change = mainlayer.m_aBGChanges[i];
if( change.m_sBGName.CompareNoCase("-random-") )
continue;
const float fStartBeat = change.m_fStartBeat;
const float fLastBeat = (i+1 < m_aBGChanges.size())? m_aBGChanges[i+1].m_fStartBeat: FLT_MAX;
const float fLastBeat = (i+1 < mainlayer.m_aBGChanges.size())? mainlayer.m_aBGChanges[i+1].m_fStartBeat: FLT_MAX;
m_aBGChanges.erase( m_aBGChanges.begin()+i );
mainlayer.m_aBGChanges.erase( mainlayer.m_aBGChanges.begin()+i );
--i;
LoadFromRandom( fStartBeat, fLastBeat, pSong->m_Timing, pSong->m_sGroupName );
}
// At this point, we shouldn't have any BGChanges to "". "" is an invalid name.
for( unsigned i=0; i<m_aBGChanges.size(); i++ )
ASSERT( !m_aBGChanges[i].m_sBGName.empty() );
for( unsigned i=0; i<mainlayer.m_aBGChanges.size(); i++ )
ASSERT( !mainlayer.m_aBGChanges[i].m_sBGName.empty() );
// Re-sort.
SortBackgroundChangesArray( m_aBGChanges );
SortBackgroundChangesArray( mainlayer.m_aBGChanges );
m_DangerAll.SetXY( (float)LEFT_EDGE, (float)TOP_EDGE );
m_DangerAll.SetZoomX( fXZoom );
@@ -501,7 +526,7 @@ void Background::LoadFromSong( const Song* pSong )
* the clock back with "effectclock,timer" in your OnCommand. Note that at this
* point, we havn't run the OnCommand yet, so it'll override correctly. */
map<CString,Actor*>::iterator it;
for( it = m_BGAnimations.begin(); it != m_BGAnimations.end(); ++it )
for( it = mainlayer.m_BGAnimations.begin(); it != mainlayer.m_BGAnimations.end(); ++it )
{
Actor *pBGA = it->second;
@@ -514,7 +539,7 @@ void Background::LoadFromSong( const Song* pSong )
}
}
int Background::FindBGSegmentForBeat( float fBeat ) const
int Background::Layer::FindBGSegmentForBeat( float fBeat ) const
{
if( m_aBGChanges.empty() )
return -1;
@@ -533,7 +558,7 @@ int Background::FindBGSegmentForBeat( float fBeat ) const
}
/* If the BG segment has changed, move focus to it. Send Update() calls. */
void Background::UpdateCurBGChange( float fCurrentTime )
void Background::Layer::UpdateCurBGChange( const Song *pSong, float fLastMusicSeconds, float fCurrentTime )
{
ASSERT( fCurrentTime != GameState::MUSIC_SECONDS_INVALID );
@@ -542,7 +567,7 @@ void Background::UpdateCurBGChange( float fCurrentTime )
float fBeat, fBPS;
bool bFreeze;
m_pSong->m_Timing.GetBeatAndBPSFromElapsedTime( fCurrentTime, fBeat, fBPS, bFreeze );
pSong->m_Timing.GetBeatAndBPSFromElapsedTime( fCurrentTime, fBeat, fBPS, bFreeze );
/* Calls to Update() should *not* be scaled by music rate; fCurrentTime is. Undo it. */
const float fRate = GAMESTATE->m_SongOptions.m_fMusicRate;
@@ -582,7 +607,7 @@ void Background::UpdateCurBGChange( float fCurrentTime )
m_fSecsLeftInFade = m_pFadingBGA!=NULL ? FADE_SECONDS : 0;
/* How much time of this BGA have we skipped? (This happens with SetSeconds.) */
const float fStartSecond = m_pSong->m_Timing.GetElapsedTimeFromBeat( change.m_fStartBeat );
const float fStartSecond = pSong->m_Timing.GetElapsedTimeFromBeat( change.m_fStartBeat );
/* This is affected by the music rate. */
float fDeltaTime = fCurrentTime - fStartSecond;
@@ -593,17 +618,16 @@ void Background::UpdateCurBGChange( float fCurrentTime )
else // we're not changing backgrounds
{
/* This is affected by the music rate. */
float fDeltaTime = fCurrentTime - m_fLastMusicSeconds;
float fDeltaTime = fCurrentTime - fLastMusicSeconds;
fDeltaTime /= fRate;
if( m_pCurrentBGA )
m_pCurrentBGA->Update( max( fDeltaTime, 0 ) );
}
float fDeltaTime = fCurrentTime - m_fLastMusicSeconds;
float fDeltaTime = fCurrentTime - fLastMusicSeconds;
fDeltaTime /= fRate;
if( m_pFadingBGA )
m_pFadingBGA->Update( max( fCurrentTime - m_fLastMusicSeconds, 0 ) );
m_fLastMusicSeconds = fCurrentTime;
m_pFadingBGA->Update( max( fCurrentTime - fLastMusicSeconds, 0 ) );
}
void Background::Update( float fDeltaTime )
@@ -624,28 +648,33 @@ void Background::Update( float fDeltaTime )
m_DeadPlayer[p].Update( fDeltaTime );
}
if( m_pDancingCharacters )
m_pDancingCharacters->Update( fDeltaTime );
/* Always update the current background, even when m_DangerAll is being displayed.
* Otherwise, we'll stop updating movies during danger (which may stop them from
* playing), and we won't start clips at the right time, which will throw backgrounds
* off sync. */
UpdateCurBGChange( GAMESTATE->m_fMusicSeconds );
if( m_pFadingBGA )
for( int i=0; i<NUM_BACKGROUND_LAYERS; i++ )
{
m_pFadingBGA->Update( fDeltaTime );
m_fSecsLeftInFade -= fDeltaTime;
float fPercentOpaque = m_fSecsLeftInFade / FADE_SECONDS;
m_pFadingBGA->SetDiffuse( RageColor(1,1,1,fPercentOpaque) );
if( fPercentOpaque <= 0 )
Layer &layer = m_Layer[i];
layer.UpdateCurBGChange( m_pSong, m_fLastMusicSeconds, GAMESTATE->m_fMusicSeconds );
if( layer.m_pFadingBGA )
{
/* Reset its diffuse color, in case we reuse it. */
m_pFadingBGA->SetDiffuse( RageColor(1,1,1,1) );
m_pFadingBGA = NULL;
layer.m_pFadingBGA->Update( fDeltaTime );
layer.m_fSecsLeftInFade -= fDeltaTime;
float fPercentOpaque = layer.m_fSecsLeftInFade / FADE_SECONDS;
layer.m_pFadingBGA->SetDiffuse( RageColor(1,1,1,fPercentOpaque) );
if( fPercentOpaque <= 0 )
{
/* Reset its diffuse color, in case we reuse it. */
layer.m_pFadingBGA->SetDiffuse( RageColor(1,1,1,1) );
layer.m_pFadingBGA = NULL;
}
}
}
if( m_pDancingCharacters )
m_pDancingCharacters->Update( fDeltaTime );
m_fLastMusicSeconds = GAMESTATE->m_fMusicSeconds;
}
void Background::DrawPrimitives()
@@ -665,10 +694,15 @@ void Background::DrawPrimitives()
{
if( m_pDancingCharacters )
m_pDancingCharacters->m_bDrawDangerLight = false;
if( m_pCurrentBGA )
m_pCurrentBGA->Draw();
if( m_pFadingBGA )
m_pFadingBGA->Draw();
for( int i=0; i<NUM_BACKGROUND_LAYERS; i++ )
{
Layer &layer = m_Layer[i];
if( layer.m_pCurrentBGA )
layer.m_pCurrentBGA->Draw();
if( layer.m_pFadingBGA )
layer.m_pFadingBGA->Draw();
}
FOREACH_PlayerNumber( p )
{
+23 -14
View File
@@ -49,30 +49,39 @@ public:
protected:
const Song *m_pSong;
void LoadFromRandom( float fFirstBeat, float fLastBeat, const TimingData &timing, CString sPreferredSubDir );
int FindBGSegmentForBeat( float fBeat ) const;
bool IsDangerAllVisible();
void UpdateCurBGChange( float fCurrentTime );
bool m_bInitted;
DancingCharacters* m_pDancingCharacters;
class Layer
{
public:
Layer();
void Unload();
Actor *CreateSongBGA( const Song *pSong, CString sBGName ) const;
CString CreateRandomBGA( const Song *pSong, CString sPreferredSubDir );
int FindBGSegmentForBeat( float fBeat ) const;
void UpdateCurBGChange( const Song *pSong, float fLastMusicSeconds, float fCurrentTime );
map<CString,Actor*> m_BGAnimations;
deque<CString> m_RandomBGAnimations;
vector<BackgroundChange> m_aBGChanges;
int m_iCurBGChangeIndex;
Actor *m_pCurrentBGA;
Actor *m_pFadingBGA;
float m_fSecsLeftInFade;
};
Layer m_Layer[NUM_BACKGROUND_LAYERS];
float m_fLastMusicSeconds;
BGAnimation m_DangerPlayer[NUM_PLAYERS];
BGAnimation m_DangerAll;
BGAnimation m_DeadPlayer[NUM_PLAYERS];
Actor *CreateSongBGA( CString sBGName ) const;
CString CreateRandomBGA( CString sPreferredSubDir );
map<CString,Actor*> m_BGAnimations;
deque<CString> m_RandomBGAnimations;
vector<BackgroundChange> m_aBGChanges;
int m_iCurBGChangeIndex;
Actor *m_pCurrentBGA;
Actor *m_pFadingBGA;
float m_fSecsLeftInFade;
float m_fLastMusicSeconds;
// cover up the edge of animations that might hang outside of the background rectangle
Quad m_quadBorderLeft, m_quadBorderTop, m_quadBorderRight, m_quadBorderBottom;
+17 -13
View File
@@ -531,22 +531,26 @@ void NoteField::DrawPrimitives()
break;
case EDIT_MODE_FULL:
{
vector<BackgroundChange> &aBackgroundChanges = GAMESTATE->m_pCurSong->m_BackgroundChanges;
for( unsigned i=0; i<aBackgroundChanges.size(); i++ )
for( int b=0; b<NUM_BACKGROUND_LAYERS; b++ )
{
if(aBackgroundChanges[i].m_fStartBeat >= fFirstBeatToDraw &&
aBackgroundChanges[i].m_fStartBeat <= fLastBeatToDraw)
vector<BackgroundChange> &aBackgroundChanges = GAMESTATE->m_pCurSong->m_BackgroundChanges[b];
for( unsigned i=0; i<aBackgroundChanges.size(); i++ )
{
const BackgroundChange& change = aBackgroundChanges[i];
CString sChangeText = ssprintf("%s\n%.0f%%%s%s%s",
change.m_sBGName.c_str(),
change.m_fRate*100,
change.m_bFadeLast ? " Fade" : "",
change.m_bRewindMovie ? " Rewind" : "",
change.m_bLoop ? " Loop" : "" );
if( aBackgroundChanges[i].m_fStartBeat >= fFirstBeatToDraw &&
aBackgroundChanges[i].m_fStartBeat <= fLastBeatToDraw )
{
const BackgroundChange& change = aBackgroundChanges[i];
CString sChangeText = ssprintf("%s%s\n%.0f%%%s%s%s",
((b!=0) ? ssprintf("%d:",b) : CString()).c_str(),
change.m_sBGName.c_str(),
change.m_fRate*100,
change.m_bFadeLast ? " Fade" : "",
change.m_bRewindMovie ? " Rewind" : "",
change.m_bLoop ? " Loop" : "" );
if( IS_ON_SCREEN(change.m_fStartBeat) )
DrawBGChangeText( change.m_fStartBeat, sChangeText );
if( IS_ON_SCREEN(change.m_fStartBeat) )
DrawBGChangeText( change.m_fStartBeat, sChangeText );
}
}
}
}
+60 -45
View File
@@ -87,11 +87,14 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
for( unsigned i=0; i<msd.GetNumValues(); i++ )
{
const MsdFile::value_t &sParams = msd.GetValue(i);
const CString sValueName = sParams[0];
CString sValueName = sParams[0];
sValueName.MakeUpper();
if( 0==stricmp(sValueName,"OFFSET") )
if( sValueName=="OFFSET" )
{
out.m_fBeat0OffsetInSeconds = strtof( sParams[1], NULL );
else if( 0==stricmp(sValueName,"STOPS") || 0==stricmp(sValueName,"FREEZES") )
}
else if( sValueName=="STOPS" || sValueName=="FREEZES" )
{
CStringArray arrayFreezeExpressions;
split( sParams[1], ",", arrayFreezeExpressions );
@@ -122,7 +125,7 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
}
}
else if( 0==stricmp(sValueName,"BPMS") )
else if( sValueName=="BPMS" )
{
CStringArray arrayBPMChangeExpressions;
split( sParams[1], ",", arrayBPMChangeExpressions );
@@ -194,99 +197,100 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
{
int iNumParams = msd.GetNumParams(i);
const MsdFile::value_t &sParams = msd.GetValue(i);
const CString sValueName = sParams[0];
CString sValueName = sParams[0];
sValueName.MakeUpper();
// handle the data
/* Don't use GetMainAndSubTitlesFromFullTitle; that's only for heuristically
* splitting other formats that *don't* natively support #SUBTITLE. */
if( 0==stricmp(sValueName,"TITLE") )
if( sValueName=="TITLE" )
out.m_sMainTitle = sParams[1];
else if( 0==stricmp(sValueName,"SUBTITLE") )
else if( sValueName=="SUBTITLE" )
out.m_sSubTitle = sParams[1];
else if( 0==stricmp(sValueName,"ARTIST") )
else if( sValueName=="ARTIST" )
out.m_sArtist = sParams[1];
else if( 0==stricmp(sValueName,"TITLETRANSLIT") )
else if( sValueName=="TITLETRANSLIT" )
out.m_sMainTitleTranslit = sParams[1];
else if( 0==stricmp(sValueName,"SUBTITLETRANSLIT") )
else if( sValueName=="SUBTITLETRANSLIT" )
out.m_sSubTitleTranslit = sParams[1];
else if( 0==stricmp(sValueName,"ARTISTTRANSLIT") )
else if( sValueName=="ARTISTTRANSLIT" )
out.m_sArtistTranslit = sParams[1];
else if( 0==stricmp(sValueName,"GENRE") )
else if( sValueName=="GENRE" )
out.m_sGenre = sParams[1];
else if( 0==stricmp(sValueName,"CREDIT") )
else if( sValueName=="CREDIT" )
out.m_sCredit = sParams[1];
else if( 0==stricmp(sValueName,"BANNER") )
else if( sValueName=="BANNER" )
out.m_sBannerFile = sParams[1];
else if( 0==stricmp(sValueName,"BACKGROUND") )
else if( sValueName=="BACKGROUND" )
out.m_sBackgroundFile = sParams[1];
/* Save "#LYRICS" for later, so we can add an internal lyrics tag. */
else if( 0==stricmp(sValueName,"LYRICSPATH") )
else if( sValueName=="LYRICSPATH" )
out.m_sLyricsFile = sParams[1];
else if( 0==stricmp(sValueName,"CDTITLE") )
else if( sValueName=="CDTITLE" )
out.m_sCDTitleFile = sParams[1];
else if( 0==stricmp(sValueName,"MUSIC") )
else if( sValueName=="MUSIC" )
out.m_sMusicFile = sParams[1];
else if( 0==stricmp(sValueName,"MUSICLENGTH") )
else if( sValueName=="MUSICLENGTH" )
{
if(!FromCache)
continue;
out.m_fMusicLengthSeconds = strtof( sParams[1], NULL );
}
else if( 0==stricmp(sValueName,"MUSICBYTES") )
else if( sValueName=="MUSICBYTES" )
; /* ignore */
/* We calculate these. Some SMs in circulation have bogus values for
* these, so make sure we always calculate it ourself. */
else if( 0==stricmp(sValueName,"FIRSTBEAT") )
else if( sValueName=="FIRSTBEAT" )
{
if(!FromCache)
continue;
out.m_fFirstBeat = strtof( sParams[1], NULL );
}
else if( 0==stricmp(sValueName,"LASTBEAT") )
else if( sValueName=="LASTBEAT" )
{
if(!FromCache)
LOG->Trace("Ignored #LASTBEAT (cache only)");
out.m_fLastBeat = strtof( sParams[1], NULL );
}
else if( 0==stricmp(sValueName,"SONGFILENAME") )
else if( sValueName=="SONGFILENAME" )
{
if( FromCache )
out.m_sSongFileName = sParams[1];
}
else if( 0==stricmp(sValueName,"HASMUSIC") )
else if( sValueName=="HASMUSIC" )
{
if( FromCache )
out.m_bHasMusic = atoi( sParams[1] ) != 0;
}
else if( 0==stricmp(sValueName,"HASBANNER") )
else if( sValueName=="HASBANNER" )
{
if( FromCache )
out.m_bHasBanner = atoi( sParams[1] ) != 0;
}
else if( 0==stricmp(sValueName,"SAMPLESTART") )
else if( sValueName=="SAMPLESTART" )
out.m_fMusicSampleStartSeconds = HHMMSSToSeconds( sParams[1] );
else if( 0==stricmp(sValueName,"SAMPLELENGTH") )
else if( sValueName=="SAMPLELENGTH" )
out.m_fMusicSampleLengthSeconds = HHMMSSToSeconds( sParams[1] );
else if( 0==stricmp(sValueName,"DISPLAYBPM") )
else if( sValueName=="DISPLAYBPM" )
{
// #DISPLAYBPM:[xxx][xxx:xxx]|[*];
if( sParams[1] == "*" )
@@ -302,7 +306,7 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
}
}
else if( 0==stricmp(sValueName,"SELECTABLE") )
else if( sValueName=="SELECTABLE" )
{
if(!stricmp(sParams[1],"YES"))
out.m_SelectionDisplay = out.SHOW_ALWAYS;
@@ -314,20 +318,30 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
LOG->Warn( "The song file '%s' has an unknown #SELECTABLE value, '%s'; ignored.", sPath.c_str(), sParams[1].c_str());
}
else if( 0==stricmp(sValueName,"BGCHANGES") || 0==stricmp(sValueName,"ANIMATIONS") )
else if( sValueName=="BGCHANGES" || sValueName=="ANIMATIONS" )
{
CStringArray aBGChangeExpressions;
split( sParams[1], ",", aBGChangeExpressions );
int iLayer = 0;
sscanf( sValueName, "BGCHANGES%d", &iLayer );
for( unsigned b=0; b<aBGChangeExpressions.size(); b++ )
if( iLayer < 0 && iLayer >= NUM_BACKGROUND_LAYERS )
{
BackgroundChange change;
if( LoadFromBGChangesString( change, aBGChangeExpressions[b] ) )
out.AddBackgroundChange( change );
LOG->Warn( "The song file '%s' has a BGCHANGES tag '%s' that is out of range.", sPath.c_str(), sValueName.c_str() );
}
else
{
CStringArray aBGChangeExpressions;
split( sParams[1], ",", aBGChangeExpressions );
for( unsigned b=0; b<aBGChangeExpressions.size(); b++ )
{
BackgroundChange change;
if( LoadFromBGChangesString( change, aBGChangeExpressions[b] ) )
out.AddBackgroundChange( iLayer, change );
}
}
}
else if( 0==stricmp(sValueName,"FGCHANGES") )
else if( sValueName=="FGCHANGES" )
{
CStringArray aFGChangeExpressions;
split( sParams[1], ",", aFGChangeExpressions );
@@ -340,7 +354,7 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
}
}
else if( 0==stricmp(sValueName,"KEYSOUNDS") )
else if( sValueName=="KEYSOUNDS" )
{
CStringArray aKeysoundFiles;
split( sParams[1], ",", aKeysoundFiles );
@@ -351,7 +365,7 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
}
}
else if( 0==stricmp(sValueName,"NOTES") || 0==stricmp(sValueName,"NOTES2") )
else if( sValueName=="NOTES" || sValueName=="NOTES2" )
{
if( iNumParams < 7 )
{
@@ -371,8 +385,8 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
out.AddSteps( pNewNotes );
}
else if( 0==stricmp(sValueName,"OFFSET") || 0==stricmp(sValueName,"BPMS") ||
0==stricmp(sValueName,"STOPS") || 0==stricmp(sValueName,"FREEZES") )
else if( sValueName=="OFFSET" || sValueName=="BPMS" ||
sValueName=="STOPS" || sValueName=="FREEZES" )
;
else
LOG->Trace( "Unexpected value named '%s'", sValueName.c_str() );
@@ -436,10 +450,11 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, CString sEditFilePath, Profi
{
int iNumParams = msd.GetNumParams(i);
const MsdFile::value_t &sParams = msd.GetValue(i);
const CString sValueName = sParams[0];
CString sValueName = sParams[0];
sValueName.MakeUpper();
// handle the data
if( 0==stricmp(sValueName,"SONG") )
if( sValueName=="SONG" )
{
if( pSong )
{
@@ -464,7 +479,7 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, CString sEditFilePath, Profi
}
}
else if( 0==stricmp(sValueName,"NOTES") )
else if( sValueName=="NOTES" )
{
if( pSong == NULL )
{
@@ -519,7 +534,7 @@ void SMLoader::TidyUpData( Song &song, bool cache )
* have to add an explicit song BG tag if they want it. This is really a
* formatting hack only; nothing outside of SMLoader ever sees "-nosongbg-".
*/
vector<BackgroundChange> &bg = song.m_BackgroundChanges;
vector<BackgroundChange> &bg = song.m_BackgroundChanges[0];
if( !bg.empty() )
{
/* BGChanges have been sorted. On the odd chance that a BGChange exists
+20 -13
View File
@@ -10,6 +10,7 @@
#include "NoteTypes.h"
#include <cstring>
#include <cerrno>
#include "Foreach.h"
void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out )
{
@@ -80,21 +81,27 @@ void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out )
f.Write( "," );
}
f.PutLine( ";" );
f.Write( "#BGCHANGES:" );
for( unsigned i=0; i<out.m_BackgroundChanges.size(); i++ )
{
const BackgroundChange &seg = out.m_BackgroundChanges[i];
f.PutLine( ssprintf( "%.3f=%s=%.3f=%d=%d=%d,", seg.m_fStartBeat, seg.m_sBGName.c_str(), seg.m_fRate, seg.m_bFadeLast, seg.m_bRewindMovie, seg.m_bLoop ) );
for( unsigned b=0; b<NUM_BACKGROUND_LAYERS; b++ )
{
if( b==0 )
f.Write( "#BGCHANGES:" );
else if( out.m_BackgroundChanges[b].empty() )
continue; // skip
else
f.Write( ssprintf("#BGCHANGES%d:", b) );
FOREACH_CONST( BackgroundChange, out.m_BackgroundChanges[b], bgc )
f.PutLine( ssprintf( "%.3f=%s=%.3f=%d=%d=%d,", bgc->m_fStartBeat, bgc->m_sBGName.c_str(), bgc->m_fRate, bgc->m_bFadeLast, bgc->m_bRewindMovie, bgc->m_bLoop ) );
/* If there's an animation plan at all, add a dummy "-nosongbg-" tag to indicate that
* this file doesn't want a song BG entry added at the end. See SMLoader::TidyUpData.
* This tag will be removed on load. Add it at a very high beat, so it won't cause
* problems if loaded in older versions. */
if( b==0 && !out.m_BackgroundChanges[b].empty() )
f.PutLine( "99999=-nosongbg-=1.000=0=0=0 // don't automatically add -songbackground-" );
f.PutLine( ";" );
}
/* If there's an animation plan at all, add a dummy "-nosongbg-" tag to indicate that
* this file doesn't want a song BG entry added at the end. See SMLoader::TidyUpData.
* This tag will be removed on load. Add it at a very high beat, so it won't cause
* problems if loaded in older versions. */
if( !out.m_BackgroundChanges.empty() )
f.PutLine( "99999=-nosongbg-=1.000=0=0=0 // don't automatically add -songbackground-" );
f.PutLine( ";" );
if( out.m_ForegroundChanges.size() )
{
+16 -8
View File
@@ -441,6 +441,7 @@ static Menu g_SongInformation(
static Menu g_BackgroundChange(
"ScreenMiniMenuBackgroundChange",
MenuRow( ScreenEdit::layer, "Layer (applies to new adds)", true, EDIT_MODE_PRACTICE, 0, "0","1" ),
MenuRow( ScreenEdit::rate, "Rate (applies to new adds)", true, EDIT_MODE_PRACTICE, 10, "0%","10%","20%","30%","40%","50%","60%","70%","80%","90%","100%","120%","140%","160%","180%","200%" ),
MenuRow( ScreenEdit::fade_last, "Fade Last (applies to new adds)", true, EDIT_MODE_PRACTICE, 0, "NO","YES" ),
MenuRow( ScreenEdit::rewind_movie, "Rewind Movie (applies to new adds)", true, EDIT_MODE_PRACTICE, 0, "NO","YES" ),
@@ -2073,16 +2074,21 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector<int> &iAns
//
// Fill in line enabled/disabled
// Fill in line's enabled/disabled
//
bool bAlreadyBGChangeHere = false;
int iLayer = 0;
BackgroundChange bgChange;
FOREACH( BackgroundChange, m_pSong->m_BackgroundChanges, bgc )
for( int l=0; l<NUM_BACKGROUND_LAYERS; l++ )
{
if( bgc->m_fStartBeat == GAMESTATE->m_fSongBeat )
FOREACH( BackgroundChange, m_pSong->m_BackgroundChanges[l], bgc )
{
bAlreadyBGChangeHere = true;
bgChange = *bgc;
if( bgc->m_fStartBeat == GAMESTATE->m_fSongBeat )
{
bAlreadyBGChangeHere = true;
iLayer = l;
bgChange = *bgc;
}
}
}
@@ -2097,6 +2103,7 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector<int> &iAns
// set default choices
g_BackgroundChange.rows[layer]. SetDefaultChoiceIfPresent( ssprintf("%d",iLayer) );
g_BackgroundChange.rows[rate]. SetDefaultChoiceIfPresent( ssprintf("%2.0f%%",bgChange.m_fRate*100) );
g_BackgroundChange.rows[fade_last].iDefaultChoice = bgChange.m_bFadeLast ? 1 : 0;
g_BackgroundChange.rows[rewind_movie].iDefaultChoice = bgChange.m_bRewindMovie ? 1 : 0;
@@ -2618,15 +2625,16 @@ void ScreenEdit::HandleSongInformationChoice( SongInformationChoice c, const vec
void ScreenEdit::HandleBGChangeChoice( BGChangeChoice c, const vector<int> &iAnswers )
{
int iLayer = atoi( g_BackgroundChange.rows[layer].choices[iAnswers[layer]] );
BackgroundChange newChange;
FOREACH( BackgroundChange, m_pSong->m_BackgroundChanges, iter )
FOREACH( BackgroundChange, m_pSong->m_BackgroundChanges[iLayer], iter )
{
if( iter->m_fStartBeat == GAMESTATE->m_fSongBeat )
{
newChange = *iter;
// delete the old change. We'll add a new one below.
m_pSong->m_BackgroundChanges.erase( iter );
m_pSong->m_BackgroundChanges[iLayer].erase( iter );
break;
}
}
@@ -2659,7 +2667,7 @@ void ScreenEdit::HandleBGChangeChoice( BGChangeChoice c, const vector<int> &iAns
newChange.m_bLoop = !!iAnswers[loop];
if( newChange.m_sBGName != "" )
m_pSong->AddBackgroundChange( newChange );
m_pSong->AddBackgroundChange( iLayer, newChange );
}
void ScreenEdit::SetupCourseAttacks()
+1
View File
@@ -364,6 +364,7 @@ public:
void HandleSongInformationChoice( SongInformationChoice c, const vector<int> &iAnswers );
enum BGChangeChoice {
layer,
rate,
fade_last,
rewind_movie,
+18 -9
View File
@@ -110,10 +110,11 @@ void Song::Reset()
}
void Song::AddBackgroundChange( BackgroundChange seg )
void Song::AddBackgroundChange( int iLayer, BackgroundChange seg )
{
m_BackgroundChanges.push_back( seg );
SortBackgroundChangesArray( m_BackgroundChanges );
ASSERT( iLayer >= 0 && iLayer < NUM_BACKGROUND_LAYERS );
m_BackgroundChanges[iLayer].push_back( seg );
SortBackgroundChangesArray( m_BackgroundChanges[iLayer] );
}
void Song::AddForegroundChange( BackgroundChange seg )
@@ -143,13 +144,13 @@ void Song::GetDisplayBpms( DisplayBpms &AddTo ) const
}
}
CString Song::GetBackgroundAtBeat( float fBeat ) const
CString Song::GetBackgroundAtBeat( int iLayer, float fBeat ) const
{
unsigned i;
for( i=0; i<m_BackgroundChanges.size()-1; i++ )
if( m_BackgroundChanges[i+1].m_fStartBeat > fBeat )
for( i=0; i<m_BackgroundChanges[iLayer].size()-1; i++ )
if( m_BackgroundChanges[iLayer][i+1].m_fStartBeat > fBeat )
break;
return m_BackgroundChanges[i].m_sBGName;
return m_BackgroundChanges[iLayer][i].m_sBGName;
}
@@ -720,7 +721,7 @@ void Song::TidyUpData()
/* Use this->GetBeatFromElapsedTime(0) instead of 0 to start when the
* music starts. */
if( arrayPossibleMovies.size() == 1 )
this->AddBackgroundChange( BackgroundChange(0,arrayPossibleMovies[0],1.f,true,true,false) );
this->AddBackgroundChange( 0, BackgroundChange(0,arrayPossibleMovies[0],1.f,true,true,false) );
}
@@ -1209,7 +1210,15 @@ bool Song::HasBanner() const {return m_sBannerFile != "" && IsAFile(GetBann
bool Song::HasLyrics() const {return m_sLyricsFile != "" && IsAFile(GetLyricsPath()); }
bool Song::HasBackground() const {return m_sBackgroundFile != "" && IsAFile(GetBackgroundPath()); }
bool Song::HasCDTitle() const {return m_sCDTitleFile != "" && IsAFile(GetCDTitlePath()); }
bool Song::HasBGChanges() const {return !m_BackgroundChanges.empty(); }
bool Song::HasBGChanges() const
{
for( unsigned i=0; i<NUM_BACKGROUND_LAYERS; i++ )
{
if( !m_BackgroundChanges[i].empty() )
return true;
}
return false;
}
CString GetSongAssetPath( CString sPath, const CString &sSongPath )
{
+6 -1
View File
@@ -229,6 +229,8 @@ void SongManager::LoadStepManiaSongDir( CString sDir, LoadingWindow *ld )
}
}
// Why Symlink instead of allowing membership in multiple groups via song tags?
// -Chris
void SongManager::LoadGroupSymLinks(CString sDir, CString sGroupFolder)
{
// Find all symlink files in this folder
@@ -243,14 +245,17 @@ void SongManager::LoadGroupSymLinks(CString sDir, CString sGroupFolder)
Song* pNewSong = new Song;
if( !pNewSong->LoadFromSongDir( sSymDestination ) )
{
delete pNewSong; // The song failed to load.
}
else
{
const vector<Steps*>& vpSteps = pNewSong->GetAllSteps();
while( vpSteps.size() )
pNewSong->RemoveSteps( vpSteps[0] );
pNewSong->m_BackgroundChanges.clear();
for( int i=0; i<NUM_BACKGROUND_LAYERS; i++ )
pNewSong->m_BackgroundChanges[i].clear();
pNewSong->m_bIsSymLink = true; // Very important so we don't double-parse later
pNewSong->m_sGroupName = sGroupFolder;
+6 -5
View File
@@ -17,11 +17,12 @@ class Profile;
class StepsID;
struct lua_State;
#define MAX_EDITS_PER_SONG_PER_PROFILE 5
#define MAX_EDITS_PER_SONG 5*NUM_PROFILE_SLOTS
const int MAX_EDITS_PER_SONG_PER_PROFILE = 5;
const int MAX_EDITS_PER_SONG = 5*NUM_PROFILE_SLOTS;
extern const int FILE_CACHE_VERSION;
const int NUM_BACKGROUND_LAYERS = 2;
struct BackgroundChange
{
@@ -153,18 +154,18 @@ public:
bool Matches(CString sGroup, CString sSong) const;
TimingData m_Timing;
vector<BackgroundChange> m_BackgroundChanges; // this must be sorted before gameplay
vector<BackgroundChange> m_BackgroundChanges[NUM_BACKGROUND_LAYERS]; // these must be sorted before gameplay
vector<BackgroundChange> m_ForegroundChanges; // this must be sorted before gameplay
vector<LyricSegment> m_LyricSegments; // this must be sorted before gameplay
void AddBPMSegment( const BPMSegment &seg ) { m_Timing.AddBPMSegment( seg ); }
void AddStopSegment( const StopSegment &seg ) { m_Timing.AddStopSegment( seg ); }
void AddBackgroundChange( BackgroundChange seg );
void AddBackgroundChange( int iLayer, BackgroundChange seg );
void AddForegroundChange( BackgroundChange seg );
void AddLyricSegment( LyricSegment seg );
void GetDisplayBpms( DisplayBpms &AddTo ) const;
CString GetBackgroundAtBeat( float fBeat ) const;
CString GetBackgroundAtBeat( int iLayer, float fBeat ) const;
float GetBPMAtBeat( float fBeat ) const { return m_Timing.GetBPMAtBeat( fBeat ); }
void SetBPMAtBeat( float fBeat, float fBPM ) { m_Timing.SetBPMAtBeat( fBeat, fBPM ); }