diff --git a/stepmania/src/ActorUtil.cpp b/stepmania/src/ActorUtil.cpp index 21ddb06ef7..d52c433023 100644 --- a/stepmania/src/ActorUtil.cpp +++ b/stepmania/src/ActorUtil.cpp @@ -16,6 +16,7 @@ #include "FontCharAliases.h" #include "LuaManager.h" #include "MessageManager.h" +#include "Foreach.h" #include "arch/Dialog/Dialog.h" @@ -341,31 +342,59 @@ void ActorUtil::SetXY( Actor& actor, const CString &sType ) actor.SetXY( THEME->GetMetricF(sType,actor.GetID()+"X"), THEME->GetMetricF(sType,actor.GetID()+"Y") ); } -void ActorUtil::RunCommand( Actor& actor, const CString &sType, const CString &sCommandName ) +void ActorUtil::LoadCommand( Actor& actor, const CString &sType, const CString &sCommandName ) { - actor.PlayCommand( sCommandName ); + actor.AddCommand( sCommandName, THEME->GetMetricA(sType,actor.GetID()+sCommandName+"Command") ); +} +void ActorUtil::LoadAndPlayCommand( Actor& actor, const CString &sType, const CString &sCommandName ) +{ // HACK: It's very often that we command things to TweenOffScreen // that we aren't drawing. We know that an Actor is not being // used if its name is blank. So, do nothing on Actors with a blank name. // (Do "playcommand" anyway; BGAs often have no name.) - if( sCommandName=="Off" ) + if( sCommandName=="Off" && actor.GetID().empty() ) + return; + + ASSERT_M( + !actor.GetID().empty(), + ssprintf("!actor.GetID().empty() ('%s', '%s')", sType.c_str(), sCommandName.c_str()) + ); + + if( !actor.HasCommand(sCommandName ) ) // this actor hasn't loaded commands yet + LoadAllCommands( actor, sType ); + + // If we didn't load the command in LoadAllCommands, load the requested command + // explicitly. The metric is missing, and ThemeManager will prompt. + if( !actor.HasCommand(sCommandName) ) { - if( actor.GetID().empty() ) - return; - } - else - { - ASSERT_M( !actor.GetID().empty(), ssprintf("!actor.GetID().empty() ('%s', '%s')", - sType.c_str(), sCommandName.c_str()) ); + // If this metric exists and we didn't load it in LoadAllCommands, then + // LoadAllCommands has a bug. + DEBUG_ASSERT( !THEME->HasMetric(sType,actor.GetID()+sCommandName+"Command") ); + + LoadCommand( actor, sType, sCommandName ); } - actor.RunCommands( THEME->GetMetricA(sType,actor.GetID()+sCommandName+"Command") ); + actor.PlayCommand( sCommandName ); } -void ActorUtil::LoadCommand( Actor& actor, const CString &sType, const CString &sCommandName ) +void ActorUtil::LoadAllCommands( Actor& actor, const CString &sType ) { - actor.AddCommand( sCommandName, THEME->GetMetricA(sType,actor.GetID()+sCommandName+"Command") ); + set vsValueNames; + THEME->GetMetricsThatBeginWith( sType, actor.GetID(), vsValueNames ); + + FOREACHS_CONST( CString, vsValueNames, v ) + { + const CString &sv = *v; + if( sv.Right(7) == "Command" ) + { + // Ugh. Where did StdString::Mid go? + CString sCommandName = sv; + sCommandName = sCommandName.Left( sCommandName.size()-7 ); // strip off "Command" + sCommandName = sCommandName.Right( sCommandName.size()-actor.GetID().size() ); // strip off actor ID + LoadCommand( actor, sType, sCommandName ); + } + } } /* diff --git a/stepmania/src/ActorUtil.h b/stepmania/src/ActorUtil.h index 401ddc9b6a..0b6b5742a4 100644 --- a/stepmania/src/ActorUtil.h +++ b/stepmania/src/ActorUtil.h @@ -31,15 +31,15 @@ namespace ActorUtil Actor* Create( const CString& sClassName, const CString& sDir, const XNode* pNode ); - void SetXY( Actor& actor, const CString &sType ); inline void SetXY( Actor* pActor, const CString &sType ) { SetXY( *pActor, sType ); } - void RunCommand( Actor& actor, const CString &sType, const CString &sCommandName ); void LoadCommand( Actor& actor, const CString &sType, const CString &sCommandName ); + void LoadAndPlayCommand( Actor& actor, const CString &sType, const CString &sCommandName ); + void LoadAllCommands( Actor& actor, const CString &sType ); - inline void OnCommand( Actor& actor, const CString &sType ) { RunCommand( actor, sType, "On" ); } - inline void OffCommand( Actor& actor, const CString &sType ) { RunCommand( actor, sType, "Off" ); } + inline void OnCommand( Actor& actor, const CString &sType ) { LoadAndPlayCommand( actor, sType, "On" ); } + inline void OffCommand( Actor& actor, const CString &sType ) { LoadAndPlayCommand( actor, sType, "Off" ); } inline void SetXYAndOnCommand( Actor& actor, const CString &sType ) { SetXY( actor, sType ); @@ -47,7 +47,7 @@ namespace ActorUtil } /* convenience */ - inline void RunCommand( Actor* pActor, const CString &sType, const CString &sCommandName ) { if(pActor) RunCommand( *pActor, sType, sCommandName ); } + inline void LoadAndPlayCommand( Actor* pActor, const CString &sType, const CString &sCommandName ) { if(pActor) LoadAndPlayCommand( *pActor, sType, sCommandName ); } inline void OnCommand( Actor* pActor, const CString &sType ) { if(pActor) OnCommand( *pActor, sType ); } inline void OffCommand( Actor* pActor, const CString &sType ) { if(pActor) OffCommand( *pActor, sType ); } inline void SetXYAndOnCommand( Actor* pActor, const CString &sType ) { if(pActor) SetXYAndOnCommand( *pActor, sType ); } @@ -63,7 +63,7 @@ namespace ActorUtil #define ON_COMMAND( actor ) ActorUtil::OnCommand( actor, m_sName ) #define OFF_COMMAND( actor ) ActorUtil::OffCommand( actor, m_sName ) #define SET_XY_AND_ON_COMMAND( actor ) ActorUtil::SetXYAndOnCommand( actor, m_sName ) -#define COMMAND( actor, command_name ) ActorUtil::RunCommand( actor, m_sName, command_name ) +#define COMMAND( actor, command_name ) ActorUtil::LoadAndPlayCommand( actor, m_sName, command_name ) #endif diff --git a/stepmania/src/ScreenSelectMusic.cpp b/stepmania/src/ScreenSelectMusic.cpp index 032573393a..7dfe9540ad 100644 --- a/stepmania/src/ScreenSelectMusic.cpp +++ b/stepmania/src/ScreenSelectMusic.cpp @@ -317,6 +317,7 @@ void ScreenSelectMusic::Init() m_sprOptionsMessage.StopAnimating(); m_sprOptionsMessage.SetHidden( true ); SET_XY( m_sprOptionsMessage ); + ActorUtil::LoadAllCommands( m_sprOptionsMessage, m_sName ); //this->AddChild( &m_sprOptionsMessage ); // we have to draw this manually over the top of transitions FOREACH_PlayerNumber( p ) diff --git a/stepmania/src/ThemeManager.cpp b/stepmania/src/ThemeManager.cpp index 5ef62c2491..fc8894b80f 100644 --- a/stepmania/src/ThemeManager.cpp +++ b/stepmania/src/ThemeManager.cpp @@ -821,6 +821,44 @@ void ThemeManager::GetMetric( const CString &sClassName, const CString &sValueNa valueOut.SetFromExpression( "function(self) " + sValue + "end" ); } +void ThemeManager::GetMetricsThatBeginWith( const CString &_sClassName, const CString &sValueName, set &vsValueNamesOut ) +{ + CString sClassName = _sClassName; + while( !sClassName.empty() ) + { + // Iterate over themes in the chain. + for( deque::const_iterator i = g_vThemes.begin(); + i != g_vThemes.end(); + ++i ) + { + const XNode *cur = i->iniMetrics->GetChild( sClassName ); + if( cur == NULL ) + continue; + + // Iterate over all metrics that match. + XAttrs::const_iterator j = cur->m_attrs.lower_bound( sValueName ); + if( j == cur->m_attrs.end() ) + continue; + for( ; true; j++ ) + { + const CString &sv = j->first; + if( sv.Left(sValueName.size()) == sValueName ) + vsValueNamesOut.insert( sv ); + else + break; + } + } + + // put the fallback (if any) in sClassName + CString sFallback; + if( GetMetricRaw( sClassName, "Fallback", sFallback ) ) + sClassName = sFallback; + else + sClassName = ""; + } +} + + // lua start #include "LuaBinding.h" diff --git a/stepmania/src/ThemeManager.h b/stepmania/src/ThemeManager.h index 68bf62c429..0c252eea36 100644 --- a/stepmania/src/ThemeManager.h +++ b/stepmania/src/ThemeManager.h @@ -89,6 +89,8 @@ public: void GetMetric( const CString &sClassName, const CString &sValueName, LuaExpression &valueOut ); void GetMetric( const CString &sClassName, const CString &sValueName, apActorCommands &valueOut ); + void GetMetricsThatBeginWith( const CString &sClassName, const CString &sValueName, set &vsValueNamesOut ); + // // For self-registering metrics //