diff --git a/stepmania/src/ScreenOptionsMaster.cpp b/stepmania/src/ScreenOptionsMaster.cpp index f869b2d7e2..bda835a51a 100644 --- a/stepmania/src/ScreenOptionsMaster.cpp +++ b/stepmania/src/ScreenOptionsMaster.cpp @@ -102,6 +102,79 @@ void ScreenOptionsMaster::SetList( OptionRowDefinition &row, OptionRowHandler &h } } +void ScreenOptionsMaster::SetLua( OptionRowDefinition &row, OptionRowHandler &hand, const CString &sLuaFunction ) +{ + hand.type = ROW_LUA; + hand.m_bUseModNameForIcon = true; + + /* Run the Lua expression. It should return a table. */ + hand.m_LuaTable.SetFromExpression( sLuaFunction ); + + if( hand.m_LuaTable.GetLuaType() != LUA_TTABLE ) + RageException::Throw( "Result of \"%s\" is not a table", sLuaFunction.c_str() ); + + { + hand.m_LuaTable.PushSelf( LUA->L ); + + lua_pushstring( LUA->L, "Name" ); + lua_gettable( LUA->L, -2 ); + const char *pStr = lua_tostring( LUA->L, -1 ); + if( pStr == NULL ) + RageException::Throw( "\"%s\" \"Name\" entry is not a string", sLuaFunction.c_str() ); + row.name = pStr; + lua_pop( LUA->L, 1 ); + + + lua_pushstring( LUA->L, "OneChoiceForAllPlayers" ); + lua_gettable( LUA->L, -2 ); + row.bOneChoiceForAllPlayers = !!lua_toboolean( LUA->L, -1 ); + lua_pop( LUA->L, 1 ); + + + lua_pushstring( LUA->L, "LayoutType" ); + lua_gettable( LUA->L, -2 ); + pStr = lua_tostring( LUA->L, -1 ); + if( pStr == NULL ) + RageException::Throw( "\"%s\" \"LayoutType\" entry is not a string", sLuaFunction.c_str() ); + row.layoutType = StringToLayoutType( pStr ); + lua_pop( LUA->L, 1 ); + + lua_pushstring( LUA->L, "SelectType" ); + lua_gettable( LUA->L, -2 ); + pStr = lua_tostring( LUA->L, -1 ); + if( pStr == NULL ) + RageException::Throw( "\"%s\" \"SelectType\" entry is not a string", sLuaFunction.c_str() ); + row.selectType = StringToSelectType( pStr ); + lua_pop( LUA->L, 1 ); + + /* Iterate over the "Choices" table. */ + lua_pushstring( LUA->L, "Choices" ); + lua_gettable( LUA->L, -2 ); + if( !lua_istable( LUA->L, -1 ) ) + RageException::Throw( "\"%s\" \"Choices\" is not a table", sLuaFunction.c_str() ); + + lua_pushnil( LUA->L ); + while( lua_next(LUA->L, -2) != 0 ) + { + /* `key' is at index -2 and `value' at index -1 */ + const char *pValue = lua_tostring( LUA->L, -1 ); + if( pValue == NULL ) + RageException::Throw( "\"%s\" Column entry is not a string", sLuaFunction.c_str() ); + LOG->Trace( "'%s'", pValue); + + row.choices.push_back( pValue ); + + lua_pop( LUA->L, 1 ); /* removes `value'; keeps `key' for next iteration */ + } + + lua_pop( LUA->L, 1 ); /* pop choices table */ + + lua_pop( LUA->L, 1 ); /* pop main table */ + ASSERT( lua_gettop(LUA->L) == 0 ); + } +} + + /* Add a list of difficulties/edits to the given row/handler. */ void ScreenOptionsMaster::SetSteps( OptionRowDefinition &row, OptionRowHandler &hand ) { @@ -357,6 +430,7 @@ ScreenOptionsMaster::ScreenOptionsMaster( CString sClassName ): const CString &name = command.GetName(); if( !name.CompareNoCase("list") ) SetList( row, hand, sArg(1) ); + else if( !name.CompareNoCase("lua") ) SetLua( row, hand, sArg(1) ); else if( !name.CompareNoCase("steps") ) SetSteps( row, hand ); else if( !name.CompareNoCase("conf") ) SetConf( row, hand, sArg(1) ); else if( !name.CompareNoCase("characters") ) SetCharacters( row, hand ); @@ -455,6 +529,54 @@ void ScreenOptionsMaster::ImportOption( const OptionRowDefinition &row, const Op return; } + case ROW_LUA: + { + ASSERT( lua_gettop(LUA->L) == 0 ); + + /* Evaluate the GetValue(self,array,pn) function, where array is a table + * representing vbSelectedOut. */ + + /* Hack: the NextRow entry is never set, and should be transparent. Remove + * it, and readd it below. */ + if( m_OptionsNavigation == NAV_TOGGLE_THREE_KEY ) + vbSelectedOut.erase( vbSelectedOut.begin() ); + + /* Create the vbSelectedOut table. */ + LUA->CreateTableFromArray( vbSelectedOut ); + ASSERT( lua_gettop(LUA->L) == 1 ); /* vbSelectedOut table */ + + /* Get the function to call from m_LuaTable. */ + hand.m_LuaTable.PushSelf( LUA->L ); + ASSERT( lua_istable( LUA->L, -1 ) ); + + lua_pushstring( LUA->L, "GetSelections" ); + lua_gettable( LUA->L, -2 ); + if( !lua_isfunction( LUA->L, -1 ) ) + RageException::Throw( "\"%s\" \"GetSelections\" entry is not a function", row.name.c_str() ); + + /* Argument 1 (vbSelectedOut): */ + lua_pushvalue( LUA->L, 1 ); + + /* Argument 2 (pn): */ + LUA->PushStack( (int) pn ); + + ASSERT( lua_gettop(LUA->L) == 5 ); /* vbSelectedOut, m_iLuaTable, function, arg, arg */ + + lua_call( LUA->L, 2, 0 ); // call function with 2 arguments and 0 results + ASSERT( lua_gettop(LUA->L) == 2 ); + + lua_pop( LUA->L, 1 ); /* pop option table */ + + LUA->ReadArrayFromTable( vbSelectedOut ); + if( m_OptionsNavigation == NAV_TOGGLE_THREE_KEY ) + vbSelectedOut.insert( vbSelectedOut.begin(), false ); + + lua_pop( LUA->L, 1 ); /* pop vbSelectedOut table */ + + ASSERT( lua_gettop(LUA->L) == 0 ); + return; + } + case ROW_CONFIG: { int iSelection = hand.opt->Get(); @@ -543,6 +665,53 @@ int ScreenOptionsMaster::ExportOption( const OptionRowDefinition &row, const Opt hand.ListEntries[i].Apply( pn ); } break; + case ROW_LUA: + { + ASSERT( lua_gettop(LUA->L) == 0 ); + + /* Evaluate the GetValue(self,array,pn) function, where array is a table + * representing vbSelectedOut. */ + + /* Hack: the NextRow entry is never set, and should be transparent. Remove it. */ + vector vbSelectedCopy = vbSelected; + if( m_OptionsNavigation == NAV_TOGGLE_THREE_KEY ) + vbSelectedCopy.erase( vbSelectedCopy.begin() ); + + /* Create the vbSelectedOut table. */ + LUA->CreateTableFromArray( vbSelectedCopy ); + ASSERT( lua_gettop(LUA->L) == 1 ); /* vbSelectedOut table */ + + /* Get the function to call. */ + hand.m_LuaTable.PushSelf( LUA->L ); + ASSERT( lua_istable( LUA->L, -1 ) ); + + lua_pushstring( LUA->L, "SetSelections" ); + lua_gettable( LUA->L, -2 ); + if( !lua_isfunction( LUA->L, -1 ) ) + RageException::Throw( "\"%s\" \"SetSelections\" entry is not a function", row.name.c_str() ); + + /* Push self. */ + hand.m_LuaTable.PushSelf( LUA->L ); + + /* Argument 1 (vbSelectedOut): */ + lua_pushvalue( LUA->L, 1 ); + + /* Argument 2 (pn): */ + LUA->PushStack( (int) pn ); + + ASSERT( lua_gettop(LUA->L) == 5 ); /* vbSelectedOut, m_iLuaTable, function, arg, arg */ + + lua_call( LUA->L, 2, 0 ); // call function with 3 arguments and 0 results + ASSERT( lua_gettop(LUA->L) == 2 ); + + lua_pop( LUA->L, 1 ); /* pop option table */ + lua_pop( LUA->L, 1 ); /* pop vbSelected table */ + + ASSERT( lua_gettop(LUA->L) == 0 ); + + // XXX: allow specifying the mask + return 0; + } case ROW_CONFIG: { diff --git a/stepmania/src/ScreenOptionsMaster.h b/stepmania/src/ScreenOptionsMaster.h index f13249dbaf..adf1a126d1 100644 --- a/stepmania/src/ScreenOptionsMaster.h +++ b/stepmania/src/ScreenOptionsMaster.h @@ -3,6 +3,7 @@ #include "ScreenOptions.h" #include "GameCommand.h" +#include "LuaReference.h" struct ConfOption; @@ -17,6 +18,7 @@ private: enum OptionRowType { ROW_LIST, /* list of custom settings */ + ROW_LUA, /* lua tells us what to do */ ROW_CONFIG, /* global pref */ NUM_OPTION_ROW_TYPES }; @@ -32,6 +34,9 @@ private: GameCommand Default; bool m_bUseModNameForIcon; + /* ROW_LUA: */ + LuaExpression m_LuaTable; + /* ROW_CONFIG: */ const ConfOption *opt; }; @@ -46,6 +51,7 @@ private: int ExportOptionForAllPlayers( int iRow ); static void SetList( OptionRowDefinition &row, OptionRowHandler &hand, CString param ); + static void SetLua( OptionRowDefinition &row, OptionRowHandler &hand, const CString &sLuaFunction ); static void SetSteps( OptionRowDefinition &row, OptionRowHandler &hand ); static void SetConf( OptionRowDefinition &row, OptionRowHandler &hand, CString param ); static void SetCharacters( OptionRowDefinition &row, OptionRowHandler &hand );