diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 9026869eef..a0725f1321 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -6,6 +6,9 @@ ________________________________________________________________________________ 2015/02/02 ---------- +* [ActorMultiVertex] ForceStateUpdate and SetDecodeMovie added. [kyzentun] +* [RageTexture] GetWidth and Height functions added. + ( means Source, Texture, or Image) [kyzentun] * [Scripts] find_missing_strings_in_theme_translations function added to _fallback scripts to assist translators in finding what needs to be translated. If you need/want to run it on your them, write a piece of lua @@ -15,6 +18,12 @@ ________________________________________________________________________________ The second arg is the name of the language that is fully translated. All other languages will be compared to it to decide what is missing or unused. [kyzentun] +* [Song] GetBGChanges added. [kyzentun] + +2015/02/01 +---------- +* [ActorMultiVertex] Animation state system added. AMVs can now have + animated textures controlled by states written in lua. [kyzentun] 2015/01/31 ---------- diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index dca57e952c..8379721827 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -493,22 +493,41 @@ + + + + + + + + + + + + + + + + + + + @@ -1369,6 +1388,12 @@ + + + + + + @@ -1492,6 +1517,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 641a206a9b..0024206366 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -1531,6 +1531,69 @@ save yourself some time, copy this for undocumented things: + + The list of quad states is used to determine which animation state is used for each quad. The offset is added to the AMV's current state, and the resulting state is used. + + + Adds an animation state to the ActorMultiVertex. The state_data table must be like this:
+ {{left, top, right, bottom}, delay}
+ left, top, right, and bottom are pixel coordinates, starting at 0. If delay is 0 or negative, the state will last forever. +
+ + Forces the AMV to update the texture coordinates on all its quads, even if the current state has not changed. + + + Returns whether the AMV uses the animation state. + + + Sets whether the AMV uses the animation state.
+ This works best when using DrawMode_Quads.
+ AMV's can have animated textures like sprites. Each state tells the AMV what part of the texture to use, and how long the state lasts.
+ Use AddState to add a state onto the end, or SetStateProperties to set all the states at once, or SetState to set a single state.
+ Each quad has its own offset that is added to the current state. Use AddQuadState to add to the list of quad states, or SetQuadState to set an existing quad state. +
+ + Returns the number of states the AMV has. + + + Returns the number of quad states in the destination tween state for the AMV. + + + Returns the id of the current state. + + + Sets whether the AMV should call the decode function for its texture during updates. + + + Sets the current state. + + + Returns the offset of the requested quad state. + + + Sets the offset of the requested quad state. + + + Returns a table containing the data for the requested state. + + + Sets the requested state to the data in state_data. Similar to AddState, but SetStateData only works on states that have already been added. + + + Each element of the table must be a state_data table, and is used to construct one state. The table as a whole is the entire list of all states for the AMV. + + + Removes the requested state from the state list. + + + Removes the requested quad state from the quad state list. + + + Sets the delay for every state to delay. + + + Sets how far into its animation the AMV is. + Sets vertex number index with the properties provided. The tables of properties are each optional and can be provided in any order. @@ -4001,6 +4064,24 @@ save yourself some time, copy this for undocumented things:
+ + Returns the source width. + + + Returns the source height. + + + Returns the texture width. + + + Returns the texture height. + + + Returns the image width. + + + Returns the image height. + Returns the number of frames in this texture. @@ -4309,6 +4390,11 @@ save yourself some time, copy this for undocumented things: Returns the path to the song's banner. + + Returns a table with all the data for the song's BGCHANGES line.
+ Each element of the table is one change like this:
+ {start_beat= 1.0, rate= 1.0, transition= "example", effect= "example", file1= "example", file2= "example", color1= "#FFFFFFFF", color2= "#FFFFFFFF"} +
Returns the path to the song's CD image. diff --git a/src/ActorMultiVertex.cpp b/src/ActorMultiVertex.cpp index b3c1102311..4420007683 100644 --- a/src/ActorMultiVertex.cpp +++ b/src/ActorMultiVertex.cpp @@ -15,6 +15,8 @@ #include "LuaManager.h" #include "LocalizedString.h" +const float min_state_delay= 0.0001f; + static const char *DrawModeNames[] = { "Quads", "QuadStrip", @@ -69,6 +71,11 @@ ActorMultiVertex::ActorMultiVertex() _splines[i].redimension(3); _splines[i].m_owned_by_actor= true; } + _skip_next_update= true; + _decode_movie= true; + _use_animation_state= false; + _secs_into_state= 0.0f; + _cur_state= 0; } ActorMultiVertex::~ActorMultiVertex() @@ -86,6 +93,11 @@ ActorMultiVertex::ActorMultiVertex( const ActorMultiVertex &cpy ): CPY( _EffectMode ); CPY( _TextureMode ); CPY( _splines ); + CPY(_skip_next_update); + CPY(_use_animation_state); + CPY(_secs_into_state); + CPY(_cur_state); + CPY(_states); #undef CPY if( cpy._Texture != NULL ) @@ -373,6 +385,199 @@ CubicSplineN* ActorMultiVertex::GetSpline(size_t i) return &(_splines[i]); } +void ActorMultiVertex::SetState(size_t i) +{ + ASSERT(i < _states.size()); + _cur_state= i; + _secs_into_state= 0.0f; +} + +void ActorMultiVertex::SetAllStateDelays(float delay) +{ + FOREACH(State, _states, s) + { + s->delay= delay; + } +} + +float ActorMultiVertex::GetAnimationLengthSeconds() const +{ + float tot= 0.0f; + FOREACH_CONST(State, _states, s) + { + tot+= s->delay; + } + return tot; +} + +void ActorMultiVertex::SetSecondsIntoAnimation(float seconds) +{ + SetState(0); + if(_Texture) + { + _Texture->SetPosition(seconds); + } + _secs_into_state= seconds; + UpdateAnimationState(true); +} + +void ActorMultiVertex::UpdateAnimationState(bool force_update) +{ + AMV_TweenState& dest= AMV_DestTweenState(); + vector& verts= dest.vertices; + vector& qs= dest.quad_states; + if(!_use_animation_state || _states.empty() || + dest._DrawMode == DrawMode_LineStrip || qs.empty()) + { return; } + bool state_changed= force_update; + if(_states.size() > 1) + { + while(_states[_cur_state].delay > min_state_delay && + _secs_into_state + min_state_delay > _states[_cur_state].delay) + { + _secs_into_state-= _states[_cur_state].delay; + _cur_state= (_cur_state + 1) % _states.size(); + state_changed= true; + } + } + if(state_changed) + { + size_t first= dest.FirstToDraw; + size_t last= first+dest.GetSafeNumToDraw(dest._DrawMode, dest.NumToDraw); +#define STATE_ID const size_t state_id= (_cur_state + qs[quad_id % qs.size()]) % _states.size(); + switch(AMV_DestTweenState()._DrawMode) + { + case DrawMode_Quads: + for(size_t i= first; i < last; ++i) + { + const size_t quad_id= (i-first)/4; + STATE_ID; + switch((i-first)%4) + { + case 0: + verts[i].t.x= _states[state_id].rect.left; + verts[i].t.y= _states[state_id].rect.top; + break; + case 1: + verts[i].t.x= _states[state_id].rect.right; + verts[i].t.y= _states[state_id].rect.top; + break; + case 2: + verts[i].t.x= _states[state_id].rect.right; + verts[i].t.y= _states[state_id].rect.bottom; + break; + case 3: + verts[i].t.x= _states[state_id].rect.left; + verts[i].t.y= _states[state_id].rect.bottom; + break; + } + } + break; + case DrawMode_QuadStrip: + for(size_t i= first; i < last; ++i) + { + const size_t quad_id= (i-first)/2; + STATE_ID; + switch((i-first)%2) + { + case 0: + verts[i].t.x= _states[state_id].rect.left; + verts[i].t.y= _states[state_id].rect.top; + break; + case 1: + verts[i].t.x= _states[state_id].rect.left; + verts[i].t.y= _states[state_id].rect.bottom; + break; + } + } + break; + case DrawMode_Strip: + case DrawMode_Fan: + for(size_t i= first; i < last; ++i) + { + const size_t quad_id= (i-first); + STATE_ID; + verts[i].t.x= _states[state_id].rect.left; + verts[i].t.y= _states[state_id].rect.top; + } + break; + case DrawMode_Triangles: + for(size_t i= first; i < last; ++i) + { + const size_t quad_id= (i-first)/3; + STATE_ID; + switch((i-first)%3) + { + case 0: + verts[i].t.x= _states[state_id].rect.left; + verts[i].t.y= _states[state_id].rect.top; + break; + case 1: + verts[i].t.x= _states[state_id].rect.right; + verts[i].t.y= _states[state_id].rect.top; + break; + case 2: + verts[i].t.x= _states[state_id].rect.right; + verts[i].t.y= _states[state_id].rect.bottom; + break; + } + } + break; + case DrawMode_SymmetricQuadStrip: + for(size_t i= first; i < last; ++i) + { + const size_t quad_id= (i-first)/3; + STATE_ID; + switch((i-first)%3) + { + case 0: + case 2: + verts[i].t.x= _states[state_id].rect.left; + verts[i].t.y= _states[state_id].rect.top; + break; + case 1: + verts[i].t.x= _states[state_id].rect.right; + verts[i].t.y= _states[state_id].rect.top; + break; + } + } + break; + } + } +#undef STATE_ID +} + +void ActorMultiVertex::EnableAnimation(bool bEnable) +{ + bool bWasEnabled = m_bIsAnimating; + Actor::EnableAnimation(bEnable); + + if(bEnable && !bWasEnabled) + { + _skip_next_update = true; + } +} + +void ActorMultiVertex::Update(float fDelta) +{ + Actor::Update(fDelta); // do tweening + const bool skip_this_movie_update= _skip_next_update; + _skip_next_update= false; + if(!m_bIsAnimating) { return; } + if(!_Texture) { return; } + float time_passed = GetEffectDeltaTime(); + _secs_into_state += time_passed; + if(_secs_into_state < 0) + { + wrap(_secs_into_state, GetAnimationLengthSeconds()); + } + UpdateAnimationState(); + if(!skip_this_movie_update && _decode_movie) + { + _Texture->DecodeSeconds(max(0, time_passed)); + } +} + void ActorMultiVertex::SetCurrentTweenStart() { AMV_start= AMV_current; @@ -693,11 +898,14 @@ public: if( lua_isnil(L, 1) ) { p->UnloadTexture(); + p->LoadFromTexture(TEXTUREMAN->GetDefaultTextureID()); } else { RageTextureID ID( SArg(1) ); + TEXTUREMAN->DisableOddDimensionWarning(); p->LoadFromTexture( ID ); + TEXTUREMAN->EnableOddDimensionWarning(); } COMMON_RETURN_SELF; } @@ -719,6 +927,199 @@ public: COMMON_RETURN_SELF; } + DEFINE_METHOD(GetUseAnimationState, _use_animation_state); + static int SetUseAnimationState(T* p, lua_State *L) + { + p->_use_animation_state= BArg(1); + COMMON_RETURN_SELF; + } + static int GetNumStates(T* p, lua_State *L) + { + lua_pushnumber(L, p->GetNumStates()); + return 1; + } + static void FillStateFromLua(lua_State *L, ActorMultiVertex::State& state, + RageTexture* tex, int index) + { + if(tex == NULL) + { + luaL_error(L, "The texture must be set before adding states."); + } + // State looks like this: + // {{left, top, right, bottom}, delay} +#define DATA_ERROR(i) \ + if(!lua_istable(L, i)) \ + { \ + luaL_error(L, "The state data must be in a table like this: {{left, top, right, bottom}, delay}"); \ + } +#define SET_SIDE(i, side) \ + lua_rawgeti(L, -1, i); \ + state.side= FArg(-1); \ + lua_pop(L, 1); + + DATA_ERROR(index); + lua_rawgeti(L, index, 1); + DATA_ERROR(-1); + SET_SIDE(1, rect.left); + SET_SIDE(2, rect.top); + SET_SIDE(3, rect.right); + SET_SIDE(4, rect.bottom); + lua_pop(L, 1); + SET_SIDE(2, delay); + const float width_ratio= tex->GetImageToTexCoordsRatioX(); + const float height_ratio= tex->GetImageToTexCoordsRatioY(); + state.rect.left= state.rect.left * width_ratio; + state.rect.top= state.rect.top * height_ratio; + // Pixel centers are at .5, so add an extra pixel to the size to adjust. + state.rect.right= (state.rect.right * width_ratio) + width_ratio; + state.rect.bottom= (state.rect.bottom * height_ratio) + height_ratio; +#undef SET_SIDE +#undef DATA_ERROR + } + static int AddState(T* p, lua_State *L) + { + ActorMultiVertex::State s; + FillStateFromLua(L, s, p->GetTexture(), 1); + p->AddState(s); + COMMON_RETURN_SELF; + } + static size_t ValidStateIndex(T* p, lua_State *L, int pos) + { + int index= IArg(pos)-1; + if(index < 0 || static_cast(index) >= p->GetNumStates()) + { + luaL_error(L, "Invalid state index %d.", index+1); + } + return static_cast(index); + } + static int RemoveState(T* p, lua_State *L) + { + p->RemoveState(ValidStateIndex(p, L, 1)); + COMMON_RETURN_SELF; + } + static int GetState(T* p, lua_State *L) + { + lua_pushnumber(L, p->GetState()+1); + return 1; + } + static int SetState(T* p, lua_State *L) + { + p->SetState(ValidStateIndex(p, L, 1)); + COMMON_RETURN_SELF; + } + static int GetStateData(T* p, lua_State *L) + { + RageTexture* tex= p->GetTexture(); + if(tex == NULL) + { + luaL_error(L, "The texture must be set before adding states."); + } + const float width_pix= tex->GetImageToTexCoordsRatioX(); + const float height_pix= tex->GetImageToTexCoordsRatioY(); + const float width_ratio= 1.0f / tex->GetImageToTexCoordsRatioX(); + const float height_ratio= 1.0f / tex->GetImageToTexCoordsRatioY(); + const ActorMultiVertex::State& state= + p->GetStateData(ValidStateIndex(p, L, 1)); + lua_createtable(L, 2, 0); + lua_createtable(L, 4, 0); + lua_pushnumber(L, state.rect.left * width_ratio); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, state.rect.top * height_ratio); + lua_rawseti(L, -2, 2); + lua_pushnumber(L, (state.rect.right - width_pix) * width_ratio); + lua_rawseti(L, -2, 3); + lua_pushnumber(L, (state.rect.bottom + height_pix) * height_ratio); + lua_rawseti(L, -2, 4); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, state.delay); + lua_rawseti(L, -2, 2); + return 1; + } + static int SetStateData(T* p, lua_State *L) + { + ActorMultiVertex::State& state= p->GetStateData(ValidStateIndex(p, L, 1)); + FillStateFromLua(L, state, p->GetTexture(), 2); + COMMON_RETURN_SELF; + } + static int SetStateProperties(T* p, lua_State *L) + { + if(!lua_istable(L, 1)) + { + luaL_error(L, "The states must be inside a table."); + } + RageTexture* tex= p->GetTexture(); + if(tex == NULL) + { + luaL_error(L, "The texture must be set before adding states."); + } + vector new_states; + size_t num_states= lua_objlen(L, 1); + new_states.resize(num_states); + for(size_t i= 0; i < num_states; ++i) + { + lua_rawgeti(L, 1, i+1); + FillStateFromLua(L, new_states[i], tex, -1); + lua_pop(L, 1); + } + p->SetStateProperties(new_states); + COMMON_RETURN_SELF; + } + static int SetAllStateDelays(T* p, lua_State *L) + { + p->SetAllStateDelays(FArg(1)); + COMMON_RETURN_SELF; + } + DEFINE_METHOD(GetAnimationLengthSeconds, GetAnimationLengthSeconds()); + static int SetSecondsIntoAnimation(T* p, lua_State *L) + { + p->SetSecondsIntoAnimation(FArg(1)); + COMMON_RETURN_SELF; + } + static int GetNumQuadStates(T* p, lua_State *L) + { + lua_pushnumber(L, p->GetNumQuadStates()); + return 1; + } + static size_t QuadStateIndex(T* p, lua_State *L, int pos) + { + int index= IArg(pos)-1; + if(index < 0 || static_cast(index) >= p->GetNumQuadStates()) + { + luaL_error(L, "Invalid state index %d.", index+1); + } + return static_cast(index); + } + static int AddQuadState(T* p, lua_State *L) + { + p->AddQuadState(IArg(1)-1); + COMMON_RETURN_SELF; + } + static int RemoveQuadState(T* p, lua_State *L) + { + p->RemoveQuadState(QuadStateIndex(p, L, 1)); + COMMON_RETURN_SELF; + } + static int GetQuadState(T* p, lua_State *L) + { + lua_pushnumber(L, p->GetQuadState(QuadStateIndex(p, L, 1))+1); + return 1; + } + static int SetQuadState(T* p, lua_State *L) + { + p->SetQuadState(QuadStateIndex(p, L, 1), IArg(2)-1); + COMMON_RETURN_SELF; + } + static int ForceStateUpdate(T* p, lua_State *L) + { + p->UpdateAnimationState(true); + COMMON_RETURN_SELF; + } + static int SetDecodeMovie(T* p, lua_State *L) + { + p->_decode_movie= BArg(1); + COMMON_RETURN_SELF; + } + static int SetTexture( T* p, lua_State *L ) { RageTexture *Texture = Luna::check(L, 1); @@ -763,6 +1164,26 @@ public: ADD_METHOD( GetSpline ); ADD_METHOD( SetVertsFromSplines ); + ADD_METHOD(GetUseAnimationState); + ADD_METHOD(SetUseAnimationState); + ADD_METHOD(GetNumStates); + ADD_METHOD(GetNumQuadStates); + ADD_METHOD(AddState); + ADD_METHOD(RemoveState); + ADD_METHOD(GetState); + ADD_METHOD(SetState); + ADD_METHOD(GetStateData); + ADD_METHOD(SetStateData); + ADD_METHOD(SetStateProperties); + ADD_METHOD(SetAllStateDelays); + ADD_METHOD(SetSecondsIntoAnimation); + ADD_METHOD(AddQuadState); + ADD_METHOD(RemoveQuadState); + ADD_METHOD(GetQuadState); + ADD_METHOD(SetQuadState); + ADD_METHOD(ForceStateUpdate); + ADD_METHOD(SetDecodeMovie); + // Copy from RageTexture ADD_METHOD( SetTexture ); ADD_METHOD( GetTexture ); diff --git a/src/ActorMultiVertex.h b/src/ActorMultiVertex.h index 5b598cb310..2c4f949aec 100644 --- a/src/ActorMultiVertex.h +++ b/src/ActorMultiVertex.h @@ -38,7 +38,7 @@ public: struct AMV_TweenState { - AMV_TweenState(): _DrawMode(DrawMode_Invalid), FirstToDraw(0), + AMV_TweenState(): _DrawMode(DrawMode_Invalid), FirstToDraw(0), NumToDraw(-1), line_width(1.0f) {} static void MakeWeightedAverage(AMV_TweenState& average_out, const AMV_TweenState& ts1, const AMV_TweenState& ts2, float percent_between); @@ -49,6 +49,7 @@ public: int GetSafeNumToDraw( DrawMode dm, int num ) const; vector vertices; + vector quad_states; DrawMode _DrawMode; int FirstToDraw; @@ -67,6 +68,8 @@ public: } const AMV_TweenState& AMV_DestTweenState() const { return const_cast(this)->AMV_DestTweenState(); } + virtual void EnableAnimation(bool bEnable); + virtual void Update(float fDelta); virtual bool EarlyAbortDraw() const; virtual void DrawPrimitives(); virtual void DrawInternal( const AMV_TweenState *TS ); @@ -111,6 +114,40 @@ public: void SetVertsFromSplines(); CubicSplineN* GetSpline(size_t i); + struct State + { + RectF rect; + float delay; + }; + int GetNumStates() const { return _states.size(); } + void AddState(const State& new_state) { _states.push_back(new_state); } + void RemoveState(size_t i) + { ASSERT(i < _states.size()); _states.erase(_states.begin()+i); } + size_t GetState() { return _cur_state; } + State& GetStateData(size_t i) + { ASSERT(i < _states.size()); return _states[i]; } + void SetStateData(size_t i, const State& s) + { ASSERT(i < _states.size()); _states[i]= s; } + void SetStateProperties(const vector& new_states) + { _states= new_states; SetState(0); } + void SetState(size_t i); + void SetAllStateDelays(float delay); + float GetAnimationLengthSeconds() const; + void SetSecondsIntoAnimation(float seconds); + void UpdateAnimationState(bool force_update= false); + size_t GetNumQuadStates() const + { return AMV_DestTweenState().quad_states.size(); } + void AddQuadState(size_t s) + { AMV_DestTweenState().quad_states.push_back(s); } + void RemoveQuadState(size_t i) + { AMV_DestTweenState().quad_states.erase(AMV_DestTweenState().quad_states.begin()+i); } + size_t GetQuadState(size_t i) + { return AMV_DestTweenState().quad_states[i]; } + void SetQuadState(size_t i, size_t s) + { AMV_DestTweenState().quad_states[i]= s; } + bool _use_animation_state; + bool _decode_movie; + virtual void PushSelf( lua_State *L ); private: @@ -130,6 +167,11 @@ private: // Four splines for controlling vert positions, because quads drawmode // requires four. -Kyz vector _splines; + + bool _skip_next_update; + float _secs_into_state; + size_t _cur_state; + vector _states; }; /** diff --git a/src/RageTexture.cpp b/src/RageTexture.cpp index 210ee7997a..bdd02897e2 100644 --- a/src/RageTexture.cpp +++ b/src/RageTexture.cpp @@ -108,6 +108,12 @@ public: p->Reload(); COMMON_RETURN_SELF; } + DEFINE_METHOD(GetSourceWidth, GetSourceWidth()); + DEFINE_METHOD(GetSourceHeight, GetSourceHeight()); + DEFINE_METHOD(GetTextureWidth, GetTextureWidth()); + DEFINE_METHOD(GetTextureHeight, GetTextureHeight()); + DEFINE_METHOD(GetImageWidth, GetImageWidth()); + DEFINE_METHOD(GetImageHeight, GetImageHeight()); LunaRageTexture() { @@ -117,6 +123,12 @@ public: ADD_METHOD( GetTextureCoordRect ); ADD_METHOD( GetNumFrames ); ADD_METHOD( Reload ); + ADD_METHOD(GetSourceWidth); + ADD_METHOD(GetSourceHeight); + ADD_METHOD(GetTextureWidth); + ADD_METHOD(GetTextureHeight); + ADD_METHOD(GetImageWidth); + ADD_METHOD(GetImageHeight); } }; diff --git a/src/Song.cpp b/src/Song.cpp index 54f62fed82..1ccdf2c8f2 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -1916,6 +1916,33 @@ public: return 1; } static int GetTimingData( T* p, lua_State *L ) { p->m_SongTiming.PushSelf(L); return 1; } + static int GetBGChanges(T* p, lua_State* L) + { + const vector& changes= p->GetBackgroundChanges(BACKGROUND_LAYER_1); + lua_createtable(L, changes.size(), 0); + for(size_t c= 0; c < changes.size(); ++c) + { + lua_createtable(L, 0, 8); + lua_pushnumber(L, changes[c].m_fStartBeat); + lua_setfield(L, -2, "start_beat"); + lua_pushnumber(L, changes[c].m_fRate); + lua_setfield(L, -2, "rate"); + LuaHelpers::Push(L, changes[c].m_sTransition); + lua_setfield(L, -2, "transition"); + LuaHelpers::Push(L, changes[c].m_def.m_sEffect); + lua_setfield(L, -2, "effect"); + LuaHelpers::Push(L, changes[c].m_def.m_sFile1); + lua_setfield(L, -2, "file1"); + LuaHelpers::Push(L, changes[c].m_def.m_sFile2); + lua_setfield(L, -2, "file2"); + LuaHelpers::Push(L, changes[c].m_def.m_sColor1); + lua_setfield(L, -2, "color1"); + LuaHelpers::Push(L, changes[c].m_def.m_sColor2); + lua_setfield(L, -2, "color2"); + lua_rawseti(L, -2, c+1); + } + return 1; + } // has functions static int HasMusic( T* p, lua_State *L ) { lua_pushboolean(L, p->HasMusic()); return 1; } static int HasBanner( T* p, lua_State *L ) { lua_pushboolean(L, p->HasBanner()); return 1; } @@ -2053,6 +2080,7 @@ public: ADD_METHOD( HasStepsTypeAndDifficulty ); ADD_METHOD( GetOneSteps ); ADD_METHOD( GetTimingData ); + ADD_METHOD(GetBGChanges); ADD_METHOD( HasMusic ); ADD_METHOD( HasBanner ); ADD_METHOD( HasBackground ); diff --git a/src/Sprite.cpp b/src/Sprite.cpp index 307ec13e76..51d1b238da 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -186,11 +186,6 @@ void Sprite::LoadFromNode( const XNode* pNode ) { newState.fDelay = 0.1f; } - if(newState.fDelay <= min_state_delay) - { - LuaHelpers::ReportScriptErrorFmt("%s: State #%i has near-zero delay.", ActorUtil::GetWhere(pNode).c_str(), i+1); - newState.fDelay= 0.1f; - } pFrame->GetAttrValue( "Frame", iFrameIndex ); if( iFrameIndex >= m_pTexture->GetNumFrames() ) @@ -362,7 +357,12 @@ void Sprite::UpdateAnimationState() // We already know what's going to show. if( m_States.size() > 1 ) { - while( m_fSecsIntoState+min_state_delay > m_States[m_iCurState].fDelay ) // it's time to switch frames + // UpdateAnimationState changed to not loop forever on negative state + // delay. This allows a state to last forever when it is reached, so + // the animation has a built-in ending point. -Kyz + while(m_States[m_iCurState].fDelay > min_state_delay && + m_fSecsIntoState+min_state_delay > m_States[m_iCurState].fDelay) + // it's time to switch frames { // increment frame and reset the counter m_fSecsIntoState -= m_States[m_iCurState].fDelay; // leave the left over time for the next frame @@ -1047,15 +1047,6 @@ void Sprite::AddImageCoords( float fX, float fY ) class LunaSprite: public Luna { public: - static float valid_state_delay(lua_State* L, int i) - { - float delay= FArg(i); - if(delay <= min_state_delay) - { - luaL_error(L, "State delay cannot be less than or equal to zero."); - } - return delay; - } static int Load( T* p, lua_State *L ) { if( lua_isnil(L, 1) ) @@ -1151,7 +1142,7 @@ public: lua_getfield(L, -1, "Delay"); if(lua_isnumber(L, -1)) { - new_state.fDelay= valid_state_delay(L, -1); + new_state.fDelay= FArg(-1); } lua_pop(L, 1); RectF r= new_state.rect; @@ -1216,8 +1207,7 @@ public: static int GetNumStates( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumStates() ); return 1; } static int SetAllStateDelays( T* p, lua_State *L ) { - float delay= valid_state_delay(L, -1); - p->SetAllStateDelays(delay); + p->SetAllStateDelays(FArg(-1)); COMMON_RETURN_SELF; }