diff --git a/stepmania/src/ActorUtil.cpp b/stepmania/src/ActorUtil.cpp index b6955883ab..815d9593c7 100644 --- a/stepmania/src/ActorUtil.cpp +++ b/stepmania/src/ActorUtil.cpp @@ -131,7 +131,16 @@ Actor* ActorUtil::LoadFromActorFile( const CString& sAniDir, const XNode* pNode bHasClass = pNode->GetAttrValue( "Type", sClass ); // for backward compatibility CString sFile; - pNode->GetAttrValue( "File", sFile ); + if( pNode->GetAttrValue( "File", sFile ) ) + { + if( sFile == "" ) + { + CString sError = ssprintf( "The actor file in '%s' is as a blank, invalid File attribute \"%s\"", + sAniDir.c_str(), sClass.c_str() ); + RageException::Throw( sError ); + } + } + LUA->RunAtExpressionS( sFile ); bool bIsAbsolutePath = sFile.Left(1) == "/"; diff --git a/stepmania/src/Background.cpp b/stepmania/src/Background.cpp index 6dd32b3c2e..177d5a58b3 100644 --- a/stepmania/src/Background.cpp +++ b/stepmania/src/Background.cpp @@ -378,11 +378,36 @@ bool BackgroundImpl::Layer::CreateBackground( const Song *pSong, const Backgroun } ASSERT( !sEffect.empty() ); - vector vsPaths, vsThrowAway; - BackgroundUtil::GetBackgroundEffects( sEffect, vsPaths, vsThrowAway ); - ASSERT_M( !vsPaths.empty(), ssprintf("BackgroundEffect '%s' is missing.",sEffect.c_str()) ); - ASSERT_M( vsPaths.size()==1, ssprintf("BackgroundEffect '%s' has more than one match.",sEffect.c_str()) ); - const CString &sEffectFile = vsPaths[0]; + + // Set Lua color globals + LUA->SetGlobal( ssprintf("Color%d",1), bd.m_sColor1.empty() ? "1,1,1,1" : bd.m_sColor1 ); + LUA->SetGlobal( ssprintf("Color%d",2), bd.m_sColor2.empty() ? "1,1,1,1" : bd.m_sColor2 ); + + + // Resolve the effect file. + CString sEffectFile; + for( int i=0; i<2; i++ ) + { + vector vsPaths, vsThrowAway; + BackgroundUtil::GetBackgroundEffects( sEffect, vsPaths, vsThrowAway ); + if( vsPaths.empty() ) + { + LOG->Warn( "BackgroundEffect '%s' is missing.",sEffect.c_str() ); + sEffect = SBE_Centered; + } + else if( vsPaths.size() > 1 ) + { + LOG->Warn( "BackgroundEffect '%s' has more than one match.",sEffect.c_str() ); + sEffect = SBE_Centered; + } + else + { + sEffectFile = vsPaths[0]; + break; + } + } + ASSERT( !sEffectFile.empty() ); + Actor *pActor = ActorUtil::MakeActor( sEffectFile ); ASSERT( pActor ); @@ -390,6 +415,8 @@ bool BackgroundImpl::Layer::CreateBackground( const Song *pSong, const Backgroun for( unsigned i=0; iSetGlobal( ssprintf("File%d",i+1), CString() ); + LUA->SetGlobal( ssprintf("Color%d",1), CString() ); + LUA->SetGlobal( ssprintf("Color%d",2), CString() ); return true; } @@ -729,6 +756,10 @@ void BackgroundImpl::Layer::UpdateCurBGChange( const Song *pSong, float fLastMus { LOG->Trace( "old bga %d -> new bga %d, %f, %f", m_iCurBGChangeIndex, i, m_aBGChanges[i].m_fStartBeat, fBeat ); + BackgroundChange oldChange; + if( m_iCurBGChangeIndex != -1 ) + oldChange = m_aBGChanges[m_iCurBGChangeIndex]; + m_iCurBGChangeIndex = i; const BackgroundChange& change = m_aBGChanges[i]; @@ -764,11 +795,19 @@ void BackgroundImpl::Layer::UpdateCurBGChange( const Song *pSong, float fLastMus m_pCurrentBGA->Reset(); m_pCurrentBGA->SetUpdateRate( change.m_fRate ); + + // Set Lua color globals before calling Init and On + LUA->SetGlobal( ssprintf("Color%d",1), change.m_def.m_sColor1.empty() ? "1,1,1,1" : change.m_def.m_sColor1 ); + LUA->SetGlobal( ssprintf("Color%d",2), change.m_def.m_sColor2.empty() ? "1,1,1,1" : change.m_def.m_sColor2 ); + m_pCurrentBGA->PlayCommand( "Init" ); m_pCurrentBGA->PlayCommand( "On" ); m_pCurrentBGA->PlayCommand( "GainFocus" ); - m_fSecsLeftInFade = m_pFadingBGA ? m_pFadingBGA->GetTweenTimeLeft() : 0; + LUA->SetGlobal( ssprintf("Color%d",1), CString() ); + LUA->SetGlobal( ssprintf("Color%d",2), CString() ); + + m_fSecsLeftInFade = m_pFadingBGA ? m_pFadingBGA->GetTweenTimeLeft() / oldChange.m_fRate : 0; /* How much time of this BGA have we skipped? (This happens with SetSeconds.) */ const float fStartSecond = pSong->m_Timing.GetElapsedTimeFromBeat( change.m_fStartBeat ); diff --git a/stepmania/src/BackgroundUtil.h b/stepmania/src/BackgroundUtil.h index b3b6911f8a..0d1464acb7 100644 --- a/stepmania/src/BackgroundUtil.h +++ b/stepmania/src/BackgroundUtil.h @@ -27,6 +27,8 @@ struct BackgroundDef COMPARE( m_sEffect ); COMPARE( m_sFile1 ); COMPARE( m_sFile2 ); + COMPARE( m_sColor1 ); + COMPARE( m_sColor2 ); #undef COMPARE return false; } @@ -35,12 +37,16 @@ struct BackgroundDef return m_sEffect == other.m_sEffect && m_sFile1 == other.m_sFile1 && - m_sFile2 == other.m_sFile2; + m_sFile2 == other.m_sFile2 && + m_sColor1 == other.m_sColor1 && + m_sColor2 == other.m_sColor2; } bool IsEmpty() { return m_sFile1.empty() && m_sFile2.empty(); } CString m_sEffect; // "" == automatically choose CString m_sFile1; // must not be "" CString m_sFile2; // may be "" + CString m_sColor1; // "" == use default + CString m_sColor2; // "" == use default }; struct BackgroundChange diff --git a/stepmania/src/NoteField.cpp b/stepmania/src/NoteField.cpp index 14d4cdc24a..2b449993e9 100644 --- a/stepmania/src/NoteField.cpp +++ b/stepmania/src/NoteField.cpp @@ -583,6 +583,8 @@ void NoteField::DrawPrimitives() if( change.m_fRate!=1.0f ) vsParts.push_back( ssprintf("%.2f%%",change.m_fRate*100) ); if( !change.m_sTransition.empty() ) vsParts.push_back( change.m_sTransition ); if( !change.m_def.m_sEffect.empty() ) vsParts.push_back( change.m_def.m_sEffect ); + if( !change.m_def.m_sColor1.empty() ) vsParts.push_back( change.m_def.m_sColor1 ); + if( !change.m_def.m_sColor2.empty() ) vsParts.push_back( change.m_def.m_sColor2 ); vsBGChanges.push_back( join("\n",vsParts) ); } diff --git a/stepmania/src/NotesLoaderSM.cpp b/stepmania/src/NotesLoaderSM.cpp index ef8e3caa8f..3d8941de54 100644 --- a/stepmania/src/NotesLoaderSM.cpp +++ b/stepmania/src/NotesLoaderSM.cpp @@ -157,41 +157,71 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out ) } } -bool LoadFromBGChangesString( BackgroundChange &change, const CString &sBGChangeExpression ) +bool LoadFromBGChangesString( BackgroundChange &change, const CString &_sBGChangeExpression ) { + CString sBGChangeExpression = _sBGChangeExpression; + sBGChangeExpression.Replace( '^', ',' ); // UGLY: unescape "," in colors + CStringArray aBGChangeValues; split( sBGChangeExpression, "=", aBGChangeValues, false ); - if( aBGChangeValues.size() >= 6 ) - { - change.m_fRate = strtof( aBGChangeValues[2], NULL ); - change.m_sTransition = (atoi( aBGChangeValues[3] ) != 0) ? "CrossFade" : ""; - bool bRewindMovie = atoi( aBGChangeValues[4] ) != 0; - bool bLoop = atoi( aBGChangeValues[5] ) != 0; + aBGChangeValues.resize( min((int)aBGChangeValues.size(),11) ); - // m_sEffect may be overwritten by param 7 below. - if( bRewindMovie ) - change.m_def.m_sEffect = SBE_StretchRewind; - if( !bLoop ) - change.m_def.m_sEffect = SBE_StretchNoLoop; - } - if( aBGChangeValues.size() >= 9 ) + switch( aBGChangeValues.size() ) { - change.m_def.m_sEffect = aBGChangeValues[6]; - change.m_def.m_sFile2 = aBGChangeValues[7]; + case 11: + change.m_def.m_sColor2 = aBGChangeValues[10]; + // fall through + case 10: + change.m_def.m_sColor1 = aBGChangeValues[9]; + // fall through + case 9: change.m_sTransition = aBGChangeValues[8]; - } - if( aBGChangeValues.size() >= 2 ) - { - change.m_fStartBeat = strtof( aBGChangeValues[0], NULL ); + // fall through + case 8: + change.m_def.m_sFile2 = aBGChangeValues[7]; + // fall through + case 7: + change.m_def.m_sEffect = aBGChangeValues[6]; + // fall through + case 6: + // param 7 overrides this. + // Backward compatibility: + if( change.m_def.m_sEffect.empty() ) + { + bool bLoop = atoi( aBGChangeValues[5] ) != 0; + if( !bLoop ) + change.m_def.m_sEffect = SBE_StretchNoLoop; + } + // fall through + case 5: + // param 7 overrides this. + // Backward compatibility: + if( change.m_def.m_sEffect.empty() ) + { + bool bRewindMovie = atoi( aBGChangeValues[4] ) != 0; + if( bRewindMovie ) + change.m_def.m_sEffect = SBE_StretchRewind; + } + // fall through + case 4: + // param 9 overrides this. + // Backward compatibility: + if( change.m_sTransition.empty() ) + change.m_sTransition = (atoi( aBGChangeValues[3] ) != 0) ? "CrossFade" : ""; + // fall through + case 3: + change.m_fRate = strtof( aBGChangeValues[2], NULL ); + // fall through + case 2: change.m_def.m_sFile1 = aBGChangeValues[1]; - return true; - } - else - { - LOG->Warn("Invalid #BGCHANGES value \"%s\" was ignored", sBGChangeExpression.c_str()); - return false; + // fall through + case 1: + change.m_fStartBeat = strtof( aBGChangeValues[0], NULL ); + // fall through } + + return aBGChangeValues.size() >= 2; } bool SMLoader::LoadFromSMFile( CString sPath, Song &out ) diff --git a/stepmania/src/NotesWriterSM.cpp b/stepmania/src/NotesWriterSM.cpp index d9536f5a4d..14d18fe07c 100644 --- a/stepmania/src/NotesWriterSM.cpp +++ b/stepmania/src/NotesWriterSM.cpp @@ -15,8 +15,8 @@ static CString BackgroundChangeToString( const BackgroundChange &bgc ) { - return ssprintf( - "%.3f=%s=%.3f=%d=%d=%d=%s=%s=%s,", + CString s = ssprintf( + "%.3f=%s=%.3f=%d=%d=%d=%s=%s=%s=%s=%s", bgc.m_fStartBeat, bgc.m_def.m_sFile1.c_str(), bgc.m_fRate, @@ -25,8 +25,12 @@ static CString BackgroundChangeToString( const BackgroundChange &bgc ) bgc.m_def.m_sEffect != SBE_StretchNoLoop, // backward compat bgc.m_def.m_sEffect.c_str(), bgc.m_def.m_sFile2.c_str(), - bgc.m_sTransition.c_str() + bgc.m_sTransition.c_str(), + bgc.m_def.m_sColor1.c_str(), + bgc.m_def.m_sColor2.c_str() ); + s.Replace( ',', '^' ); // UGLY: escape "," in colors. + return s; } void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out ) @@ -106,7 +110,7 @@ void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out ) f.Write( ssprintf("#BGCHANGES%d:", b+1) ); FOREACH_CONST( BackgroundChange, out.GetBackgroundChanges(b), bgc ) - f.PutLine( BackgroundChangeToString(*bgc) ); + f.PutLine( BackgroundChangeToString(*bgc)+"," ); /* 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. @@ -122,7 +126,7 @@ void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out ) f.Write( "#FGCHANGES:" ); FOREACH_CONST( BackgroundChange, out.GetForegroundChanges(), bgc ) { - f.PutLine( BackgroundChangeToString(*bgc) ); + f.PutLine( BackgroundChangeToString(*bgc)+"," ); } f.PutLine( ";" ); } diff --git a/stepmania/src/ScreenEdit.cpp b/stepmania/src/ScreenEdit.cpp index e3957268c9..5963049c48 100644 --- a/stepmania/src/ScreenEdit.cpp +++ b/stepmania/src/ScreenEdit.cpp @@ -466,8 +466,10 @@ static Menu g_BackgroundChange( "ScreenMiniMenuBackgroundChange", MenuRow( ScreenEdit::layer, "Layer", false, EDIT_MODE_FULL, 0, "" ), MenuRow( ScreenEdit::rate, "Rate", true, EDIT_MODE_FULL, 10, "0%","10%","20%","30%","40%","50%","60%","70%","80%","90%","100%","120%","140%","160%","180%","200%" ), - MenuRow( ScreenEdit::transition, "Transition", true, EDIT_MODE_FULL, 0, NULL ), + MenuRow( ScreenEdit::transition, "Force Transition", true, EDIT_MODE_FULL, 0, NULL ), MenuRow( ScreenEdit::effect, "Force Effect", true, EDIT_MODE_FULL, 0, NULL ), + MenuRow( ScreenEdit::color1, "Force Color 1", true, EDIT_MODE_FULL, 0, "","1,1,1,1","0.5,0.5,0.5,1","1,1,1,0.5","0,0,0,1","1,0,0,1","0,1,0,1","0,0,1,1","1,1,0,1","0,1,1,1","1,0,1,1" ), + MenuRow( ScreenEdit::color2, "Force Color 2", true, EDIT_MODE_FULL, 0, "","1,1,1,1","0.5,0.5,0.5,1","1,1,1,0.5","0,0,0,1","1,0,0,1","0,1,0,1","0,0,1,1","1,1,0,1","0,1,1,1","1,0,1,1" ), MenuRow( ScreenEdit::file1_type, "File1 Type", true, EDIT_MODE_FULL, 0, "Song BGAnimation", "Song Movie", "Song Bitmap", "Global BGAnimation", "Global Movie", "Global Movie from Song Group", "Global Movie from Song Group and Genre", "Dynamic Random", "Baked Random", "None" ), MenuRow( ScreenEdit::file1_song_bganimation, "File1 Song BGAnimation", EnabledIfSet1SongBGAnimation, EDIT_MODE_FULL, 0, NULL ), MenuRow( ScreenEdit::file1_song_movie, "File1 Song Movie", EnabledIfSet1SongMovie, EDIT_MODE_FULL, 0, NULL ), @@ -2206,6 +2208,8 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAns if( g_BackgroundChange.rows[file2_global_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) g_BackgroundChange.rows[file2_type].iDefaultChoice = global_movie; if( g_BackgroundChange.rows[file2_global_movie_song_group]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) g_BackgroundChange.rows[file2_type].iDefaultChoice = global_movie_song_group; if( g_BackgroundChange.rows[file2_global_movie_song_group_and_genre]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) g_BackgroundChange.rows[file2_type].iDefaultChoice = global_movie_song_group_and_genre; + g_BackgroundChange.rows[color1]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sColor1 ); + g_BackgroundChange.rows[color2]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sColor2 ); SCREENMAN->MiniMenu( &g_BackgroundChange, SM_BackFromBGChange ); } @@ -2736,6 +2740,8 @@ void ScreenEdit::HandleBGChangeChoice( BGChangeChoice c, const vector &iAns newChange.m_fRate = strtof( g_BackgroundChange.rows[rate].choices[iAnswers[rate]], NULL )/100.f; newChange.m_sTransition = g_BackgroundChange.rows[transition].choices[iAnswers[transition]]; newChange.m_def.m_sEffect = g_BackgroundChange.rows[effect].choices[iAnswers[effect]]; + newChange.m_def.m_sColor1 = g_BackgroundChange.rows[color1].choices[iAnswers[color1]]; + newChange.m_def.m_sColor2 = g_BackgroundChange.rows[color2].choices[iAnswers[color2]]; switch( iAnswers[file1_type] ) { default: ASSERT(0); diff --git a/stepmania/src/ScreenEdit.h b/stepmania/src/ScreenEdit.h index 377489ccc8..6905b1f08f 100644 --- a/stepmania/src/ScreenEdit.h +++ b/stepmania/src/ScreenEdit.h @@ -371,6 +371,8 @@ public: rate, transition, effect, + color1, + color2, file1_type, file1_song_bganimation, file1_song_movie,