From 24f87d2e57f7c35376379e178446907b077b2afe Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 2 May 2014 16:45:49 -0600 Subject: [PATCH 1/2] Fixed stack acciddent bug in PlayerOptions lua functions. Added skew and tilt mods to PlayerOptions::GetMods and FromOneModString for when the perspective isn't one of the old ones. --- src/PlayerOptions.cpp | 45 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp index 5564e83071..b5eeadb339 100644 --- a/src/PlayerOptions.cpp +++ b/src/PlayerOptions.cpp @@ -260,6 +260,11 @@ void PlayerOptions::GetMods( vector &AddTo, bool bForceNoteSkin ) const { AddPart( AddTo, m_fSkew, "Incoming" ); } + else + { + AddPart( AddTo, m_fSkew, "Skew" ); + AddPart( AddTo, m_fPerspectiveTilt, "Tilt" ); + } // Don't display a string if using the default NoteSkin unless we force it. if( bForceNoteSkin || (!m_sNoteSkin.empty() && m_sNoteSkin != CommonMetrics::DEFAULT_NOTESKIN_NAME.GetValue()) ) @@ -461,6 +466,8 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut else if( sBit == "hallway" ) { m_fSkew = 0; m_fPerspectiveTilt = -level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } else if( sBit == "distant" ) { m_fSkew = 0; m_fPerspectiveTilt = +level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } else if( NOTESKIN && NOTESKIN->DoesNoteSkinExist(sBit) ) m_sNoteSkin = sBit; + else if( sBit == "skew" ) SET_FLOAT( fSkew ) + else if( sBit == "tilt" ) SET_FLOAT( fPerspectiveTilt ) else if( sBit == "noteskin" && !on ) /* "no noteskin" */ m_sNoteSkin = CommonMetrics::DEFAULT_NOTESKIN_NAME; else if( sBit == "randomspeed" ) SET_FLOAT( fRandomSpeed ) else if( sBit == "failarcade" || @@ -925,24 +932,39 @@ public: static float CheckedApproachSpeed(lua_State* L, float s) { - if(s <= 0) + if(s < 0) { - luaL_error(L, "Approach speed must be greater than zero."); + luaL_error(L, "Approach speed must be greater than or equal to zero."); } return s; } + // Explicitly separates the stack into args and return values. + // This way, the stack can safely be used to store the previous values. + static void DefaultNilArgs(lua_State* L, int n) + { + while(lua_gettop(L) < n) + { + lua_pushnil(L); + } + } + // 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. #define FLOAT_INTERFACE(func_name, member) \ static int func_name(T* p, lua_State* L) \ { \ + DefaultNilArgs(L, 2); \ lua_pushnumber(L, p->m_f ## member); \ lua_pushnumber(L, p->m_Speedf ## member); \ if(lua_isnumber(L, 1)) \ { \ p->m_f ## member = FArg(1); \ + if(p->m_Speedf ## member <= 0.0f) \ + { \ + p->m_Speedf ## member = 1.0f; \ + } \ } \ if(lua_isnumber(L, 2)) \ { \ @@ -953,6 +975,7 @@ public: #define BOOL_INTERFACE(func_name, member) \ static int func_name(T* p, lua_State* L) \ { \ + DefaultNilArgs(L, 1); \ lua_pushboolean(L, p->m_b ## member); \ if(lua_isboolean(L, 1)) \ { \ @@ -1047,6 +1070,7 @@ public: // NoteSkins static int NoteSkin(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if( p->m_sNoteSkin.empty() ) { lua_pushstring( L, CommonMetrics::DEFAULT_NOTESKIN_NAME.GetValue() ); @@ -1062,12 +1086,21 @@ public: p->m_sNoteSkin = SArg(1); lua_pushboolean(L, true); } + else + { + lua_pushnil(L); + } + } + else + { + lua_pushnil(L); } return 2; } static int FailSetting(T* p, lua_State* L) { + DefaultNilArgs(L, 1); Enum::Push(L, p->m_FailType); if(lua_isstring(L, 1)) { @@ -1089,6 +1122,7 @@ public: // engine's enforcement of sane separation between speed mod types. static int CMod(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if(p->m_fTimeSpacing) { lua_pushnumber(L, p->m_fScrollBPM); @@ -1120,6 +1154,7 @@ public: static int XMod(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if(!p->m_fTimeSpacing) { lua_pushnumber(L, p->m_fScrollSpeed); @@ -1146,6 +1181,7 @@ public: static int MMod(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if(!p->m_fTimeSpacing && p->m_fMaxScrollBPM) { lua_pushnumber(L, p->m_fMaxScrollBPM); @@ -1183,6 +1219,7 @@ public: static int Overhead(T* p, lua_State* L) { + DefaultNilArgs(L, 2); lua_pushboolean(L, (p->m_fPerspectiveTilt == 0.0f && p->m_fSkew == 0.0f)); if(lua_toboolean(L, 1)) { @@ -1198,6 +1235,7 @@ public: static int Incoming(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if((p->m_fSkew > 0.0f && p->m_fPerspectiveTilt < 0.0f) || (p->m_fSkew < 0.0f && p->m_fPerspectiveTilt > 0.0f)) { @@ -1224,6 +1262,7 @@ public: static int Space(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if((p->m_fSkew > 0.0f && p->m_fPerspectiveTilt > 0.0f) || (p->m_fSkew < 0.0f && p->m_fPerspectiveTilt < 0.0f)) { @@ -1250,6 +1289,7 @@ public: static int Hallway(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if(p->m_fSkew == 0.0f && p->m_fPerspectiveTilt < 0.0f) { lua_pushnumber(L, -p->m_fPerspectiveTilt); @@ -1274,6 +1314,7 @@ public: static int Distant(T* p, lua_State* L) { + DefaultNilArgs(L, 2); if(p->m_fSkew == 0.0f && p->m_fPerspectiveTilt > 0.0f) { lua_pushnumber(L, p->m_fPerspectiveTilt); From b6b840187defee4fc61f79ff33bc6423990595a8 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 2 May 2014 21:25:59 -0600 Subject: [PATCH 2/2] Made PlayerState:GetCurrentPlayerOptions safe to use. Added Docs/Themerdocs/modslevel.txt to explain what ModsLevel should be used by a theme using the new PlayerOptions interface. --- Docs/Themerdocs/modslevel.txt | 12 ++++++++++++ src/PlayerState.cpp | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 Docs/Themerdocs/modslevel.txt diff --git a/Docs/Themerdocs/modslevel.txt b/Docs/Themerdocs/modslevel.txt new file mode 100644 index 0000000000..f95a1124ff --- /dev/null +++ b/Docs/Themerdocs/modslevel.txt @@ -0,0 +1,12 @@ +PlayerOptions are arranged by ModsLevel. +ModsLevel_Preferred, // user-chosen player options. Does not include any forced mods. +ModsLevel_Stage, // Preferred + forced stage mods +ModsLevel_Song, // Stage + forced attack mods +ModsLevel_Current, // Approaches Song + +The following applies to modifying the player options through the object fetched from PlayerState:GetPlayerOptions(mods_level). + +Changes to ModsLevel_Current on ScreenGameplay take effect immediately, but are quickly nullified as Current tweens toward Song. The approach speeds in Current are ignored. Changing Current when not on ScreenGameplay will have no effect. +Changes to ModsLevel_Song while on ScreenGameplay take effect over time, governed by an approach speed. ModsLevel_Song should only be modified by the theme on ScreenGameplay, because it is reset to the contents of Preferred when ScreenGameplay starts. +ModsLevel_Stage is a holding area the engine uses when building the active attacks a course or song forces on a player. It should not be used by a theme. +ModsLevel_Preferred is the user-chosen player options. Use this if you are building a custom replacement for ScreenPlayerOptions. Changes to ModsLevel_Preferred are applied to Stage, Song, and Current when ScreenGameplay starts. Approach speeds in ModsLevel_Preferred are ignored. diff --git a/src/PlayerState.cpp b/src/PlayerState.cpp index 19be5e40ec..bae87951e6 100644 --- a/src/PlayerState.cpp +++ b/src/PlayerState.cpp @@ -252,8 +252,7 @@ public: } static int GetCurrentPlayerOptions( T* p, lua_State *L ) { - PlayerOptions po = p->m_PlayerOptions.GetCurrent(); - po.PushSelf(L); + p->m_PlayerOptions.GetCurrent().PushSelf(L); return 1; } DEFINE_METHOD( GetHealthState, m_HealthState );