From 7a585fcd389c58d6eedc4dbe5fcaf79d675865f6 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Tue, 2 Dec 2014 19:58:52 -0700 Subject: [PATCH] Function chaining for PlayerOptions and SongOptions APIs. --- Docs/Luadoc/LuaDocumentation.xml | 9 ++++- src/OptionsBinding.h | 50 ++++++++++++++++--------- src/PlayerOptions.cpp | 64 ++++++++++++++++++-------------- 3 files changed, 78 insertions(+), 45 deletions(-) diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 2308132dd1..7105c657c3 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -3002,6 +3002,13 @@ save yourself some time, copy this for undocumented things: + All these functions have an optional last argument: If the last argument is the boolean value true, then instead of returning the previous settings as normal, they will instead return the PlayerOptions object.
+ This allows you to chain them like this:
+ player_options:Twirl(5, 1, true):Roll(5, true):Dizzy(true):Twirl()
+
+ Special note: Functions that take a bool as their arg must have true as the second arg to be used with chaining.
+ "player_options:Backwards(true, true):Beat(5)" will chain, "player_options:Backwards(true):Beat(5)" will not chain.
+
Most options fall into one of four types: float, int, bool, or enum.
Float type options have this interface:
Option(value, approach_speed)
@@ -3121,7 +3128,7 @@ save yourself some time, copy this for undocumented things: Changing the noteskin during a song is not supported.
Example:
note_name= options:NoteSkin() -- Sets note_name to the player's current noteskin.
- prev_note_name, succeeded= options:NoteSkin("cel") -- Sets prev_note_name to the noteskin the player had set, changes the current noteskin to "cel", sets succeeded to true if the the "cel" noteskin exists.
+ prev_note_name, succeeded= options:NoteSkin("cel") -- Sets prev_note_name to the noteskin the player had set, changes the current noteskin to "cel", sets succeeded to true if the "cel" noteskin exists.
If the player is using Overhead (0 tilt, 0 skew), returns true.
diff --git a/src/OptionsBinding.h b/src/OptionsBinding.h index d3db401c9b..52c0d9b676 100644 --- a/src/OptionsBinding.h +++ b/src/OptionsBinding.h @@ -8,13 +8,21 @@ // Functions are designed to combine Get and Set into one, to be less clumsy to use. -Kyz // If a valid arg is passed, the value is set. // The previous value is returned. +// OPTIONAL_RETURN_SELF exists to provide optional chaining support. +// Example: local a= player_options:Twirl(5, 1, true):Roll(5, true):Dizzy(true):Twirl() +#define OPTIONAL_RETURN_SELF(option_index) \ + if(lua_isboolean(L, option_index) && lua_toboolean(L, option_index)) \ + { \ + p->PushSelf(L); \ + return 1; \ + } #define FLOAT_INTERFACE(func_name, member, valid) \ static int func_name(T* p, lua_State* L) \ { \ - DefaultNilArgs(L, 2); \ + int original_top= lua_gettop(L); \ lua_pushnumber(L, p->m_f ## member); \ lua_pushnumber(L, p->m_Speedf ## member); \ - if(lua_isnumber(L, 1)) \ + if(lua_isnumber(L, 1) && original_top >= 1) \ { \ float v= FArg(1); \ if(!valid) \ @@ -22,23 +30,20 @@ luaL_error(L, "Invalid value %f", v); \ } \ p->m_f ## member = v; \ - if(p->m_Speedf ## member <= 0.0f) \ - { \ - p->m_Speedf ## member = 1.0f; \ - } \ } \ - if(lua_isnumber(L, 2)) \ + if(original_top >= 2 && lua_isnumber(L, 2)) \ { \ p->m_Speedf ## member = FArgGTEZero(L, 2); \ } \ + OPTIONAL_RETURN_SELF(original_top); \ return 2; \ } #define FLOAT_NO_SPEED_INTERFACE(func_name, member, valid) \ static int func_name(T* p, lua_State* L) \ { \ - DefaultNilArgs(L, 1); \ + int original_top= lua_gettop(L); \ lua_pushnumber(L, p->m_f ## member); \ - if(lua_isnumber(L, 1)) \ + if(lua_isnumber(L, 1) && original_top >= 1) \ { \ float v= FArg(1); \ if(!valid) \ @@ -47,53 +52,63 @@ } \ p->m_f ## member = v; \ } \ + OPTIONAL_RETURN_SELF(original_top); \ return 1; \ } #define INT_INTERFACE(func_name, member) \ static int func_name(T* p, lua_State* L) \ { \ - DefaultNilArgs(L, 1); \ + int original_top= lua_gettop(L); \ lua_pushnumber(L, p->m_ ## member); \ - if(lua_isnumber(L, 1)) \ + if(lua_isnumber(L, 1) && original_top >= 1) \ { \ p->m_ ## member = IArg(1); \ } \ + OPTIONAL_RETURN_SELF(original_top); \ return 1; \ } +// BOOL_INTERFACE can't use OPTIONAL_RETURN_SELF because it pushes a bool. +// If it did original_top, then "foo(true)" would chain when it shouldn't. #define BOOL_INTERFACE(func_name, member) \ static int func_name(T* p, lua_State* L) \ { \ - DefaultNilArgs(L, 1); \ + int original_top= lua_gettop(L); \ lua_pushboolean(L, p->m_b ## member); \ - if(lua_isboolean(L, 1)) \ + if(lua_isboolean(L, 1) && original_top >= 1) \ { \ p->m_b ## member = BArg(1); \ } \ + if(original_top >= 2 && lua_isboolean(L, 2)) \ + { \ + p->PushSelf(L); \ + return 1; \ + } \ return 1; \ } #define ENUM_INTERFACE(func_name, member, enum_name) \ static int func_name(T* p, lua_State* L) \ { \ - DefaultNilArgs(L, 1); \ + int original_top= lua_gettop(L); \ Enum::Push(L, p->m_ ## member); \ - if(!lua_isnil(L, 1)) \ + if(!lua_isnil(L, 1) && original_top >= 1) \ { \ p->m_ ## member= Enum::Check(L, 1); \ } \ + OPTIONAL_RETURN_SELF(original_top); \ return 1; \ } // Walk the table to make sure all entries are valid before setting. #define FLOAT_TABLE_INTERFACE(func_name, member, valid) \ static int func_name(T* p, lua_State* L) \ { \ - DefaultNilArgs(L, 1); \ + int original_top= lua_gettop(L); \ lua_createtable(L, p->m_ ## member.size(), 0); \ for(size_t n= 0; n < p->m_ ## member.size(); ++n) \ { \ lua_pushnumber(L, p->m_ ## member[n]); \ lua_rawseti(L, -2, n+1); \ } \ - if(lua_istable(L, 1)) \ + if(lua_istable(L, 1) && original_top >= 1) \ { \ size_t size= lua_objlen(L, 1); \ if(valid(L, 1)) \ @@ -110,6 +125,7 @@ } \ } \ } \ + OPTIONAL_RETURN_SELF(original_top); \ return 1; \ } diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp index fa6fb0bed1..3bc3cc68a5 100644 --- a/src/PlayerOptions.cpp +++ b/src/PlayerOptions.cpp @@ -1086,7 +1086,7 @@ public: // NoteSkins static int NoteSkin(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if( p->m_sNoteSkin.empty() ) { lua_pushstring( L, CommonMetrics::DEFAULT_NOTESKIN_NAME.GetValue() ); @@ -1095,11 +1095,12 @@ public: { lua_pushstring( L, p->m_sNoteSkin ); } - if(lua_isstring(L, 1)) + if(original_top >= 1 && lua_isstring(L, 1)) { - if(NOTESKIN->DoesNoteSkinExist(SArg(1))) + RString skin= SArg(1); + if(NOTESKIN->DoesNoteSkinExist(skin)) { - p->m_sNoteSkin = SArg(1); + p->m_sNoteSkin = skin; lua_pushboolean(L, true); } else @@ -1111,6 +1112,7 @@ public: { lua_pushnil(L); } + OPTIONAL_RETURN_SELF(original_top); return 2; } @@ -1127,7 +1129,7 @@ public: // engine's enforcement of sane separation between speed mod types. static int CMod(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if(p->m_fTimeSpacing) { lua_pushnumber(L, p->m_fScrollBPM); @@ -1138,7 +1140,7 @@ public: lua_pushnil(L); lua_pushnil(L); } - if(lua_isnumber(L, 1)) + if(original_top >= 1 && lua_isnumber(L, 1)) { float speed= FArg(1); if(!isfinite(speed) || speed <= 0.0f) @@ -1150,16 +1152,17 @@ public: p->m_fScrollSpeed = 1; p->m_fMaxScrollBPM = 0; } - if(lua_isnumber(L, 2)) + if(original_top >= 2 && lua_isnumber(L, 2)) { SetSpeedModApproaches(p, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 2; } static int XMod(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if(!p->m_fTimeSpacing) { lua_pushnumber(L, p->m_fScrollSpeed); @@ -1170,23 +1173,24 @@ public: lua_pushnil(L); lua_pushnil(L); } - if(lua_isnumber(L, 1)) + if(lua_isnumber(L, 1) && original_top >= 1) { p->m_fScrollSpeed = FArg(1); p->m_fTimeSpacing = 0; p->m_fScrollBPM= CMOD_DEFAULT; p->m_fMaxScrollBPM = 0; } - if(lua_isnumber(L, 2)) + if(lua_isnumber(L, 2) && original_top >= 2) { SetSpeedModApproaches(p, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 2; } static int MMod(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if(!p->m_fTimeSpacing && p->m_fMaxScrollBPM) { lua_pushnumber(L, p->m_fMaxScrollBPM); @@ -1197,7 +1201,7 @@ public: lua_pushnil(L); lua_pushnil(L); } - if(lua_isnumber(L, 1)) + if(lua_isnumber(L, 1) && original_top >= 1) { float speed= FArg(1); if(!isfinite(speed) || speed <= 0.0f) @@ -1209,10 +1213,11 @@ public: p->m_fScrollSpeed= 1; p->m_fMaxScrollBPM = speed; } - if(lua_isnumber(L, 2)) + if(lua_isnumber(L, 2) && original_top >= 2) { SetSpeedModApproaches(p, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 2; } @@ -1224,23 +1229,24 @@ public: static int Overhead(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); lua_pushboolean(L, (p->m_fPerspectiveTilt == 0.0f && p->m_fSkew == 0.0f)); if(lua_toboolean(L, 1)) { p->m_fPerspectiveTilt= 0; p->m_fSkew= 0; } - if(lua_isnumber(L, 2)) + if(lua_isnumber(L, 2) && original_top >= 2) { SetPerspectiveApproach(p, L, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 1; } static int Incoming(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if((p->m_fSkew > 0.0f && p->m_fPerspectiveTilt < 0.0f) || (p->m_fSkew < 0.0f && p->m_fPerspectiveTilt > 0.0f)) { @@ -1252,22 +1258,23 @@ public: lua_pushnil(L); lua_pushnil(L); } - if(lua_isnumber(L, 1)) + if(lua_isnumber(L, 1) && original_top >= 1) { float value= FArg(1); p->m_fPerspectiveTilt= -value; p->m_fSkew= value; } - if(lua_isnumber(L, 2)) + if(lua_isnumber(L, 2) && original_top >= 2) { SetPerspectiveApproach(p, L, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 2; } static int Space(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if((p->m_fSkew > 0.0f && p->m_fPerspectiveTilt > 0.0f) || (p->m_fSkew < 0.0f && p->m_fPerspectiveTilt < 0.0f)) { @@ -1279,22 +1286,23 @@ public: lua_pushnil(L); lua_pushnil(L); } - if(lua_isnumber(L, 1)) + if(lua_isnumber(L, 1) && original_top >= 1) { float value= FArg(1); p->m_fPerspectiveTilt= value; p->m_fSkew= value; } - if(lua_isnumber(L, 2)) + if(lua_isnumber(L, 2) && original_top >= 2) { SetPerspectiveApproach(p, L, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 2; } static int Hallway(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if(p->m_fSkew == 0.0f && p->m_fPerspectiveTilt < 0.0f) { lua_pushnumber(L, -p->m_fPerspectiveTilt); @@ -1305,21 +1313,22 @@ public: lua_pushnil(L); lua_pushnil(L); } - if(lua_isnumber(L, 1)) + if(lua_isnumber(L, 1) && original_top >= 1) { p->m_fPerspectiveTilt= -FArg(1); p->m_fSkew= 0; } - if(lua_isnumber(L, 2)) + if(lua_isnumber(L, 2) && original_top >= 2) { SetPerspectiveApproach(p, L, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 2; } static int Distant(T* p, lua_State* L) { - DefaultNilArgs(L, 2); + int original_top= lua_gettop(L); if(p->m_fSkew == 0.0f && p->m_fPerspectiveTilt > 0.0f) { lua_pushnumber(L, p->m_fPerspectiveTilt); @@ -1330,15 +1339,16 @@ public: lua_pushnil(L); lua_pushnil(L); } - if(lua_isnumber(L, 1)) + if(lua_isnumber(L, 1) && original_top >= 1) { p->m_fPerspectiveTilt= FArg(1); p->m_fSkew= 0; } - if(lua_isnumber(L, 2)) + if(lua_isnumber(L, 2) && original_top >= 2) { SetPerspectiveApproach(p, L, FArgGTEZero(L, 2)); } + OPTIONAL_RETURN_SELF(original_top); return 2; }