From 647b361284f3da60be88dacca4734f71ddf94c2a Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 30 Jan 2015 09:05:09 -0700 Subject: [PATCH] Added wrapper states to actors. Wrapping an actor inside a frame when building a screen is now unnecessary. --- Docs/Changelog_sm5.txt | 7 ++ Docs/Luadoc/Lua.xml | 4 + Docs/Luadoc/LuaDocumentation.xml | 14 +++ src/Actor.cpp | 177 +++++++++++++++++++++++++++---- src/Actor.h | 8 ++ src/ActorFrame.cpp | 6 ++ 6 files changed, 196 insertions(+), 20 deletions(-) diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 3da66d0f3f..5162c1bbd8 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,6 +4,13 @@ The StepMania 5 Changelog covers all post-sm-ssc changes. For a list of changes from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. ________________________________________________________________________________ +2015/01/30 +---------- +* [Actor] Wrapper states added. This makes wrapping an Actor inside an + ActorFrame for any reason unnecessary, and makes it possible to do things + that were only possible by wrapping an Actor in an ActorFrame to Actors + that you couldn't put inside a frame before. [kyzentun] + 2015/01/20 ---------- * [ScreenSelect] ChoiceNames can be a lua command that is evaluated to get a diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index beeca21152..dca57e952c 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -257,6 +257,7 @@ + @@ -278,6 +279,7 @@ + @@ -287,6 +289,7 @@ + @@ -301,6 +304,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 23eaa72ec0..641a206a9b 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -831,6 +831,20 @@ save yourself some time, copy this for undocumented things: + + This adds a wrapper state around the Actor, which is like wrapping the Actor in an ActorFrame, except that you can use it on any actor, and add or remove wrapper states in response to things that happen while the screen is being used. (wrapping an Actor in an ActorFrame normally requires setting it up before the screen starts)
+ The ActorFrame that is returned is the wrapper state, for convenience.
+ An Actor can have any number of wrapper states. Use GetWrapperState to access wrapper states for the actor. +
+ + Returns the number of wrapper states the actor has. + + + Returns the wrapper state at index i. Think of wrapper states with a higher index as being "further out". Actor is inside Wrapper 1, Wrapper 1 is inside Wrapper 2, Wrapper 2 is inside Wrapper 3, and so on. + + + Removes the wrapper state at index i. + Returns the Actor's parent, or nil if it doesn't have one. diff --git a/src/Actor.cpp b/src/Actor.cpp index 9dac9f0454..b9eebb33db 100644 --- a/src/Actor.cpp +++ b/src/Actor.cpp @@ -1,5 +1,6 @@ #include "global.h" #include "Actor.h" +#include "ActorFrame.h" #include "RageDisplay.h" #include "RageUtil.h" #include "RageMath.h" @@ -172,6 +173,11 @@ Actor::~Actor() { StopTweening(); UnsubscribeAll(); + for(size_t i= 0; i < m_WrapperStates.size(); ++i) + { + SAFE_DELETE(m_WrapperStates[i]); + } + m_WrapperStates.clear(); } Actor::Actor( const Actor &cpy ): @@ -187,6 +193,12 @@ Actor::Actor( const Actor &cpy ): CPY( m_FakeParent ); CPY( m_pLuaInstance ); + m_WrapperStates.resize(cpy.m_WrapperStates.size()); + for(size_t i= 0; i < m_WrapperStates.size(); ++i) + { + m_WrapperStates[i]= new ActorFrame(*dynamic_cast(cpy.m_WrapperStates[i])); + } + CPY( m_baseRotation ); CPY( m_baseScale ); CPY( m_fBaseAlpha ); @@ -296,7 +308,6 @@ void Actor::Draw() { return; // early abort } - bool fake_parent_partially_opaque= true; if(m_FakeParent) { if(!m_FakeParent->m_bVisible || m_FakeParent->m_fHibernateSecondsLeft > 0 @@ -305,31 +316,99 @@ void Actor::Draw() return; } m_FakeParent->PreDraw(); - fake_parent_partially_opaque= m_FakeParent->PartiallyOpaque(); - } - - this->PreDraw(); - ASSERT( m_pTempState != NULL ); - if(PartiallyOpaque() && fake_parent_partially_opaque) - { - if(m_FakeParent) + if(!m_FakeParent->PartiallyOpaque()) { - m_FakeParent->BeginDraw(); - } - // call the most-derived versions - this->BeginDraw(); - this->DrawPrimitives(); // call the most-derived version of DrawPrimitives(); - this->EndDraw(); - if(m_FakeParent) - { - m_FakeParent->EndDraw(); + m_FakeParent->PostDraw(); + return; } } - - this->PostDraw(); + if(m_FakeParent) { + m_FakeParent->BeginDraw(); + } + size_t wrapper_states_used= 0; + RageColor last_diffuse; + RageColor last_glow; + bool use_last_diffuse= false; + // dont_abort_draw exists because if one of the layers is invisible, + // there's no point in continuing. -Kyz + bool dont_abort_draw= true; + // abort_with_end_draw exists because PreDraw happens before the + // opaqueness test, so if we abort at the opaqueness test, there isn't + // a BeginDraw to match the EndDraw. -Kyz + bool abort_with_end_draw= true; + // It's more intuitive to apply the highest index wrappers first. + // On the lua side, it looks like this: + // wrapper[3] is the outermost frame. wrapper[2] is inside wrapper[3]. + // wrapper[1] is inside wrapper[2]. The actor is inside wrapper[1]. + // -Kyz + for(size_t i= m_WrapperStates.size(); i > 0 && dont_abort_draw; --i) + { + Actor* state= m_WrapperStates[i-1]; + if(!state->m_bVisible || state->m_fHibernateSecondsLeft > 0 || + state->EarlyAbortDraw()) + { + dont_abort_draw= false; + } + else + { + state->PreDraw(); + if(state->PartiallyOpaque()) + { + state->BeginDraw(); + last_diffuse= state->m_pTempState->diffuse[0]; + last_glow= state->m_pTempState->glow; + use_last_diffuse= true; + if(i > 1) + { + m_WrapperStates[i-2]->SetInternalDiffuse(last_diffuse); + m_WrapperStates[i-2]->SetInternalGlow(last_glow); + } + } + else + { + dont_abort_draw= false; + abort_with_end_draw= false; + } + ++wrapper_states_used; + } + } + // call the most-derived versions + if(dont_abort_draw) + { + if(use_last_diffuse) + { + this->SetInternalDiffuse(last_diffuse); + this->SetInternalGlow(last_glow); + } + this->PreDraw(); + ASSERT( m_pTempState != NULL ); + if(PartiallyOpaque()) + { + this->BeginDraw(); + this->DrawPrimitives(); + this->EndDraw(); + } + this->PostDraw(); + } + for(size_t i= 0; i < wrapper_states_used; ++i) + { + Actor* state= m_WrapperStates[i]; + if(abort_with_end_draw) + { + state->EndDraw(); + } + abort_with_end_draw= true; + state->PostDraw(); + state->m_pTempState= NULL; + } + + if(m_FakeParent) + { + m_FakeParent->EndDraw(); m_FakeParent->PostDraw(); + m_FakeParent->m_pTempState= NULL; } m_pTempState = NULL; } @@ -728,6 +807,10 @@ void Actor::Update( float fDeltaTime ) fDeltaTime = -m_fHibernateSecondsLeft; m_fHibernateSecondsLeft = 0; } + for(size_t i= 0; i < m_WrapperStates.size(); ++i) + { + m_WrapperStates[i]->Update(fDeltaTime); + } this->UpdateInternal( fDeltaTime ); } @@ -823,6 +906,26 @@ RString Actor::GetLineage() const return sPath; } +void Actor::AddWrapperState() +{ + ActorFrame* wrapper= new ActorFrame; + wrapper->InitState(); + m_WrapperStates.push_back(wrapper); +} + +void Actor::RemoveWrapperState(size_t i) +{ + ASSERT(i < m_WrapperStates.size()); + SAFE_DELETE(m_WrapperStates[i]); + m_WrapperStates.erase(m_WrapperStates.begin()+i); +} + +Actor* Actor::GetWrapperState(size_t i) +{ + ASSERT(i < m_WrapperStates.size()); + return m_WrapperStates[i]; +} + void Actor::BeginTweening( float time, ITween *pTween ) { ASSERT( time >= 0 ); @@ -1765,6 +1868,36 @@ public: } COMMON_RETURN_SELF; } + static int AddWrapperState(T* p, lua_State* L) + { + p->AddWrapperState(); + p->GetWrapperState(p->GetNumWrapperStates()-1)->PushSelf(L); + return 1; + } + static size_t get_state_index(T* p, lua_State* L, int stack_index) + { + // Lua is one indexed. + int i= IArg(stack_index)-1; + const size_t si= static_cast(i); + if(i < 0 || si >= p->GetNumWrapperStates()) + { + luaL_error(L, "%d is not a valid wrapper state index.", i+1); + } + return si; + } + static int RemoveWrapperState(T* p, lua_State* L) + { + size_t si= get_state_index(p, L, 1); + p->RemoveWrapperState(si); + COMMON_RETURN_SELF; + } + DEFINE_METHOD(GetNumWrapperStates, GetNumWrapperStates()); + static int GetWrapperState(T* p, lua_State* L) + { + size_t si= get_state_index(p, L, 1); + p->GetWrapperState(si)->PushSelf(L); + return 1; + } static int Draw( T* p, lua_State *L ) { LUA->YieldLua(); @@ -1938,6 +2071,10 @@ public: ADD_METHOD( GetParent ); ADD_METHOD( GetFakeParent ); ADD_METHOD( SetFakeParent ); + ADD_METHOD( AddWrapperState ); + ADD_METHOD( RemoveWrapperState ); + ADD_METHOD( GetNumWrapperStates ); + ADD_METHOD( GetWrapperState ); ADD_METHOD( Draw ); } diff --git a/src/Actor.h b/src/Actor.h index 7957306409..0acd10d96a 100644 --- a/src/Actor.h +++ b/src/Actor.h @@ -310,6 +310,11 @@ public: void SetFakeParent(Actor* mailman) { m_FakeParent= mailman; } Actor* GetFakeParent() { return m_FakeParent; } + void AddWrapperState(); + void RemoveWrapperState(size_t i); + Actor* GetWrapperState(size_t i); + size_t GetNumWrapperStates() const { return m_WrapperStates.size(); } + /** * @brief Retrieve the Actor's x position. * @return the Actor's x position. */ @@ -616,6 +621,9 @@ protected: // state without making that actor the parent. It's like having multiple // parents. -Kyz Actor* m_FakeParent; + // WrapperStates provides a way to wrap the actor inside ActorFrames, + // applicable to any actor, not just ones the theme creates. + vector m_WrapperStates; /** @brief Some general information about the Tween. */ struct TweenInfo diff --git a/src/ActorFrame.cpp b/src/ActorFrame.cpp index 2b9bc9c6f0..174fe1ba64 100644 --- a/src/ActorFrame.cpp +++ b/src/ActorFrame.cpp @@ -251,6 +251,12 @@ void ActorFrame::DrawPrimitives() RageColor diffuse = m_pTempState->diffuse[0]; RageColor glow = m_pTempState->glow; + // Word of warning: Actor::Draw duplicates the structure of how an Actor + // is drawn inside of an ActorFrame for its wrapping feature. So if + // you're adding something new to ActorFrames that affects how Actors are + // drawn, make sure to also apply it in Actor::Draw's handling of the + // wrappers. -Kyz + // draw all sub-ActorFrames while we're in the ActorFrame's local coordinate space if( m_bDrawByZPosition ) {