From 0b1de9ba878b3ca61c99d5ef6d0cb45fb7baf61d Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Fri, 21 Mar 2014 21:11:19 -0400 Subject: [PATCH 01/40] Correct my mistake from 83df0ad3d6ac. ...3+ years too late! This should compile in all systems due to NotesLoaderJson inclusion. --- extern/jsoncpp/src/lib_json/json_reader.cpp | 4 ++-- extern/jsoncpp/src/lib_json/json_value.cpp | 4 ++-- extern/jsoncpp/src/lib_json/json_writer.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extern/jsoncpp/src/lib_json/json_reader.cpp b/extern/jsoncpp/src/lib_json/json_reader.cpp index 86cafcafaa..3e79ab733e 100644 --- a/extern/jsoncpp/src/lib_json/json_reader.cpp +++ b/extern/jsoncpp/src/lib_json/json_reader.cpp @@ -1,5 +1,5 @@ -#include <../extern/jsoncpp/include/json/reader.h> -#include <../extern/jsoncpp/include/json/value.h> +#include "json/reader.h" +#include "json/value.h" #include #include #include diff --git a/extern/jsoncpp/src/lib_json/json_value.cpp b/extern/jsoncpp/src/lib_json/json_value.cpp index fb480daa4a..1ecacd45d0 100644 --- a/extern/jsoncpp/src/lib_json/json_value.cpp +++ b/extern/jsoncpp/src/lib_json/json_value.cpp @@ -1,6 +1,6 @@ #include -#include <../extern/jsoncpp/include/json/value.h> -#include <../extern/jsoncpp/include/json/writer.h> +#include "json/value.h" +#include "json/writer.h" #include #include #include diff --git a/extern/jsoncpp/src/lib_json/json_writer.cpp b/extern/jsoncpp/src/lib_json/json_writer.cpp index 7868a22cbf..41373250bb 100644 --- a/extern/jsoncpp/src/lib_json/json_writer.cpp +++ b/extern/jsoncpp/src/lib_json/json_writer.cpp @@ -1,4 +1,4 @@ -#include <../extern/jsoncpp/include/json/writer.h> +#include "json/writer.h" #include #include #include From 9738ba9b20865b0f41861d8ccf11d5897bdc664c Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Fri, 21 Mar 2014 21:22:25 -0400 Subject: [PATCH 02/40] Move JSON to separate project (2011) --- extern/jsoncpp/libjson-net2011.vcxproj | 88 +++++++++++++++++++ .../jsoncpp/libjson-net2011.vcxproj.filters | 8 ++ src/StepMania-net2011.vcxproj | 33 +------ src/StepMania-net2011.vcxproj.filters | 15 ---- src/sm5-net2011.sln | 10 +++ 5 files changed, 109 insertions(+), 45 deletions(-) create mode 100644 extern/jsoncpp/libjson-net2011.vcxproj create mode 100644 extern/jsoncpp/libjson-net2011.vcxproj.filters diff --git a/extern/jsoncpp/libjson-net2011.vcxproj b/extern/jsoncpp/libjson-net2011.vcxproj new file mode 100644 index 0000000000..8d4c2eb738 --- /dev/null +++ b/extern/jsoncpp/libjson-net2011.vcxproj @@ -0,0 +1,88 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0DCAA088-FC09-4422-892B-8E89F1F798CB} + libjson + libjson + + + + StaticLibrary + true + v110 + MultiByte + + + StaticLibrary + false + v110 + true + MultiByte + + + + + + + + + + + + + $(IntDir) + $(SolutionDir)/build-$(SolutionName)/$(ProjectName)/$(Configuration)\ + + + $(IntDir) + $(SolutionDir)/build-$(SolutionName)/$(ProjectName)/$(Configuration)\ + + + + Level3 + Disabled + true + + + ./include + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + ./include + + + true + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/extern/jsoncpp/libjson-net2011.vcxproj.filters b/extern/jsoncpp/libjson-net2011.vcxproj.filters new file mode 100644 index 0000000000..23fe16e111 --- /dev/null +++ b/extern/jsoncpp/libjson-net2011.vcxproj.filters @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/StepMania-net2011.vcxproj b/src/StepMania-net2011.vcxproj index 846feec821..2a974356c5 100644 --- a/src/StepMania-net2011.vcxproj +++ b/src/StepMania-net2011.vcxproj @@ -1222,36 +1222,6 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1914,6 +1884,9 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)" {9522f2e6-ffeb-4620-9ee2-79353a55671b} + + {0dcaa088-fc09-4422-892b-8e89f1f798cb} + {046c857a-41c5-4e87-ad44-462ab17bbf4f} diff --git a/src/StepMania-net2011.vcxproj.filters b/src/StepMania-net2011.vcxproj.filters index 9b43a92ff6..f791d86e40 100644 --- a/src/StepMania-net2011.vcxproj.filters +++ b/src/StepMania-net2011.vcxproj.filters @@ -97,12 +97,6 @@ {2d157d5c-7c0e-4967-925a-3976a6ad0dd0} - - {46a5ab77-6eaf-44eb-93ec-43b2db4741ff} - - - {52c8e19c-32ec-4115-bee6-f5957389d7ea} - @@ -1443,15 +1437,6 @@ BaseClasses - - Helpers\jsoncpp - - - Helpers\jsoncpp - - - Helpers\jsoncpp - Data Structures diff --git a/src/sm5-net2011.sln b/src/sm5-net2011.sln index c7498d19cc..b3c11814db 100644 --- a/src/sm5-net2011.sln +++ b/src/sm5-net2011.sln @@ -15,6 +15,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libglew", "..\extern\glew-1 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblua", "..\extern\lua-5.1\liblua-net2011.vcxproj", "{0555A743-D202-409C-A331-349FEE971025}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libjson", "..\extern\jsoncpp\libjson-net2011.vcxproj", "{0DCAA088-FC09-4422-892B-8E89F1F798CB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -79,6 +81,14 @@ Global {0555A743-D202-409C-A331-349FEE971025}.Release|Win32.Build.0 = Release|Win32 {0555A743-D202-409C-A331-349FEE971025}.Release-SSE2|Win32.ActiveCfg = Release|Win32 {0555A743-D202-409C-A331-349FEE971025}.Release-SSE2|Win32.Build.0 = Release|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.Debug|Win32.ActiveCfg = Debug|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.Debug|Win32.Build.0 = Debug|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.FastDebug|Win32.ActiveCfg = Debug|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.FastDebug|Win32.Build.0 = Debug|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.Release|Win32.ActiveCfg = Release|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.Release|Win32.Build.0 = Release|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.Release-SSE2|Win32.ActiveCfg = Release|Win32 + {0DCAA088-FC09-4422-892B-8E89F1F798CB}.Release-SSE2|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From f230729a6ffa97f25f123fc0525eba13646371d2 Mon Sep 17 00:00:00 2001 From: Flameshadowxeroshin Date: Fri, 23 Jan 2015 10:25:06 -0600 Subject: [PATCH 03/40] unsigned int combo and score, Player::IncrementComboOrMissCombo --- src/HighScore.cpp | 12 ++--- src/HighScore.h | 8 ++-- src/Inventory.cpp | 6 +-- src/Inventory.h | 2 +- src/LuaManager.cpp | 1 + src/Player.cpp | 97 ++++++++++++++++++++------------------- src/Player.h | 10 ++-- src/PlayerStageStats.h | 12 ++--- src/ScoreKeeperNormal.cpp | 12 ++--- src/ScreenGameplay.cpp | 2 +- src/StageStats.cpp | 4 +- src/StageStats.h | 2 +- 12 files changed, 86 insertions(+), 82 deletions(-) diff --git a/src/HighScore.cpp b/src/HighScore.cpp index e70bf6a701..cc9e17c0c6 100644 --- a/src/HighScore.cpp +++ b/src/HighScore.cpp @@ -17,10 +17,10 @@ struct HighScoreImpl { RString sName; // name that shows in the machine's ranking screen Grade grade; - int iScore; + unsigned int iScore; float fPercentDP; float fSurviveSeconds; - int iMaxCombo; // maximum combo obtained [SM5 alpha 1a+] + unsigned int iMaxCombo; // maximum combo obtained [SM5 alpha 1a+] StageAward stageAward; // stage award [SM5 alpha 1a+] PeakComboAward peakComboAward; // peak combo award [SM5 alpha 1a+] RString sModifiers; @@ -191,8 +191,8 @@ bool HighScore::IsEmpty() const RString HighScore::GetName() const { return m_Impl->sName; } Grade HighScore::GetGrade() const { return m_Impl->grade; } -int HighScore::GetScore() const { return m_Impl->iScore; } -int HighScore::GetMaxCombo() const { return m_Impl->iMaxCombo; } +unsigned int HighScore::GetScore() const { return m_Impl->iScore; } +unsigned int HighScore::GetMaxCombo() const { return m_Impl->iMaxCombo; } StageAward HighScore::GetStageAward() const { return m_Impl->stageAward; } PeakComboAward HighScore::GetPeakComboAward() const { return m_Impl->peakComboAward; } float HighScore::GetPercentDP() const { return m_Impl->fPercentDP; } @@ -211,8 +211,8 @@ bool HighScore::GetDisqualified() const { return m_Impl->bDisqualified; } void HighScore::SetName( const RString &sName ) { m_Impl->sName = sName; } void HighScore::SetGrade( Grade g ) { m_Impl->grade = g; } -void HighScore::SetScore( int iScore ) { m_Impl->iScore = iScore; } -void HighScore::SetMaxCombo( int i ) { m_Impl->iMaxCombo = i; } +void HighScore::SetScore( unsigned int iScore ) { m_Impl->iScore = iScore; } +void HighScore::SetMaxCombo( unsigned int i ) { m_Impl->iMaxCombo = i; } void HighScore::SetStageAward( StageAward a ) { m_Impl->stageAward = a; } void HighScore::SetPeakComboAward( PeakComboAward a ) { m_Impl->peakComboAward = a; } void HighScore::SetPercentDP( float f ) { m_Impl->fPercentDP = f; } diff --git a/src/HighScore.h b/src/HighScore.h index c3e8ed86bc..568ad191ea 100644 --- a/src/HighScore.h +++ b/src/HighScore.h @@ -30,7 +30,7 @@ struct HighScore /** * @brief Retrieve the score earned. * @return the score. */ - int GetScore() const; + unsigned int GetScore() const; /** * @brief Determine if any judgments were tallied during this run. * @return true if no judgments were recorded, false otherwise. */ @@ -41,7 +41,7 @@ struct HighScore * @return the number of seconds left. */ float GetSurviveSeconds() const; float GetSurvivalSeconds() const; - int GetMaxCombo() const; + unsigned int GetMaxCombo() const; StageAward GetStageAward() const; PeakComboAward GetPeakComboAward() const; /** @@ -66,10 +66,10 @@ struct HighScore * @param sName the name of the Player. */ void SetName( const RString &sName ); void SetGrade( Grade g ); - void SetScore( int iScore ); + void SetScore( unsigned int iScore ); void SetPercentDP( float f ); void SetAliveSeconds( float f ); - void SetMaxCombo( int i ); + void SetMaxCombo( unsigned int i ); void SetStageAward( StageAward a ); void SetPeakComboAward( PeakComboAward a ); void SetModifiers( RString s ); diff --git a/src/Inventory.cpp b/src/Inventory.cpp index 5124d15c33..aabb821bad 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -25,7 +25,7 @@ ThemeMetric ITEM_USE_RATE_SECONDS("Inventory","ItemUseRateSeconds"); struct Item { AttackLevel level; - int iCombo; + unsigned int iCombo; RString sModifier; }; static vector g_Items; @@ -100,9 +100,9 @@ void Inventory::Update( float fDelta ) // check to see if they deserve a new item if( STATSMAN->m_CurStageStats.m_player[pn].m_iCurCombo != m_iLastSeenCombo ) { - int iOldCombo = m_iLastSeenCombo; + unsigned int iOldCombo = m_iLastSeenCombo; m_iLastSeenCombo = STATSMAN->m_CurStageStats.m_player[pn].m_iCurCombo; - int iNewCombo = m_iLastSeenCombo; + unsigned int iNewCombo = m_iLastSeenCombo; #define CROSSED(i) (iOldCombo=i) #define BROKE_ABOVE(i) (iNewCombo=i) diff --git a/src/Inventory.h b/src/Inventory.h index 5ad36dfe71..93712c3e17 100644 --- a/src/Inventory.h +++ b/src/Inventory.h @@ -28,7 +28,7 @@ protected: void AwardItem( int iItemIndex ); PlayerState* m_pPlayerState; - int m_iLastSeenCombo; + unsigned int m_iLastSeenCombo; /** @brief a sound played when an item has been acquired. */ RageSound m_soundAcquireItem; diff --git a/src/LuaManager.cpp b/src/LuaManager.cpp index 3e703474fb..116e9d8d1b 100644 --- a/src/LuaManager.cpp +++ b/src/LuaManager.cpp @@ -81,6 +81,7 @@ namespace LuaHelpers template<> void Push( lua_State *L, const bool &Object ) { lua_pushboolean( L, Object ); } template<> void Push( lua_State *L, const float &Object ) { lua_pushnumber( L, Object ); } template<> void Push( lua_State *L, const int &Object ) { lua_pushinteger( L, Object ); } + template<> void Push( lua_State *L, const unsigned int &Object ) { lua_pushnumber( L, double(Object) ); } template<> void Push( lua_State *L, const RString &Object ) { lua_pushlstring( L, Object.data(), Object.size() ); } template<> bool FromStack( Lua *L, bool &Object, int iOffset ) { Object = !!lua_toboolean( L, iOffset ); return true; } diff --git a/src/Player.cpp b/src/Player.cpp index 6c8a3bfc56..83634f4191 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -371,6 +371,7 @@ void Player::Init( m_pSecondaryScoreKeeper = pSecondaryScoreKeeper; m_iLastSeenCombo = -1; + m_bSeenComboYet = false; // set initial life if( m_pLifeMeter && m_pPlayerStageStats ) @@ -763,10 +764,10 @@ void Player::Load() m_pIterUnjudgedMineRows = new NoteData::all_tracks_iterator( m_NoteData.GetTapNoteRangeAllTracks(iNoteRow, MAX_NOTE_ROW ) ); } -void Player::SendComboMessages( int iOldCombo, int iOldMissCombo ) +void Player::SendComboMessages( unsigned int iOldCombo, unsigned int iOldMissCombo ) { - const int iCurCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; - if( iOldCombo > COMBO_STOPPED_AT && iCurCombo < COMBO_STOPPED_AT ) + const unsigned int iCurCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; + if( iOldCombo > (unsigned int)COMBO_STOPPED_AT && iCurCombo < (unsigned int)COMBO_STOPPED_AT ) { SCREENMAN->PostMessageToTopScreen( SM_ComboStopped, 0 ); } @@ -1410,7 +1411,7 @@ void Player::UpdateHoldNotes( int iSongRow, float fDeltaTime, vectorTrace("initiated note and didn't let go"); fLife = 1; // xxx: should be MAX_HOLD_LIFE instead? -aj hns = HNS_Held; - bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo>(int)BRIGHT_GHOST_COMBO_THRESHOLD; + bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo>(unsigned int)BRIGHT_GHOST_COMBO_THRESHOLD; if( m_pNoteField ) { FOREACH( TrackRowTapNote, vTN, trtn ) @@ -1462,19 +1463,7 @@ void Player::UpdateHoldNotes( int iSongRow, float fDeltaTime, vectorm_iCurCombo : 0; - const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; - - if( m_pPlayerStageStats ) - { - m_pPlayerStageStats->m_iCurCombo = 0; - m_pPlayerStageStats->m_iCurMissCombo++; - SetCombo( m_pPlayerStageStats->m_iCurCombo, m_pPlayerStageStats->m_iCurMissCombo ); - } - - SendComboMessages( iOldCombo, iOldMissCombo ); - } + IncrementMissCombo(); if( hns != HNS_None ) { @@ -1918,8 +1907,8 @@ void Player::DoTapScoreNone() Message msg( "ScoreNone" ); MESSAGEMAN->Broadcast( msg ); - const int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; - const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; + const unsigned int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; + const unsigned int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; /* The only real way to tell if a mine has been scored is if it has disappeared * but this only works for hit mines so update the scores for avoided mines here. */ @@ -2105,21 +2094,9 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b if( ROLL_BODY_INCREMENTS_COMBO && m_pPlayerState->m_PlayerController != PC_AUTOPLAY ) { - // increment combo - const int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; - const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; - - if( m_pPlayerStageStats ) - { - m_pPlayerStageStats->m_iCurCombo++; - m_pPlayerStageStats->m_iCurMissCombo = 0; - } - - SendComboMessages( iOldCombo, iOldMissCombo ); - if( m_pPlayerStageStats ) - SetCombo( m_pPlayerStageStats->m_iCurCombo, m_pPlayerStageStats->m_iCurMissCombo ); - - bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo>(int)BRIGHT_GHOST_COMBO_THRESHOLD; + IncrementCombo(); + + bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo>(unsigned int)BRIGHT_GHOST_COMBO_THRESHOLD; if( m_pNoteField ) m_pNoteField->DidHoldNote( col, HNS_Held, bBright ); } @@ -2576,7 +2553,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b const bool bBlind = (m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind != 0); // XXX: This is the wrong combo for shared players. // STATSMAN->m_CurStageStats.m_Player[pn] might work, but could be wrong. - const bool bBright = ( m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo > int(BRIGHT_GHOST_COMBO_THRESHOLD) ) || bBlind; + const bool bBright = ( m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo > (unsigned int)BRIGHT_GHOST_COMBO_THRESHOLD ) || bBlind; if( m_pNoteField ) m_pNoteField->DidTapNote( col, bBlind? TNS_W1:score, bBright ); if( score >= m_pPlayerState->m_PlayerOptions.GetCurrent().m_MinTNSToHideNotes || bBlind ) @@ -2915,7 +2892,7 @@ void Player::FlashGhostRow( int iRow ) { TapNoteScore lastTNS = NoteDataWithScoring::LastTapNoteWithResult( m_NoteData, iRow ).result.tns; const bool bBlind = (m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind != 0); - const bool bBright = ( m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo > int(BRIGHT_GHOST_COMBO_THRESHOLD) ) || bBlind; + const bool bBright = ( m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo > (unsigned int)BRIGHT_GHOST_COMBO_THRESHOLD ) || bBlind; for( int iTrack = 0; iTrack < m_NoteData.GetNumTracks(); ++iTrack ) { @@ -3104,8 +3081,8 @@ void Player::HandleTapRowScore( unsigned row ) return; TapNoteScore scoreOfLastTap = NoteDataWithScoring::LastTapNoteWithResult(m_NoteData, row).result.tns; - const int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; - const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; + const unsigned int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; + const unsigned int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; if( scoreOfLastTap == TNS_Miss ) m_LastTapNoteScore = TNS_Miss; @@ -3130,8 +3107,8 @@ void Player::HandleTapRowScore( unsigned row ) if( m_pSecondaryScoreKeeper != NULL ) m_pSecondaryScoreKeeper->HandleTapRowScore( m_NoteData, row ); - const int iCurCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; - const int iCurMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; + const unsigned int iCurCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; + const unsigned int iCurMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; SendComboMessages( iOldCombo, iOldMissCombo ); @@ -3211,8 +3188,8 @@ void Player::HandleHoldCheckpoint(int iRow, if( bNoCheating && m_pPlayerState->m_PlayerController == PC_AUTOPLAY ) return; - const int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; - const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; + const unsigned int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; + const unsigned int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; if( m_pPrimaryScoreKeeper ) m_pPrimaryScoreKeeper->HandleHoldCheckpointScore(m_NoteData, @@ -3233,7 +3210,7 @@ void Player::HandleHoldCheckpoint(int iRow, FOREACH_CONST( int, viColsWithHold, i ) { bool bBright = m_pPlayerStageStats - && m_pPlayerStageStats->m_iCurCombo>(int)BRIGHT_GHOST_COMBO_THRESHOLD; + && m_pPlayerStageStats->m_iCurCombo>(unsigned int)BRIGHT_GHOST_COMBO_THRESHOLD; if( m_pNoteField ) m_pNoteField->DidHoldNote( *i, HNS_Held, bBright ); } @@ -3402,17 +3379,20 @@ void Player::SetHoldJudgment( TapNote &tn, int iTrack ) } } -void Player::SetCombo( int iCombo, int iMisses ) +void Player::SetCombo( unsigned int iCombo, unsigned int iMisses ) { - if( m_iLastSeenCombo == -1 ) // first update, don't set bIsMilestone=true + if( !m_bSeenComboYet ) // first update, don't set bIsMilestone=true + { + m_bSeenComboYet = true; m_iLastSeenCombo = iCombo; - + } + bool b25Milestone = false; bool b50Milestone = false; bool b100Milestone = false; bool b250Milestone = false; bool b1000Milestone = false; - for( int i=m_iLastSeenCombo+1; i<=iCombo; i++ ) + for( unsigned int i=m_iLastSeenCombo+1; i<=iCombo; i++ ) { if( i < 600 ) { @@ -3484,6 +3464,29 @@ void Player::SetCombo( int iCombo, int iMisses ) } } +void Player::IncrementComboOrMissCombo(bool bComboOrMissCombo) +{ + const unsigned int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; + const unsigned int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; + + if( m_pPlayerStageStats ) + { + if( bComboOrMissCombo ) + { + m_pPlayerStageStats->m_iCurCombo++; + m_pPlayerStageStats->m_iCurMissCombo = 0; + } + else + { + m_pPlayerStageStats->m_iCurCombo = 0; + m_pPlayerStageStats->m_iCurMissCombo++; + } + SetCombo( m_pPlayerStageStats->m_iCurCombo, m_pPlayerStageStats->m_iCurMissCombo ); + } + + SendComboMessages( iOldCombo, iOldMissCombo ); +} + RString Player::ApplyRandomAttack() { if( GAMESTATE->m_RandomAttacks.size() < 1 ) diff --git a/src/Player.h b/src/Player.h index 9d75b98235..acdb82bed2 100644 --- a/src/Player.h +++ b/src/Player.h @@ -128,14 +128,17 @@ protected: void HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumHoldsMissedThisRow, const vector &viColsWithHold ); void DrawTapJudgments(); void DrawHoldJudgments(); - void SendComboMessages( int iOldCombo, int iOldMissCombo ); + void SendComboMessages( unsigned int iOldCombo, unsigned int iOldMissCombo ); void PlayKeysound( const TapNote &tn, TapNoteScore score ); void SetMineJudgment( TapNoteScore tns , int iTrack ); void SetJudgment( int iRow, int iFirstTrack, const TapNote &tn ) { SetJudgment( iRow, iFirstTrack, tn, tn.result.tns, tn.result.fTapNoteOffset ); } void SetJudgment( int iRow, int iFirstTrack, const TapNote &tn, TapNoteScore tns, float fTapNoteOffset ); // -1 if no track as in TNS_Miss void SetHoldJudgment( TapNote &tn, int iTrack ); - void SetCombo( int iCombo, int iMisses ); + void SetCombo( unsigned int iCombo, unsigned int iMisses ); + void IncrementComboOrMissCombo( bool bComboOrMissCombo ); + void IncrementCombo() { IncrementComboOrMissCombo(true); }; + void IncrementMissCombo() { IncrementComboOrMissCombo(false); }; void ChangeLife( TapNoteScore tns ); void ChangeLife( HoldNoteScore hns, TapNoteScore tns ); @@ -194,7 +197,8 @@ protected: NoteData::all_tracks_iterator *m_pIterUncrossedRows; NoteData::all_tracks_iterator *m_pIterUnjudgedRows; NoteData::all_tracks_iterator *m_pIterUnjudgedMineRows; - int m_iLastSeenCombo; + unsigned int m_iLastSeenCombo; + bool m_bSeenComboYet; JudgedRows *m_pJudgedRows; RageSound m_soundMine; diff --git a/src/PlayerStageStats.h b/src/PlayerStageStats.h index d0ef051395..53482124b5 100644 --- a/src/PlayerStageStats.h +++ b/src/PlayerStageStats.h @@ -63,18 +63,18 @@ public: int m_iTapNoteScores[NUM_TapNoteScore]; int m_iHoldNoteScores[NUM_HoldNoteScore]; /** @brief The Player's current combo. */ - int m_iCurCombo; + unsigned int m_iCurCombo; /** @brief The Player's max combo. */ - int m_iMaxCombo; + unsigned int m_iMaxCombo; /** @brief The Player's current miss combo. */ - int m_iCurMissCombo; + unsigned int m_iCurMissCombo; int m_iCurScoreMultiplier; /** @brief The player's current score. */ - int m_iScore; + unsigned int m_iScore; /** @brief The theoretically highest score the Player could have at this point. */ - int m_iCurMaxScore; + unsigned int m_iCurMaxScore; /** @brief The maximum score the Player can get this goaround. */ - int m_iMaxScore; + unsigned int m_iMaxScore; /** * @brief The possible RadarValues for a song. diff --git a/src/ScoreKeeperNormal.cpp b/src/ScoreKeeperNormal.cpp index 6cb4e42ec0..78286933cb 100644 --- a/src/ScoreKeeperNormal.cpp +++ b/src/ScoreKeeperNormal.cpp @@ -260,8 +260,8 @@ void ScoreKeeperNormal::AddScoreInternal( TapNoteScore score ) if( m_UseInternalScoring ) { - int &iScore = m_pPlayerStageStats->m_iScore; - int &iCurMaxScore = m_pPlayerStageStats->m_iCurMaxScore; + unsigned int &iScore = m_pPlayerStageStats->m_iScore; + unsigned int &iCurMaxScore = m_pPlayerStageStats->m_iCurMaxScore; // See Aaron In Japan for more details about the scoring formulas. // Note: this assumes no custom scoring systems are in use. @@ -277,8 +277,8 @@ void ScoreKeeperNormal::AddScoreInternal( TapNoteScore score ) m_iTapNotesHit++; - const int N = m_iNumTapsAndHolds; - const int sum = (N * (N + 1)) / 2; + const uint64_t N = uint64_t(m_iNumTapsAndHolds); + const int sum = int((N * (N + 1)) / 2); const int Z = m_iMaxPossiblePoints/10; // Don't use a multiplier if the player has failed @@ -320,15 +320,11 @@ void ScoreKeeperNormal::AddScoreInternal( TapNoteScore score ) iCurMaxScore += m_iPointBonus; } - ASSERT_M( iScore >= 0, "iScore < 0 before re-rounding" ); - // Undo rounding from the last tap, and re-round. iScore += m_iScoreRemainder; m_iScoreRemainder = (iScore % m_iRoundTo); iScore = iScore - m_iScoreRemainder; - ASSERT_M( iScore >= 0, "iScore < 0 after re-rounding" ); - // LOG->Trace( "score: %i", iScore ); } } diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index a821eb2aac..1a8bd1e974 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -1846,7 +1846,7 @@ void ScreenGameplay::Update( float fDeltaTime ) } if (bAllHumanHaveBigMissCombo) // possible to get in here. { - bAllHumanHaveBigMissCombo = FAIL_ON_MISS_COMBO.GetValue() != -1 && STATSMAN->m_CurStageStats.GetMinimumMissCombo() >= FAIL_ON_MISS_COMBO; + bAllHumanHaveBigMissCombo = FAIL_ON_MISS_COMBO.GetValue() != -1 && STATSMAN->m_CurStageStats.GetMinimumMissCombo() >= (unsigned int)FAIL_ON_MISS_COMBO; } if( bGiveUpTimerFired || bAllHumanHaveBigMissCombo ) { diff --git a/src/StageStats.cpp b/src/StageStats.cpp index 348b0c0ecb..8cd894129b 100644 --- a/src/StageStats.cpp +++ b/src/StageStats.cpp @@ -334,9 +334,9 @@ bool StageStats::PlayerHasHighScore( PlayerNumber pn ) const return false; } -int StageStats::GetMinimumMissCombo() const +unsigned int StageStats::GetMinimumMissCombo() const { - int iMin = INT_MAX; + unsigned int iMin = INT_MAX; FOREACH_HumanPlayer( p ) iMin = min( iMin, m_player[p].m_iCurMissCombo ); return iMin; diff --git a/src/StageStats.h b/src/StageStats.h index a5a52587ef..60db5b32cc 100644 --- a/src/StageStats.h +++ b/src/StageStats.h @@ -75,7 +75,7 @@ public: * @param pn the PlayerNumber in question. * @return true if the PlayerNumber has a high score, false otherwise. */ bool PlayerHasHighScore( PlayerNumber pn ) const; - int GetMinimumMissCombo() const; + unsigned int GetMinimumMissCombo() const; // Lua void PushSelf( lua_State *L ); From f8d8a3379f6c228682de8ca5093f3144f0518a5a Mon Sep 17 00:00:00 2001 From: Flameshadowxeroshin Date: Fri, 23 Jan 2015 15:58:41 -0600 Subject: [PATCH 04/40] improved checkpoint performance, one other change --- src/Player.cpp | 32 +++++++++++++++++++++----------- src/Player.h | 1 + 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Player.cpp b/src/Player.cpp index 83634f4191..e684c20ecb 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -370,8 +370,10 @@ void Player::Init( m_pPrimaryScoreKeeper = pPrimaryScoreKeeper; m_pSecondaryScoreKeeper = pSecondaryScoreKeeper; - m_iLastSeenCombo = -1; + m_iLastSeenCombo = 0; m_bSeenComboYet = false; + for( int i=0; i<5; i++ ) + m_iMilestoneCounters[i] = 0; // set initial life if( m_pLifeMeter && m_pPlayerStageStats ) @@ -3392,21 +3394,29 @@ void Player::SetCombo( unsigned int iCombo, unsigned int iMisses ) bool b100Milestone = false; bool b250Milestone = false; bool b1000Milestone = false; - for( unsigned int i=m_iLastSeenCombo+1; i<=iCombo; i++ ) + int iNewMilestoneCounters[5] = { + int(iCombo/25), + int(iCombo/50), + int(iCombo/100), + int(iCombo/250), + int(iCombo/200) + }; + //Don't do milestones right after combo overflow or if combo hasn't changed. + if( m_iLastSeenCombo <= iCombo ) { - if( i < 600 ) - { - b25Milestone |= ((i % 25) == 0); - b50Milestone |= ((i % 50) == 0); - b100Milestone |= ((i % 100) == 0); - b250Milestone |= ((i % 250) == 0); - } + if( iCombo > 600 ) + b1000Milestone = iNewMilestoneCounters[4] > m_iMilestoneCounters[4]; else { - b1000Milestone |= ((i % 200) == 0); + b25Milestone = iNewMilestoneCounters[0] > m_iMilestoneCounters[0]; + b50Milestone = iNewMilestoneCounters[1] > m_iMilestoneCounters[1]; + b100Milestone = iNewMilestoneCounters[2] > m_iMilestoneCounters[2]; + b250Milestone = iNewMilestoneCounters[3] > m_iMilestoneCounters[3]; } } - m_iLastSeenCombo = iCombo; + + for( int i=0; i<5; i++ ) + m_iMilestoneCounters[i] = iNewMilestoneCounters[i]; if( b25Milestone ) this->PlayCommand( "TwentyFiveMilestone"); diff --git a/src/Player.h b/src/Player.h index acdb82bed2..45159c767a 100644 --- a/src/Player.h +++ b/src/Player.h @@ -199,6 +199,7 @@ protected: NoteData::all_tracks_iterator *m_pIterUnjudgedMineRows; unsigned int m_iLastSeenCombo; bool m_bSeenComboYet; + int m_iMilestoneCounters[5]; JudgedRows *m_pJudgedRows; RageSound m_soundMine; From 06ee30d8b9ecbd472cbbcd729ef3ee49b60b7a5d Mon Sep 17 00:00:00 2001 From: Flameshadowxeroshin Date: Fri, 23 Jan 2015 21:30:06 -0600 Subject: [PATCH 05/40] Kyzentun's milestone code --- src/Player.cpp | 40 ++++++++++++++++------------------------ src/Player.h | 1 - 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/Player.cpp b/src/Player.cpp index e684c20ecb..1c10047c46 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -372,9 +372,7 @@ void Player::Init( m_iLastSeenCombo = 0; m_bSeenComboYet = false; - for( int i=0; i<5; i++ ) - m_iMilestoneCounters[i] = 0; - + // set initial life if( m_pLifeMeter && m_pPlayerStageStats ) { @@ -3394,29 +3392,23 @@ void Player::SetCombo( unsigned int iCombo, unsigned int iMisses ) bool b100Milestone = false; bool b250Milestone = false; bool b1000Milestone = false; - int iNewMilestoneCounters[5] = { - int(iCombo/25), - int(iCombo/50), - int(iCombo/100), - int(iCombo/250), - int(iCombo/200) - }; - //Don't do milestones right after combo overflow or if combo hasn't changed. - if( m_iLastSeenCombo <= iCombo ) + +#define MILESTONE_CHECK(amount) ((iCombo / amount) > (m_iLastSeenCombo / amount)) + if(m_iLastSeenCombo < 600) { - if( iCombo > 600 ) - b1000Milestone = iNewMilestoneCounters[4] > m_iMilestoneCounters[4]; - else - { - b25Milestone = iNewMilestoneCounters[0] > m_iMilestoneCounters[0]; - b50Milestone = iNewMilestoneCounters[1] > m_iMilestoneCounters[1]; - b100Milestone = iNewMilestoneCounters[2] > m_iMilestoneCounters[2]; - b250Milestone = iNewMilestoneCounters[3] > m_iMilestoneCounters[3]; - } + b25Milestone= MILESTONE_CHECK(25); + b50Milestone= MILESTONE_CHECK(50); + b100Milestone= MILESTONE_CHECK(100); + b250Milestone= MILESTONE_CHECK(250); + b1000Milestone= MILESTONE_CHECK(1000); } - - for( int i=0; i<5; i++ ) - m_iMilestoneCounters[i] = iNewMilestoneCounters[i]; + else + { + b1000Milestone= MILESTONE_CHECK(1000); + } +#undef MILESTONE_CHECK + + m_iLastSeenCombo = iCombo; if( b25Milestone ) this->PlayCommand( "TwentyFiveMilestone"); diff --git a/src/Player.h b/src/Player.h index 45159c767a..acdb82bed2 100644 --- a/src/Player.h +++ b/src/Player.h @@ -199,7 +199,6 @@ protected: NoteData::all_tracks_iterator *m_pIterUnjudgedMineRows; unsigned int m_iLastSeenCombo; bool m_bSeenComboYet; - int m_iMilestoneCounters[5]; JudgedRows *m_pJudgedRows; RageSound m_soundMine; From e86131844962c59da357f67f6323ab97b12638b5 Mon Sep 17 00:00:00 2001 From: Flameshadowxeroshin Date: Wed, 28 Jan 2015 10:20:50 -0600 Subject: [PATCH 06/40] I didn't change everything that needed to change --- src/ScoreKeeperNormal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ScoreKeeperNormal.cpp b/src/ScoreKeeperNormal.cpp index 78286933cb..a1e60f0990 100644 --- a/src/ScoreKeeperNormal.cpp +++ b/src/ScoreKeeperNormal.cpp @@ -198,7 +198,7 @@ void ScoreKeeperNormal::OnNextSong( int iSongInCourseIndex, const Steps* pSteps, GAMESTATE->SetProcessedTimingData(NULL); } -static int GetScore(int p, int Z, int S, int n) +static int GetScore(int p, int Z, int64_t S, int n) { /* There's a problem with the scoring system described below. Z/S is truncated * to an int. However, in some cases we can end up with very small base scores. @@ -277,8 +277,8 @@ void ScoreKeeperNormal::AddScoreInternal( TapNoteScore score ) m_iTapNotesHit++; - const uint64_t N = uint64_t(m_iNumTapsAndHolds); - const int sum = int((N * (N + 1)) / 2); + const int64_t N = uint64_t(m_iNumTapsAndHolds); + const int64_t sum = (N * (N + 1)) / 2; const int Z = m_iMaxPossiblePoints/10; // Don't use a multiplier if the player has failed From a733560e621f7bc139c6d729e6b276b99d66f5e7 Mon Sep 17 00:00:00 2001 From: "Ben \"root\" Anderson" Date: Wed, 28 Jan 2015 16:00:13 -0600 Subject: [PATCH 07/40] I assume this was a typo --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index bda964f867..99fe9ba226 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,7 +31,7 @@ AM_CXXFLAGS += $(GL_CFLAGS) .PHONY: increment_version gitversion increment_version: - if test -e ver.h; then \ + if test -e ver.cpp; then \ build=`sed -rs 's/.*version_num = ([[:digit:]]+);/\1/;q' ver.cpp`; \ build=`expr $$build + 1`; \ else \ From 0d394e269d299f214576e768f8f15c3da74657e7 Mon Sep 17 00:00:00 2001 From: "Ben \"root\" Anderson" Date: Wed, 28 Jan 2015 16:45:19 -0600 Subject: [PATCH 08/40] Declare version_* in only one place. Define them in ver.cpp (at least on autotools). --- src/CommandLineActions.cpp | 10 ++++------ src/Makefile.am | 6 +++--- src/NetworkSyncManager.cpp | 5 +++-- src/PrefsManager.cpp | 8 ++++---- src/StepMania.cpp | 9 ++++----- src/archutils/Unix/CrashHandlerChild.cpp | 4 +--- src/archutils/Win32/CrashHandlerChild.cpp | 5 ++--- 7 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/CommandLineActions.cpp b/src/CommandLineActions.cpp index 91b20f7897..3e69058303 100644 --- a/src/CommandLineActions.cpp +++ b/src/CommandLineActions.cpp @@ -18,6 +18,10 @@ #include "JsonUtil.h" #include "ScreenInstallOverlay.h" +#if defined(HAVE_VERSION_INFO) +#include "ver.h" +#endif + // only used for Version() #if defined(_WINDOWS) #include @@ -77,12 +81,6 @@ static void LuaInformation() delete pNode; } -#if defined(HAVE_VERSION_INFO) -extern unsigned long version_num; -extern const char *const version_date; -extern const char *const version_time; -#endif - /** * @brief Print out version information. * diff --git a/src/Makefile.am b/src/Makefile.am index 99fe9ba226..bd1d0a172d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,9 +37,9 @@ increment_version: else \ build=0; \ fi; \ - echo "extern const unsigned long version_num = $$build;" > ver.cpp; \ - echo "extern const char *const version_time = \"`date +"%X %Z (UTC%:z)"`\";" >> ver.cpp; - echo "extern const char *const version_date = \"`date +"%Y%m%d"`\";" >> ver.cpp; + echo "const unsigned long version_num = $$build;" > ver.cpp; \ + echo "const char *const version_time = \"`date +"%X %Z (UTC%:z)"`\";" >> ver.cpp; + echo "const char *const version_date = \"`date +"%Y%m%d"`\";" >> ver.cpp; ver.cpp: increment_version diff --git a/src/NetworkSyncManager.cpp b/src/NetworkSyncManager.cpp index 7fed108439..ae232d52eb 100644 --- a/src/NetworkSyncManager.cpp +++ b/src/NetworkSyncManager.cpp @@ -6,9 +6,10 @@ NetworkSyncManager *NSMAN; -// Aldo: used by GetCurrentSMVersion() +// Aldo: version_num used by GetCurrentSMVersion() +// XXX: That's probably not what you want... --root #if defined(HAVE_VERSION_INFO) -extern unsigned long version_num; +#include "ver.h" #endif #if defined(WITHOUT_NETWORKING) diff --git a/src/PrefsManager.cpp b/src/PrefsManager.cpp index 9931f3a027..91f31fe22a 100644 --- a/src/PrefsManager.cpp +++ b/src/PrefsManager.cpp @@ -11,6 +11,10 @@ #include "RageLog.h" #include "SpecialFiles.h" +#if !defined(WITHOUT_NETWORKING) && defined(HAVE_VERSION_INFO) +#include "ver.h" +#endif + //DEFAULTS_INI_PATH = "Data/Defaults.ini"; // these can be overridden //PREFERENCES_INI_PATH // overlay on Defaults.ini, contains the user's choices //STATIC_INI_PATH = "Data/Static.ini"; // overlay on the 2 above, can't be overridden @@ -143,10 +147,6 @@ bool g_bAutoRestart = false; # define TRUE_IF_DEBUG false #endif -#if !defined(WITHOUT_NETWORKING) && defined(HAVE_VERSION_INFO) -extern unsigned long version_num; -#endif - void ValidateDisplayAspectRatio( float &val ) { if( val < 0 ) diff --git a/src/StepMania.cpp b/src/StepMania.cpp index 98dbf12cde..bc117771cc 100644 --- a/src/StepMania.cpp +++ b/src/StepMania.cpp @@ -69,6 +69,10 @@ #include "Profile.h" #include "ActorUtil.h" +#ifdef HAVE_VERSION_INFO +#include "ver.h" +#endif + #if defined(WIN32) #include #endif @@ -907,11 +911,6 @@ static void MountTreeOfZips( const RString &dir ) } } -#if defined(HAVE_VERSION_INFO) -extern unsigned long version_num; -extern const char *const version_date; -extern const char *const version_time; -#endif static void WriteLogHeader() { diff --git a/src/archutils/Unix/CrashHandlerChild.cpp b/src/archutils/Unix/CrashHandlerChild.cpp index 518ad0c7f6..c923522369 100644 --- a/src/archutils/Unix/CrashHandlerChild.cpp +++ b/src/archutils/Unix/CrashHandlerChild.cpp @@ -23,9 +23,7 @@ #endif #if defined(HAVE_VERSION_INFO) -extern const unsigned long version_num; -extern const char *const version_date; -extern const char *const version_time; +#include "ver.h" #endif bool child_read( int fd, void *p, int size ); diff --git a/src/archutils/Win32/CrashHandlerChild.cpp b/src/archutils/Win32/CrashHandlerChild.cpp index 7e377017df..5dd70744b9 100644 --- a/src/archutils/Win32/CrashHandlerChild.cpp +++ b/src/archutils/Win32/CrashHandlerChild.cpp @@ -30,9 +30,8 @@ #pragma comment(lib, "archutils/Win32/ddk/dbghelp.lib") #endif -extern unsigned long version_num; -extern const char *const version_date; -extern const char *const version_time; +// XXX: What happens when we *don't* have version info? Does that ever actually happen? +#include "ver.h" // VDI symbol lookup: namespace VDDebugInfo From 36a412f6e4693a2505ba1a3a861bd1598e33878b Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 28 Jan 2015 16:11:26 -0800 Subject: [PATCH 09/40] Add --with-system-pcre; allow building against system PCRE library --- configure.ac | 15 +++++++++++++++ src/Makefile.am | 12 ++++++++---- src/RageUtil.cpp | 4 ++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index e875775951..9cb0d610a2 100644 --- a/configure.ac +++ b/configure.ac @@ -388,6 +388,21 @@ AC_SUBST(RESFLAGS) dnl Add these after all tests are done as they don't exist just yet. LIBS="$LIBS $FFMPEG_LIBS" +# Selection between bundled pcre and system pcre +AC_ARG_WITH(system-pcre, AS_HELP_STRING([--with-system-pcre],[Disable building of bundled libpcre]), with_system_pcre=$withval, with_system_pcre=no) +AM_CONDITIONAL(USE_SYSTEM_PCRE, test "$with_system_pcre" == "yes") + +if test "$with_system_pcre" == "yes"; then + have_libpcre=yes + AC_CHECK_HEADER(pcre.h, , have_libpcre=no) + if test "$have_libpcre" = "no"; then + AC_MSG_ERROR([If you want to use system libpcre, please make sure that it is installed. Otherwise please configure with --without-system-pcre.]) + else + AC_DEFINE([USE_SYSTEM_PCRE], 1, [Build with system libpcre]) + LIBS="$LIBS -lpcre" + fi +fi + AC_CONFIG_FILES(Makefile) AC_CONFIG_FILES(bundle/Makefile) AC_CONFIG_FILES(src/Makefile) diff --git a/src/Makefile.am b/src/Makefile.am index bda964f867..3031b65d04 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -486,8 +486,13 @@ ScoreDisplayNormal.cpp ScoreDisplayNormal.h ScoreDisplayOni.cpp ScoreDisplayOni. ScoreDisplayPercentage.cpp ScoreDisplayPercentage.h ScoreDisplayRave.cpp ScoreDisplayRave.h \ SongPosition.cpp SongPosition.h -PCRE = ../extern/pcre/get.c ../extern/pcre/internal.h ../extern/pcre/maketables.c ../extern/pcre/pcre.c ../extern/pcre/pcre.h ../extern/pcre/study.c +Rage = +all_test_SOURCES = +if !USE_SYSTEM_PCRE +Rage += ../extern/pcre/get.c ../extern/pcre/internal.h ../extern/pcre/maketables.c ../extern/pcre/pcre.c ../extern/pcre/pcre.h ../extern/pcre/study.c +all_test_SOURCES += ../extern/pcre/get.c ../extern/pcre/internal.h ../extern/pcre/maketables.c ../extern/pcre/pcre.c ../extern/pcre/pcre.h ../extern/pcre/study.c EXTRA_DIST += ../extern/pcre/chartables.c +endif Lua = ../extern/lua-5.1/src/lapi.c ../extern/lua-5.1/src/lauxlib.c ../extern/lua-5.1/src/lbaselib.c ../extern/lua-5.1/src/lcode.c ../extern/lua-5.1/src/ldblib.c \ ../extern/lua-5.1/src/ldebug.c ../extern/lua-5.1/src/ldo.c ../extern/lua-5.1/src/ldump.c ../extern/lua-5.1/src/lfunc.c ../extern/lua-5.1/src/lgc.c ../extern/lua-5.1/src/linit.c \ @@ -523,7 +528,7 @@ RageFileDriverReadAhead.cpp RageFileDriverReadAhead.h \ RageFileDriverSlice.cpp RageFileDriverSlice.h \ RageFileDriverTimeout.cpp RageFileDriverTimeout.h -Rage = $(PCRE) $(Lua) $(jsoncpp) $(RageFile) $(RageSoundFileReaders) \ +Rage += $(Lua) $(jsoncpp) $(RageFile) $(RageSoundFileReaders) \ CubicSpline.cpp CubicSpline.h \ RageBitmapTexture.cpp RageBitmapTexture.h \ RageDisplay.cpp RageDisplay.h \ @@ -769,9 +774,8 @@ if HAVE_GTK GtkModule_so_SOURCES = arch/LoadingWindow/LoadingWindow_GtkModule.cpp endif -all_test_SOURCES = \ +all_test_SOURCES += \ $(ArchUtils) \ - $(PCRE) \ $(RageFile) \ $(Lua) \ $(ArchHooks) \ diff --git a/src/RageUtil.cpp b/src/RageUtil.cpp index c2d033f87e..b61b3f12b3 100644 --- a/src/RageUtil.cpp +++ b/src/RageUtil.cpp @@ -1342,7 +1342,11 @@ bool GetFileContents( const RString &sFile, vector &asOut ) return true; } +#ifndef USE_SYSTEM_PCRE #include "../extern/pcre/pcre.h" +#else +#include +#endif void Regex::Compile() { const char *error; From 7a33b8a44ad753ac335ccd35fb0f52562d1df471 Mon Sep 17 00:00:00 2001 From: "Ben \"root\" Anderson" Date: Wed, 28 Jan 2015 19:21:24 -0600 Subject: [PATCH 10/40] Program version is now in a const from a generated file, not a static macro. Plus all the mess I had to make to get that to work. --- src/CommandLineActions.cpp | 7 ++--- src/LuaManager.cpp | 3 +- src/Makefile.am | 35 +++++++++++------------ src/NetworkSyncManager.cpp | 2 +- src/ProductInfo.h | 27 ----------------- src/ProductInfo.inc | 5 ++-- src/StepMania.cpp | 2 +- src/archutils/Win32/CrashHandlerChild.cpp | 5 ++-- 8 files changed, 29 insertions(+), 57 deletions(-) diff --git a/src/CommandLineActions.cpp b/src/CommandLineActions.cpp index 3e69058303..3aac6f784e 100644 --- a/src/CommandLineActions.cpp +++ b/src/CommandLineActions.cpp @@ -17,10 +17,7 @@ #include "Preference.h" #include "JsonUtil.h" #include "ScreenInstallOverlay.h" - -#if defined(HAVE_VERSION_INFO) #include "ver.h" -#endif // only used for Version() #if defined(_WINDOWS) @@ -73,7 +70,7 @@ static void LuaInformation() pNode->AppendAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); pNode->AppendAttr("xsi:schemaLocation", "http://www.stepmania.com Lua.xsd"); - pNode->AppendChild("Version", PRODUCT_ID_VER); + pNode->AppendChild("Version", string(PRODUCT_FAMILY) + product_version); pNode->AppendChild("Date", DateTime::GetNowDate().GetString()); XmlFileUtil::SaveToFile(pNode, "Lua.xml", "Lua.xsl"); @@ -90,7 +87,7 @@ static void LuaInformation() static void Version() { #if defined(WIN32) - RString sProductID = ssprintf("%s", PRODUCT_ID_VER); + RString sProductID = ssprintf("%s", (string(PRODUCT_FAMILY) + product_version).c_str() ); RString sVersion = "(StepMania was built without HAVE_VERSION_INFO)"; #if defined(HAVE_VERSION_INFO) sVersion = ssprintf("build %lu\nCompile Date: %s @ %s", version_num, version_date, version_time); diff --git a/src/LuaManager.cpp b/src/LuaManager.cpp index 116e9d8d1b..3d2f12e50b 100644 --- a/src/LuaManager.cpp +++ b/src/LuaManager.cpp @@ -12,6 +12,7 @@ #include "RageLog.h" #include "RageTypes.h" #include "MessageManager.h" +#include "ver.h" #include // conversion for lua functions. #include @@ -1040,7 +1041,7 @@ void LuaHelpers::PushValueFunc( lua_State *L, int iArgs ) #include "ProductInfo.h" LuaFunction( ProductFamily, (RString) PRODUCT_FAMILY ); -LuaFunction( ProductVersion, (RString) PRODUCT_VER ); +LuaFunction( ProductVersion, (RString) product_version ); LuaFunction( ProductID, (RString) PRODUCT_ID ); extern const char *const version_date; diff --git a/src/Makefile.am b/src/Makefile.am index bd1d0a172d..af79145ad2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,6 @@ AM_CFLAGS = noinst_LIBRARIES = EXTRA_DIST = - AM_CXXFLAGS += -fno-exceptions # Relax inlining; the default of 600 takes way too long to compile and @@ -28,31 +27,31 @@ AM_CXXFLAGS += -finline-limit=300 AM_CXXFLAGS += $(GL_CFLAGS) -.PHONY: increment_version gitversion +.PHONY: update_version -increment_version: +update_version: if test -e ver.cpp; then \ - build=`sed -rs 's/.*version_num = ([[:digit:]]+);/\1/;q' ver.cpp`; \ + build=`sed -rs 's/.*version_num = ([[:digit:]]+);/\1/;2q;d' ver.cpp`; \ build=`expr $$build + 1`; \ else \ build=0; \ fi; \ - echo "const unsigned long version_num = $$build;" > ver.cpp; \ - echo "const char *const version_time = \"`date +"%X %Z (UTC%:z)"`\";" >> ver.cpp; - echo "const char *const version_date = \"`date +"%Y%m%d"`\";" >> ver.cpp; - -ver.cpp: increment_version - -gitversion: - rm -f gitversion.h - touch gitversion.h + echo "#include \"ver.h\"" > ver.cpp; \ + echo "const unsigned long version_num = $$build;" >> ver.cpp +# Header inclusion necessary to get linker symbols right + echo "const char *const version_time = \"`date +"%X %Z (UTC%:z)"`\";" >> ver.cpp + echo "const char *const version_date = \"`date +"%Y%m%d"`\";" >> ver.cpp if git rev-parse --short HEAD; then \ - echo "#define PRODUCT_VER_BARE 5.0-git-`git rev-parse --short HEAD`" > gitversion.h; \ + echo "const char *const product_version = \"5.0-git-`git rev-parse --short HEAD`\";" >> ver.cpp; \ + else \ + echo "const char *const product_version = \"5.0-UNKNOWN\";" >> ver.cpp; \ fi -gitversion.h: gitversion - -BUILT_SOURCES = gitversion.h ver.cpp +ver.cpp: update_version +# make doesn't recognize a file it updated during the current invocation +# (in this case ver.cpp) as being updated. So remake and delete stepmania-ver.o +# every time. +.INTERMEDIATE: stepmania-ver.o PNG = \ ../extern/libpng/include/png.c ../extern/libpng/include/.png.h \ @@ -745,7 +744,7 @@ main_LDADD = \ $(GL_LIBS) \ libtomcrypt.a libtommath.a -nodist_stepmania_SOURCES = ver.cpp gitversion.h +nodist_stepmania_SOURCES = ver.cpp stepmania_CPPFLAGS = $(main_CPPFLAGS) stepmania_SOURCES = $(main_SOURCES) diff --git a/src/NetworkSyncManager.cpp b/src/NetworkSyncManager.cpp index ae232d52eb..047eda4332 100644 --- a/src/NetworkSyncManager.cpp +++ b/src/NetworkSyncManager.cpp @@ -166,7 +166,7 @@ void NetworkSyncManager::PostStartUp( const RString& ServerIP ) m_packet.Write1( NETPROTOCOLVERSION ); - m_packet.WriteNT( RString(PRODUCT_ID_VER) ); + m_packet.WriteNT( RString( string(PRODUCT_FAMILY) + product_version ) ); /* Block until response is received. * Move mode to blocking in order to give CPU back to the system, diff --git a/src/ProductInfo.h b/src/ProductInfo.h index a09e70ccfc..ec2eff2910 100644 --- a/src/ProductInfo.h +++ b/src/ProductInfo.h @@ -5,9 +5,6 @@ // XXX: VC doesn't generate this yet. Hack around it for now #include "config.h" -#ifdef USE_GITVERSION -#include "gitversion.h" -#endif /** * @brief A friendly string to refer to the product in crash dialogs, etc. @@ -24,36 +21,12 @@ */ #define PRODUCT_ID_BARE StepMania 5.0 -/** - * @brief Version info displayed to the user. - * - * These are the 'official' version designations: - *
    - *
  • "v5.0 alpha #": Alpha versions (bug squashing, polishing until we reach beta)
  • - *
  • "v5.0 beta #": Beta versions (bug squashing, _focus_ is on high priority bugs)
  • - *
  • "v5.0 rc#": Release Candidates (if there are no problems, move on to final)
  • - *
  • "v5.0.0": Final Releases
  • - *
- */ -#ifndef PRODUCT_VER_BARE -#define PRODUCT_VER_BARE 5.0-UNKNOWN -#endif - -/** - * @brief A unique ID for a build of an application. - * - * This is used in crash reports and in the network code's version handling. - */ -#define PRODUCT_ID_VER_BARE PRODUCT_FAMILY_BARE PRODUCT_VER_BARE - // These cannot be #undef'd so make them unlikely to conflict with anything #define PRODUCT_STRINGIFY(x) #x #define PRODUCT_XSTRINGIFY(x) PRODUCT_STRINGIFY(x) #define PRODUCT_FAMILY PRODUCT_XSTRINGIFY(PRODUCT_FAMILY_BARE) #define PRODUCT_ID PRODUCT_XSTRINGIFY(PRODUCT_ID_BARE) -#define PRODUCT_VER PRODUCT_XSTRINGIFY(PRODUCT_VER_BARE) -#define PRODUCT_ID_VER PRODUCT_XSTRINGIFY(PRODUCT_ID_VER_BARE) #define VIDEO_TROUBLESHOOTING_URL "http://old.stepmania.com/stepmaniawiki.php?title=Video_Driver_Troubleshooting" /** @brief The URL to report bugs on the program. */ diff --git a/src/ProductInfo.inc b/src/ProductInfo.inc index bb271ea572..eb9079f405 100644 --- a/src/ProductInfo.inc +++ b/src/ProductInfo.inc @@ -6,8 +6,9 @@ ; PRODUCT_ID must NOT be "StepMania" unless you want people to uninstall 3.9 ; when they install StepMania 5. (not recommended) -!define PRODUCT_ID "StepMania 5" -!define PRODUCT_VER "v5.0 beta 4a" +!define PRODUCT_ID "StepMania 5.0" +; TODO: This needs to be updated with the git rev hash +!define PRODUCT_VER "5.0-UNKNOWN" !define PRODUCT_DISPLAY "${PRODUCT_FAMILY} ${PRODUCT_VER}" !define PRODUCT_BITMAP "sm5" diff --git a/src/StepMania.cpp b/src/StepMania.cpp index bc117771cc..fe412d7f62 100644 --- a/src/StepMania.cpp +++ b/src/StepMania.cpp @@ -914,7 +914,7 @@ static void MountTreeOfZips( const RString &dir ) static void WriteLogHeader() { - LOG->Info( PRODUCT_ID_VER ); + LOG->Info( RString(PRODUCT_FAMILY) + product_version ); #if defined(HAVE_VERSION_INFO) LOG->Info( "Compiled %s @ %s (build %lu)", version_date, version_time, version_num ); diff --git a/src/archutils/Win32/CrashHandlerChild.cpp b/src/archutils/Win32/CrashHandlerChild.cpp index 5dd70744b9..98c1c2f350 100644 --- a/src/archutils/Win32/CrashHandlerChild.cpp +++ b/src/archutils/Win32/CrashHandlerChild.cpp @@ -25,6 +25,7 @@ #include "XmlFileUtil.h" #include "LocalizedString.h" #include "RageFileDriverDeflate.h" +#include "ver.h" #if defined(_MSC_VER) #pragma comment(lib, "archutils/Win32/ddk/dbghelp.lib") @@ -434,7 +435,7 @@ static void MakeCrashReport( const CompleteCrashData &Data, RString &sOut ) sOut += ssprintf( "%s crash report (build %d, %s @ %s)\n" "--------------------------------------\n\n", - PRODUCT_ID_VER, version_num, version_date, version_time ); + (string(PRODUCT_FAMILY) + product_version).c_str(), version_num, version_date, version_time ); sOut += ssprintf( "Crash reason: %s\n", Data.m_CrashInfo.m_CrashReason ); sOut += ssprintf( "\n" ); @@ -737,7 +738,7 @@ BOOL CrashDialog::HandleMessage( UINT msg, WPARAM wParam, LPARAM lParam ) // Create the form data to send. m_pPost = new NetworkPostData; m_pPost->SetData( "Product", PRODUCT_ID ); - m_pPost->SetData( "Version", PRODUCT_VER ); + m_pPost->SetData( "Version", product_version ); m_pPost->SetData( "Arch", HOOKS->GetArchName().c_str() ); m_pPost->SetData( "Report", m_sCrashReport ); m_pPost->SetData( "Reason", m_CrashData.m_CrashInfo.m_CrashReason ); From da49c509ddbaae37589d9de97a77913aaecdaa0c Mon Sep 17 00:00:00 2001 From: "Ben \"root\" Anderson" Date: Wed, 28 Jan 2015 20:05:34 -0600 Subject: [PATCH 11/40] I accidentally a file --- src/ver.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/ver.h diff --git a/src/ver.h b/src/ver.h new file mode 100644 index 0000000000..d56f1f4b22 --- /dev/null +++ b/src/ver.h @@ -0,0 +1,17 @@ +#ifndef STEPMANIA_VER_H +#define STEPMANIA_VER_H + +// HACK: The MSVC project doesn't generate this yet +#ifdef _MSC_VER +#define product_version "5.0-UNKNOWN" +#else +extern const char *const product_version; +#endif + +// XXX: These names are misnomers. This is actually the BUILD number, time and date. +// NOTE: In GNU toolchain these are defined in ver.cpp. In MSVC these are defined in archutils/Win32/verinc.c I think. Why? I don't know and I don't have MSVC. --root +extern const unsigned long version_num; +extern const char *const version_time; +extern const char *const version_date; + +#endif \ No newline at end of file From 9c52fb659bf92ba38580ab49ba3bf6d637009eaa Mon Sep 17 00:00:00 2001 From: "Ben \"root\" Anderson" Date: Wed, 28 Jan 2015 20:09:19 -0600 Subject: [PATCH 12/40] I have no idea why I did that. I shouldn't have done that. --- src/ProductInfo.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ProductInfo.h b/src/ProductInfo.h index ec2eff2910..d43924109d 100644 --- a/src/ProductInfo.h +++ b/src/ProductInfo.h @@ -3,9 +3,6 @@ #ifndef PRODUCT_INFO_H #define PRODUCT_INFO_H -// XXX: VC doesn't generate this yet. Hack around it for now -#include "config.h" - /** * @brief A friendly string to refer to the product in crash dialogs, etc. * From 7a6db336446e34e53ee39c08fc03ad9b5b19fd47 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Wed, 28 Jan 2015 21:45:23 -0500 Subject: [PATCH 13/40] Adapt to recent version_num changes. ...I don't get what's going on either. --- src/archutils/Win32/verinc.c | 145 +++++++++++++++++---------------- src/archutils/Win32/verinc.exe | Bin 67072 -> 98304 bytes 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/src/archutils/Win32/verinc.c b/src/archutils/Win32/verinc.c index e12895d408..a0811fbcd0 100644 --- a/src/archutils/Win32/verinc.c +++ b/src/archutils/Win32/verinc.c @@ -1,72 +1,73 @@ -// Ehhh.... -// -// I think I'll just put this one in the public domain -// (with no warranty as usual). -// -// --Avery - -// took some ideas from OpenITG... -aj - -#include -#include -#include -#include - -typedef unsigned long ulong; - -int main(void) -{ - FILE *f; - ulong build=0; - char strdate[10], strtime[64]; - time_t tm; - //struct tm *ptm; - - // try to read the last version seen - if( f = fopen("version.bin","r") ) - { - fread( &build, sizeof(ulong), 1, f ); - fclose( f ); - } - - // increment the build number and write it - build++; - - if ( f = fopen("version.bin","wb") ) - { - fwrite(&build,sizeof(ulong),1,f); - fclose(f); - } -// printf("Incrementing to build %d\n",build); - - // get the current time - time(&tm); - - /* - //memcpy(version_time, asctime(localtime(&tm)), sizeof(version_time)-1); - ptm = localtime(&tm); - strftime(s, sizeof(s), "%Y%m%d", ptm); - s[sizeof(s)-1]=0; - */ - - // print the debug serial date/time - strftime( strdate, 15, "%Y%m%d", localtime(&tm) ); - strftime( strtime, 64, "%H:%M:%S %Z", localtime(&tm) ); - //memcpy( strtime, asctime(localtime(&tm)), 24 ); - - // zero out the newline character - strtime[sizeof(strtime)-1] = 0; - - // write to verstub - if ( f = fopen("verstub.cpp","w") ) - { - fprintf(f, - "unsigned long version_num = %ld;\n" - "extern const char *const version_date = \"%s\";\n" - "extern const char *const version_time = \"%s\";\n", - build, strdate, strtime); - fclose(f); - } - - return 0; -} +// Ehhh.... +// +// I think I'll just put this one in the public domain +// (with no warranty as usual). +// +// --Avery + +// took some ideas from OpenITG... -aj + +#include +#include +#include +#include + +typedef unsigned long ulong; + +int main(void) +{ + FILE *f; + ulong build=0; + char strdate[10], strtime[64]; + time_t tm; + //struct tm *ptm; + + // try to read the last version seen + if( f = fopen("version.bin","r") ) + { + fread( &build, sizeof(ulong), 1, f ); + fclose( f ); + } + + // increment the build number and write it + build++; + + if ( f = fopen("version.bin","wb") ) + { + fwrite(&build,sizeof(ulong),1,f); + fclose(f); + } +// printf("Incrementing to build %d\n",build); + + // get the current time + time(&tm); + + /* + //memcpy(version_time, asctime(localtime(&tm)), sizeof(version_time)-1); + ptm = localtime(&tm); + strftime(s, sizeof(s), "%Y%m%d", ptm); + s[sizeof(s)-1]=0; + */ + + // print the debug serial date/time + strftime( strdate, 15, "%Y%m%d", localtime(&tm) ); + strftime( strtime, 64, "%H:%M:%S %Z", localtime(&tm) ); + //memcpy( strtime, asctime(localtime(&tm)), 24 ); + + // zero out the newline character + strtime[sizeof(strtime)-1] = 0; + + // write to verstub + if ( f = fopen("verstub.cpp","w") ) + { + fprintf(f, + "#include \"ver.h\"\n" + "unsigned long const version_num = %ld;\n" + "extern char const * const version_date = \"%s\";\n" + "extern char const * const version_time = \"%s\";\n", + build, strdate, strtime); + fclose(f); + } + + return 0; +} diff --git a/src/archutils/Win32/verinc.exe b/src/archutils/Win32/verinc.exe index d17008756afecc646edc7661d738cdd2c075af5b..9d1bda4622e876bd6ddceccc4eee9ad402d1dd56 100644 GIT binary patch literal 98304 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 vcrDm@>|*kH01|o-GI|J7+52r literal 67072 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 XYW)53{89qHl)x_~@Jk6?RRaG9Hp@6K From a7fc46829ef244de2e40a4794e69d994cb9fc358 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Wed, 28 Jan 2015 21:46:13 -0500 Subject: [PATCH 14/40] Don't force the old SDK. --- Xcode/stepmania.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Xcode/stepmania.xcodeproj/project.pbxproj b/Xcode/stepmania.xcodeproj/project.pbxproj index 64a6d1d13f..afa80d112a 100644 --- a/Xcode/stepmania.xcodeproj/project.pbxproj +++ b/Xcode/stepmania.xcodeproj/project.pbxproj @@ -8408,7 +8408,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.6; ONLY_ACTIVE_ARCH = YES; PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES; - SDKROOT = macosx10.6; + SDKROOT = macosx; SHARED_PRECOMPS_DIR = "/Library/Caches/com.apple.Xcode.$(UID)/SharedPrecompiledHeaders/$(PRODUCT_NAME)/$(CONFIGURATION)"; STRIP_INSTALLED_PRODUCT = NO; STRIP_STYLE = "non-global"; @@ -8789,7 +8789,7 @@ LINKER_DISPLAYS_MANGLED_NAMES = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES; - SDKROOT = macosx10.6; + SDKROOT = macosx; SHARED_PRECOMPS_DIR = "/Library/Caches/com.apple.Xcode.$(UID)/SharedPrecompiledHeaders/$(PRODUCT_NAME)/$(CONFIGURATION)"; STRIP_STYLE = "non-global"; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../extern/glew-1.5.8/include $(PROJECT_DIR)/../extern/jsoncpp/include $(PROJECT_DIR)/../extern/vorbis"; @@ -9102,7 +9102,7 @@ LINKER_DISPLAYS_MANGLED_NAMES = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES; - SDKROOT = macosx10.6; + SDKROOT = macosx; SHARED_PRECOMPS_DIR = "/Library/Caches/com.apple.Xcode.$(UID)/SharedPrecompiledHeaders/$(PRODUCT_NAME)/$(CONFIGURATION)"; STRIP_STYLE = "non-global"; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../extern/glew-1.5.8/include $(PROJECT_DIR)/../extern/jsoncpp/include $(PROJECT_DIR)/../extern/vorbis"; From 0a4902c6487bd935daabb10de6ab262ef41a06f4 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Wed, 28 Jan 2015 22:09:38 -0500 Subject: [PATCH 15/40] ...and the Mac side. --- src/archutils/Unix/CrashHandlerChild.cpp | 4 +--- src/ver.h | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/archutils/Unix/CrashHandlerChild.cpp b/src/archutils/Unix/CrashHandlerChild.cpp index c923522369..7c9ef9412f 100644 --- a/src/archutils/Unix/CrashHandlerChild.cpp +++ b/src/archutils/Unix/CrashHandlerChild.cpp @@ -22,9 +22,7 @@ #include "archutils/Darwin/Crash.h" #endif -#if defined(HAVE_VERSION_INFO) #include "ver.h" -#endif bool child_read( int fd, void *p, int size ); @@ -202,7 +200,7 @@ static void child_process() exit(1); } - fprintf( CrashDump, "%s crash report", PRODUCT_ID_VER ); + fprintf( CrashDump, "%s%s crash report", PRODUCT_FAMILY, product_version ); #if defined(HAVE_VERSION_INFO) fprintf( CrashDump, " (build %lu, %s @ %s)", version_num, version_date, version_time ); #endif diff --git a/src/ver.h b/src/ver.h index d56f1f4b22..adb3b99e87 100644 --- a/src/ver.h +++ b/src/ver.h @@ -1,8 +1,10 @@ #ifndef STEPMANIA_VER_H #define STEPMANIA_VER_H + + // HACK: The MSVC project doesn't generate this yet -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__MACOSX__) #define product_version "5.0-UNKNOWN" #else extern const char *const product_version; From 110d9585581726beac5528a53018cd7b2e239175 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Thu, 29 Jan 2015 13:17:50 -0700 Subject: [PATCH 16/40] =?UTF-8?q?Updated=20default=20theme=20Polish=20tran?= =?UTF-8?q?slation=20from=20Jaros=C5=82aw=20Pietras.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Themes/default/Languages/pl.ini | 41 ++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/Themes/default/Languages/pl.ini b/Themes/default/Languages/pl.ini index ce8cea874c..7191c08eda 100644 --- a/Themes/default/Languages/pl.ini +++ b/Themes/default/Languages/pl.ini @@ -9,7 +9,7 @@ EventStageCounter=Piosenka %03i [ScreenTitleMenu] HelpText=&BACK; Wyjście &START; Zatwierdź &MENUUP;&MENUDOWN; Wybierz Network OK=Sieć OK -Offline=Brak poŁĄczenia +Offline=Brak połączenia Connected to %s=Podłączony do %s CurrentGametype=Tryb gry: %s LifeDifficulty=Zużycie energii: %s @@ -24,7 +24,10 @@ EventMode=Tryb Imprezy JointPremiumMain=Premia Łączona JointPremiumSecondary=Dwóch graczy może grac za jeden kredyt! DoublesPremiumMain=Podwójna Premia -DoublesPremiumSecondary=Graj w trybie Double za jeden kredyt! +DoublesPremiumSecondary=Graj w trybie Dwuosobowym za jeden kredyt! + +[ScreenCaution] +HelpText=&START; Kontynuuj [ScreenDemonstration] Demonstration=Demonstracja @@ -83,7 +86,7 @@ HelpText= HelpText= [ScreenEvaluation] -HelpText=&BACK; Wyjście &START; Dalej &MENULEFT;+&MENURIGHT; or &SELECT; Screenshot +HelpText=&BACK; Wyjście &START; Dalej &MENULEFT;+&MENURIGHT; lub &SELECT; Zrzut ekranu LifeDifficulty=Zużycie energii: %s TimingDifficulty=Ocena trafień: %s MachineRecord=Rekord Maszyny #%i! @@ -91,6 +94,9 @@ PersonalRecord=Rekord Życiowy #%i! ITG DP:=ITG DP: MIGS DP:=MIGS DP: +[ScreenContinue] +HelpText=&START; Advance Timer + [OptionTitles] AutoSetStyle=Automatycznie Ustaw Styl NotePosition=Pozycja Strzałek @@ -130,7 +136,7 @@ Appearance=Kontrola widoczności strzałek. Turn=Zmiana choreografii stepchart`a po przez modyfikacje pozycji istniejących strzałek. Insert=Zmiana choreografii stepchart`a po przez proceduralne dodanie nowych strzałek. Holds=Zmienia cześć strzałki w HOLD`y -Mines=Dodanie lub usuniecie min. +Mines=Dodanie lub usuniecie Min. PlayerAutoPlay=Program odgrywa piosenkę automatycznie ScoreDisplay=Zmiana sposobu wyświetlania punktacji. ProTiming=Zmiana sposobu wyświetlania dokładności trafienia w strzałkę. @@ -188,10 +194,10 @@ Importing Songs=Importowanie piosenek z zainstalowanych wcześniej wersji Stepma Reload Songs=Przeładowanie Piosenek Exit=Powrót do Ekranu Startowego -Explanation-WhereToFind=Uruchamia stronę www z informacja miejscach i sposobach na znalezienie piosenek. -Explanation-HowToInstall=Uruchamia stronę www z instrukcja instalacji piosenek. -Explanation-AdditionalFolders=Jeśli masz zainstalowane inne instancje Stepmanii, możesz użyć opcji AdditionalFolder z pliku ustawień by załadować je bez konieczności ich kopiowania. -Explanation-ReloadSongs=Ta czynność jest konieczna jeśli dodano/zmieniono/osunięto piosenki w trakcie pracy programu. +Explanation-WhereToFind=Uruchamia stronę www z informacją miejscach i sposobach na znalezienie piosenek. +Explanation-HowToInstall=Uruchamia stronę www z instrukcją instalacji piosenek. +Explanation-AdditionalFolders=Jeśli masz zainstalowane inne instancje Stepmanii, możesz użyć opcji Foldery Dodatkowe z pliku ustawień by załadować je bez konieczności ich kopiowania. +Explanation-ReloadSongs=Ta czynność jest konieczna jeśli dodano/zmieniono/usunięto piosenki w trakcie pracy programu. Explanation-Exit=Powrót do Ekranu Startowego. [OptionNames] @@ -209,12 +215,12 @@ HelpText=Przytrzymaj &BACK; lub &START; aby wyjść. [PaneDisplay] Taps=Kroki Jumps=Skoki -Holds=Holdy +Holds=Holds Mines=Miny -Hands=RĘce -Rolls=Rolle -Lifts=Lifty -Fakes=Fejki +Hands=Ręce +Rolls=Rolls +Lifts=Lifts +Fakes=Fakes S=S V=V A=A @@ -230,6 +236,13 @@ Play again soon!=Zagraj jeszcze raz! [ScreenHowToPlay] How To Play StepMania=Jak Grać w Stepmanię +Information=Informacja + +Feet=Twoje stopy będą wykorzystywane do gry! +Tap=Kiedy strzałki pokryją się z tym punktem,\npołóż nogę na odpowiednim panelu. +Jump=Połóż nogi na obydwu panelach jeśli pojawią się dwie\nróżne strzałki w tym samym czasie! +Miss=Jeśli wielokrotnie będziesz się mylił,\ntwój wskaźnik tańca będzie się zmniejszał\naż gra się skończy! + [Protiming] -MS=MS \ No newline at end of file +MS=MS From 07defa43792a9494e8c3be35452ea9425ea9cda1 Mon Sep 17 00:00:00 2001 From: "Ben \"root\" Anderson" Date: Thu, 29 Jan 2015 15:17:26 -0600 Subject: [PATCH 17/40] On closer inspection, this shouldn't be changed until 5.1. --- src/ProductInfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProductInfo.h b/src/ProductInfo.h index d43924109d..4512ee66da 100644 --- a/src/ProductInfo.h +++ b/src/ProductInfo.h @@ -16,7 +16,7 @@ * As an example, use "StepMania4" here, not "StepMania". * It would cause a conflict with older versions such as StepMania 3.X. */ -#define PRODUCT_ID_BARE StepMania 5.0 +#define PRODUCT_ID_BARE StepMania 5 // These cannot be #undef'd so make them unlikely to conflict with anything #define PRODUCT_STRINGIFY(x) #x From 615d4e6ef6bcadf32fe8782d9163395276db7070 Mon Sep 17 00:00:00 2001 From: "Ben \"root\" Anderson" Date: Thu, 29 Jan 2015 15:29:33 -0600 Subject: [PATCH 18/40] That shit was deprecated back in Windows XP! Service pack nothing, I'm talking RELEASE DAY. --- src/archutils/Win32/SpecialDirs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/archutils/Win32/SpecialDirs.cpp b/src/archutils/Win32/SpecialDirs.cpp index 5c48528e9e..499dd78538 100644 --- a/src/archutils/Win32/SpecialDirs.cpp +++ b/src/archutils/Win32/SpecialDirs.cpp @@ -6,8 +6,8 @@ static RString GetSpecialFolderPath( int csidl ) { RString sDir; TCHAR szDir[MAX_PATH] = ""; - BOOL bResult = SHGetSpecialFolderPath( NULL, szDir, csidl, FALSE ); - ASSERT( bResult ); + HRESULT hResult = SHGetFolderPath( NULL, csidl, NULL, SHGFP_TYPE_CURRENT, szDir ); + ASSERT( hResult == S_OK ); sDir = szDir; sDir += "/"; return sDir; From 647b361284f3da60be88dacca4734f71ddf94c2a Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 30 Jan 2015 09:05:09 -0700 Subject: [PATCH 19/40] 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 ) { From b4ede3f9772a126c79a075bb1578fcb25f42f063 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 30 Jan 2015 09:30:50 -0700 Subject: [PATCH 20/40] Stupid LuaHelpes::Push doesn't have a size_t specialization. --- src/Actor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Actor.cpp b/src/Actor.cpp index b9eebb33db..1096bf1fe2 100644 --- a/src/Actor.cpp +++ b/src/Actor.cpp @@ -1891,7 +1891,11 @@ public: p->RemoveWrapperState(si); COMMON_RETURN_SELF; } - DEFINE_METHOD(GetNumWrapperStates, GetNumWrapperStates()); + static int GetNumWrapperStates(T* p, lua_State* L) + { + lua_pushnumber(L, p->GetNumWrapperStates()); + return 1; + } static int GetWrapperState(T* p, lua_State* L) { size_t si= get_state_index(p, L, 1); From b6e6185156be961b918056b870197e4f41966fcb Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 30 Jan 2015 10:05:15 -0700 Subject: [PATCH 21/40] LoadTransformedSlidingWindow and LoadOverlapped now strip the PlayerNumber of each tapnote to PLAYER_INVALID so that NoteDisplay doesn't crash during rendering a single chart that was autogenned from a couple chart. --- src/NoteDataUtil.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NoteDataUtil.cpp b/src/NoteDataUtil.cpp index 2017f704dd..3d7a54ea0f 100644 --- a/src/NoteDataUtil.cpp +++ b/src/NoteDataUtil.cpp @@ -556,7 +556,8 @@ void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &o { int iOldTrack = t; int iNewTrack = (iOldTrack + iCurTrackOffset) % iNewNumTracks; - const TapNote &tn = in.GetTapNote( iOldTrack, r ); + TapNote tn = in.GetTapNote( iOldTrack, r ); + tn.pn= PLAYER_INVALID; out.SetTapNote( iNewTrack, r, tn ); } } @@ -622,9 +623,10 @@ void NoteDataUtil::LoadOverlapped( const NoteData &in, NoteData &out, int iNewNu { for( int iTrackFrom = 0; iTrackFrom < in.GetNumTracks(); ++iTrackFrom ) { - const TapNote &tnFrom = in.GetTapNote( iTrackFrom, row ); + TapNote tnFrom = in.GetTapNote( iTrackFrom, row ); if( tnFrom.type == TapNoteType_Empty || tnFrom.type == TapNoteType_AutoKeysound ) continue; + tnFrom.pn= PLAYER_INVALID; // If this is a hold note, find the end. int iEndIndex = row; From 86410a0570782143e775f5bf7cd4cf7764016877 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 30 Jan 2015 21:22:04 -0700 Subject: [PATCH 22/40] Lazy update to ReceptorArrowRow so that ScreenNameEntry will work again. --- src/ReceptorArrowRow.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ReceptorArrowRow.cpp b/src/ReceptorArrowRow.cpp index 5a0a1d9870..c339601a7b 100644 --- a/src/ReceptorArrowRow.cpp +++ b/src/ReceptorArrowRow.cpp @@ -12,6 +12,7 @@ ReceptorArrowRow::ReceptorArrowRow() m_pPlayerState = NULL; m_fYReverseOffsetPixels = 0; m_fFadeToFailPercent = 0; + m_renderers= NULL; } void ReceptorArrowRow::Load( const PlayerState* pPlayerState, float fYReverseOffset ) @@ -62,8 +63,18 @@ void ReceptorArrowRow::Update( float fDeltaTime ) CLAMP( fBaseAlpha, 0.0f, 1.0f ); m_ReceptorArrow[c]->SetBaseAlpha( fBaseAlpha ); - // set arrow XYZ - (*m_renderers)[c].UpdateReceptorGhostStuff(m_ReceptorArrow[c]); + if(m_renderers != NULL) + { + // set arrow XYZ + (*m_renderers)[c].UpdateReceptorGhostStuff(m_ReceptorArrow[c]); + } + else + { + // ScreenNameEntry uses ReceptorArrowRow but doesn't have or need + // column renderers. Just do the lazy thing and offset x. -Kyz + const Style* style= GAMESTATE->GetCurrentStyle(m_pPlayerState->m_PlayerNumber); + m_ReceptorArrow[c]->SetX(style->m_ColumnInfo[m_pPlayerState->m_PlayerNumber][c].fXOffset); + } } } From 09eede311cbb7dafcbb6f63c9fd3b32960beb6ed Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 30 Jan 2015 23:41:59 -0700 Subject: [PATCH 23/40] LOG->Info takes a char*, not an RString. --- src/StepMania.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StepMania.cpp b/src/StepMania.cpp index fe412d7f62..ffea073c90 100644 --- a/src/StepMania.cpp +++ b/src/StepMania.cpp @@ -914,7 +914,7 @@ static void MountTreeOfZips( const RString &dir ) static void WriteLogHeader() { - LOG->Info( RString(PRODUCT_FAMILY) + product_version ); + LOG->Info((RString(PRODUCT_FAMILY) + product_version).c_str()); #if defined(HAVE_VERSION_INFO) LOG->Info( "Compiled %s @ %s (build %lu)", version_date, version_time, version_num ); From 8cde91eb2e15bcbb6be69685f4c8fcc3f418c1fd Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Sat, 31 Jan 2015 00:23:54 -0700 Subject: [PATCH 24/40] Prevent crashes from an invalid noteskin being set. --- src/NoteField.cpp | 5 +++++ src/NoteSkinManager.cpp | 9 +++++++++ src/NoteSkinManager.h | 4 +++- src/PlayerOptions.cpp | 6 +++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/NoteField.cpp b/src/NoteField.cpp index 1d7672af4d..33a3b04b94 100644 --- a/src/NoteField.cpp +++ b/src/NoteField.cpp @@ -137,7 +137,10 @@ void NoteField::CacheAllUsedNoteSkins() GAMESTATE->GetAllUsedNoteSkins( asSkinsLower ); asSkinsLower.push_back( m_pPlayerState->m_PlayerOptions.GetStage().m_sNoteSkin ); FOREACH( RString, asSkinsLower, s ) + { + NOTESKIN->ValidateNoteSkinName(*s); s->MakeLower(); + } for( unsigned i=0; i < asSkinsLower.size(); ++i ) CacheNoteSkin( asSkinsLower[i] ); @@ -155,6 +158,7 @@ void NoteField::CacheAllUsedNoteSkins() UncacheNoteSkin( *s ); RString sCurrentNoteSkinLower = m_pPlayerState->m_PlayerOptions.GetCurrent().m_sNoteSkin; + NOTESKIN->ValidateNoteSkinName(sCurrentNoteSkinLower); sCurrentNoteSkinLower.MakeLower(); map::iterator it = m_NoteDisplays.find( sCurrentNoteSkinLower ); @@ -165,6 +169,7 @@ void NoteField::CacheAllUsedNoteSkins() FOREACH_EnabledPlayer( pn ) { RString sNoteSkinLower = GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions.GetCurrent().m_sNoteSkin; + NOTESKIN->ValidateNoteSkinName(sNoteSkinLower); sNoteSkinLower.MakeLower(); it = m_NoteDisplays.find( sNoteSkinLower ); ASSERT_M( it != m_NoteDisplays.end(), sNoteSkinLower ); diff --git a/src/NoteSkinManager.cpp b/src/NoteSkinManager.cpp index 9d3dd0d7f7..02948b2ddd 100644 --- a/src/NoteSkinManager.cpp +++ b/src/NoteSkinManager.cpp @@ -246,6 +246,15 @@ RString NoteSkinManager::GetDefaultNoteSkinName() return name; } +void NoteSkinManager::ValidateNoteSkinName(RString& name) +{ + if(name.empty() || !DoesNoteSkinExist(name)) + { + LuaHelpers::ReportScriptError("Someone set a noteskin that doesn't exist. Good job."); + name= GetDefaultNoteSkinName(); + } +} + void NoteSkinManager::GetAllNoteSkinNamesForGame( const Game *pGame, vector &AddTo ) { if( pGame == m_pCurGame ) diff --git a/src/NoteSkinManager.h b/src/NoteSkinManager.h index f5ad944020..db06b25719 100644 --- a/src/NoteSkinManager.h +++ b/src/NoteSkinManager.h @@ -25,9 +25,11 @@ public: bool DoNoteSkinsExistForGame( const Game *pGame ); RString GetDefaultNoteSkinName(); // looks up current const Game* in GAMESTATE + void ValidateNoteSkinName(RString& name); + void SetCurrentNoteSkin( const RString &sNoteSkin ) { m_sCurrentNoteSkin = sNoteSkin; } const RString &GetCurrentNoteSkin() { return m_sCurrentNoteSkin; } - void SetPlayerNumber( PlayerNumber pn ) { m_PlayerNumber = pn; } + void SetPlayerNumber( PlayerNumber pn ) { m_PlayerNumber = pn; } void SetGameController( GameController gc ) { m_GameController = gc; } RString GetPath( const RString &sButtonName, const RString &sElement ); bool PushActorTemplate( Lua *L, const RString &sButton, const RString &sElement, bool bSpriteOnly ); diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp index 8efefb4f0d..94a4991c13 100644 --- a/src/PlayerOptions.cpp +++ b/src/PlayerOptions.cpp @@ -811,7 +811,11 @@ PlayerOptions& PlayerOptions::operator=(PlayerOptions const& other) CPY_SPEED(fPlayerAutoPlay); CPY_SPEED(fPerspectiveTilt); CPY_SPEED(fSkew); - CPY(m_sNoteSkin); + if(!other.m_sNoteSkin.empty() && + NOTESKIN->DoesNoteSkinExist(other.m_sNoteSkin)) + { + CPY(m_sNoteSkin); + } for( int i = 0; i < PlayerOptions::NUM_ACCELS; ++i ) { CPY_SPEED(fAccels[i]); From 7f55c82846eb42a833c46010694c450b393d0825 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 31 Jan 2015 04:18:32 -0500 Subject: [PATCH 25/40] toggling wheel sections should not end screen --- src/WheelBase.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/WheelBase.cpp b/src/WheelBase.cpp index 99142ec4e1..8adb5fd205 100644 --- a/src/WheelBase.cpp +++ b/src/WheelBase.cpp @@ -290,7 +290,7 @@ bool WheelBase::Select() // return true if this selection can end the screen { case WheelItemDataType_Generic: m_LastSelection = m_CurWheelItemData[m_iSelection]; - break; + return true; case WheelItemDataType_Section: { RString sThisItemSectionName = m_CurWheelItemData[m_iSelection]->m_sText; @@ -305,12 +305,11 @@ bool WheelBase::Select() // return true if this selection can end the screen m_soundExpand.Play(); } } - break; + // Opening/closing sections cannot end the screen + return false; default: - break; + return true; } - - return true; } WheelItemBaseData* WheelBase::GetItem( unsigned int iIndex ) From f18fa454950e3a046a90cb26bbde2f40251f415a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 31 Jan 2015 04:26:51 -0500 Subject: [PATCH 26/40] MusicWheel: fix CloseCurrentFolder with OnlyShowActiveSection Moves all of the wheel-entry-hiding logic into SetOpenSection rather than prematurely throwing out section headings. Now when we close the active section with OnlyShowActiveSection in effect, the headings are shown rather than an empty wheel. --- src/MusicWheel.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/MusicWheel.cpp b/src/MusicWheel.cpp index 82d87c33e0..e1a3b9b641 100644 --- a/src/MusicWheel.cpp +++ b/src/MusicWheel.cpp @@ -677,10 +677,7 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt // todo: preferred sort section color handling? -aj RageColor colorSection = (so==SORT_GROUP) ? SONGMAN->GetSongGroupColor(pSong->m_sGroupName) : SECTION_COLORS.GetValue(iSectionColorIndex); iSectionColorIndex = (iSectionColorIndex+1) % NUM_SECTION_COLORS; - // In certain situations (e.g. simulating Pump it Up), themes may - // want to only show one group at a time. - if( !HIDE_INACTIVE_SECTIONS ) - arrayWheelItemDatas.push_back( new MusicWheelItemData(WheelItemDataType_Section, NULL, sThisSection, NULL, colorSection, iSectionCount) ); + arrayWheelItemDatas.push_back( new MusicWheelItemData(WheelItemDataType_Section, NULL, sThisSection, NULL, colorSection, iSectionCount) ); sLastSection = sThisSection; } } @@ -1361,9 +1358,14 @@ void MusicWheel::SetOpenSection( RString group ) for( unsigned i = 0; i < from.size(); ++i ) { MusicWheelItemData &d = *from[i]; + // Don't show songs outside the current group if( (d.m_Type == WheelItemDataType_Song || d.m_Type == WheelItemDataType_Course) && !d.m_sText.empty() && d.m_sText != group ) continue; + // In certain situations (e.g. simulating Pump it Up), themes may + // want to hide inactive group headings as well. + if( HIDE_INACTIVE_SECTIONS && d.m_Type == WheelItemDataType_Section && group != "" ) + continue; // If AUTO_SET_STYLE, hide courses that prefer a style that isn't available. if( d.m_Type == WheelItemDataType_Course && CommonMetrics::AUTO_SET_STYLE ) From d5a1a858b63ee76be02bc4e3a50ac5a0cba9f4a3 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 31 Jan 2015 11:11:26 -0500 Subject: [PATCH 27/40] add apt-get update and -y to dependency installation Recommended by Travis docs: http://docs.travis-ci.com/user/installing-dependencies/ We may eventually want to look into whether the before_install/install hooks are supposed to be used for this rather than before_script. --- .travis.yml | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index da0a4f3408..51b3ca0797 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,24 +9,25 @@ notifications: irc: "chat.freenode.net#stepmania-devs" before_script: - - sudo apt-get install nasm - - sudo apt-get install libmad0-dev - - sudo apt-get install libgtk2.0-dev - - sudo apt-get install binutils-dev - - sudo apt-get install libasound-dev - - sudo apt-get install libpulse-dev - - sudo apt-get install libjack-dev - - sudo apt-get install libc6-dev - - sudo apt-get install libogg-dev - - sudo apt-get install libvorbis-dev - - sudo apt-get install libbz2-dev - - sudo apt-get install zlib1g-dev - - sudo apt-get install libjpeg8-dev - - sudo apt-get install libpng12-dev - - sudo apt-get install libxtst-dev libxrandr-dev - - sudo apt-get install libglu1-mesa-dev - - sudo apt-get install mesa-common-dev - - sudo apt-get install libglew-dev + - sudo apt-get update -qq + - sudo apt-get install -y nasm + - sudo apt-get install -y libmad0-dev + - sudo apt-get install -y libgtk2.0-dev + - sudo apt-get install -y binutils-dev + - sudo apt-get install -y libasound-dev + - sudo apt-get install -y libpulse-dev + - sudo apt-get install -y libjack-dev + - sudo apt-get install -y libc6-dev + - sudo apt-get install -y libogg-dev + - sudo apt-get install -y libvorbis-dev + - sudo apt-get install -y libbz2-dev + - sudo apt-get install -y zlib1g-dev + - sudo apt-get install -y libjpeg8-dev + - sudo apt-get install -y libpng12-dev + - sudo apt-get install -y libxtst-dev libxrandr-dev + - sudo apt-get install -y libglu1-mesa-dev + - sudo apt-get install -y mesa-common-dev + - sudo apt-get install -y libglew-dev script: - ./autogen.sh From 39c7e1bb4502a6f56d892969fc56d856c0cc5c54 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Sat, 31 Jan 2015 11:14:56 -0500 Subject: [PATCH 28/40] Cleaner approach. --- src/StepMania.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StepMania.cpp b/src/StepMania.cpp index ffea073c90..69059c5715 100644 --- a/src/StepMania.cpp +++ b/src/StepMania.cpp @@ -914,7 +914,7 @@ static void MountTreeOfZips( const RString &dir ) static void WriteLogHeader() { - LOG->Info((RString(PRODUCT_FAMILY) + product_version).c_str()); + LOG->Info("%s%s", PRODUCT_FAMILY, product_version); #if defined(HAVE_VERSION_INFO) LOG->Info( "Compiled %s @ %s (build %lu)", version_date, version_time, version_num ); From c7ffbb34532c141720e131ffc606287985171cd3 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Sat, 31 Jan 2015 14:03:30 -0700 Subject: [PATCH 29/40] Lead in time before recording starts in edit mode is now a preference. --- Docs/Changelog_sm5.txt | 5 +++++ Themes/_fallback/Languages/en.ini | 34 +++++++++++++++++++++++++++++++ Themes/_fallback/metrics.ini | 3 ++- Themes/default/metrics.ini | 2 +- src/PrefsManager.cpp | 1 + src/PrefsManager.h | 3 +++ src/ScreenEdit.cpp | 4 ++-- src/ScreenOptionsMasterPrefs.cpp | 18 ++++++++++++++++ 8 files changed, 66 insertions(+), 4 deletions(-) diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 3da66d0f3f..54d5565f51 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,6 +4,11 @@ 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/31 +---------- +* [EditMode] Want more lead in time before recording starts? + It's a preference. [kyzentun] + 2015/01/20 ---------- * [ScreenSelect] ChoiceNames can be a lua command that is evaluated to get a diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 10505b2a40..cf0de8bf02 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -397,6 +397,7 @@ Edit Courses/Mods=Edit Courses/Mods Edit Songs/Steps=Edit Songs/Steps Practice Songs/Steps=Practice Songs/Steps EditorShowBGChangesPlay=Choose whether to display song backgrounds while playing. +EditRecordModeLeadIn=Set the amount of lead-in time before recording starts. Effect=Effect EffectsReceptor=Add alternative behaviors to the receptors and notes. EffectsArrow=Add alternative behaviors to the receptors and notes. @@ -555,6 +556,38 @@ GamePrefDefaultFail=Immediate fail causes a player to die when their life bar re UserPrefScoringMode=Select the scoring mode to be used when not in a course. [OptionNames] ++0 s=+0 s ++1 s=+1 s ++2 s=+2 s ++3 s=+3 s ++4 s=+4 s ++5 s=+5 s ++6 s=+6 s ++7 s=+7 s ++8 s=+8 s ++9 s=+9 s ++10 s=+10 s ++11 s=+11 s ++12 s=+12 s ++13 s=+13 s ++14 s=+14 s ++15 s=+15 s ++16 s=+16 s ++17 s=+17 s ++18 s=+18 s ++19 s=+19 s ++20 s=+20 s ++21 s=+21 s ++22 s=+22 s ++23 s=+23 s ++24 s=+24 s ++25 s=+25 s ++26 s=+26 s ++27 s=+27 s ++28 s=+28 s ++29 s=+29 s ++30 s=+30 s ++31 s=+31 s 0ms=0ms 10ms=10ms 20ms=20ms @@ -1000,6 +1033,7 @@ Edit scrolling factor=Edit scrolling factor Edit fake=Edit fake segment Editor options=Options EditorShowBGChangesPlay=Show Backgrounds +EditRecordModeLeadIn=Record Lead In Erase step timing=Erase step timing Effect=Effect EffectsReceptor=Effects diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index a5686ff6c9..2d823d19dc 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -4132,7 +4132,7 @@ PlayMusic=false TimerSeconds=-1 ShowStyleIcon=false -LineNames="1,2,3,4,5,6,R1,R2,7,8,9,10,Attacks,11,12,13,14,15,16" +LineNames="1,2,3,4,5,6,R1,R2,7,8,9,10,Attacks,11,12,13,14,15,16,lead_in" # uses legacy speed line Line1="list,Speed" Line2="list,Accel" @@ -4153,6 +4153,7 @@ Line13="list,Assist" Line14="list,Rate" Line15="list,AutoAdjust" Line16="conf,EditorShowBGChangesPlay" +Linelead_in="conf,EditRecordModeLeadIn" OutCancelCommand= [StepsDisplayEdit] diff --git a/Themes/default/metrics.ini b/Themes/default/metrics.ini index f1a18125ff..9805deacea 100644 --- a/Themes/default/metrics.ini +++ b/Themes/default/metrics.ini @@ -2062,7 +2062,7 @@ TimerOnCommand=visible,false [ScreenPractice] [ScreenEditOptions] -LineNames="1,2,3,4,5,6,R1,R2,7,8,9,10,Attacks,11,12,13,14,15,16,SF" +LineNames="1,2,3,4,5,6,R1,R2,7,8,9,10,Attacks,11,12,13,14,15,16,SF,lead_in" Line1="lua,ArbitrarySpeedMods()" LineSF="lua,OptionRowScreenFilter()" diff --git a/src/PrefsManager.cpp b/src/PrefsManager.cpp index 91f31fe22a..a17b113221 100644 --- a/src/PrefsManager.cpp +++ b/src/PrefsManager.cpp @@ -258,6 +258,7 @@ PrefsManager::PrefsManager() : m_fDebounceCoinInputTime ( "DebounceCoinInputTime", 0 ), m_fPadStickSeconds ( "PadStickSeconds", 0 ), + m_EditRecordModeLeadIn("EditRecordModeLeadIn", 1.0f), m_bForceMipMaps ( "ForceMipMaps", false ), m_bTrilinearFiltering ( "TrilinearFiltering", false ), m_bAnisotropicFiltering ( "AnisotropicFiltering", false ), diff --git a/src/PrefsManager.h b/src/PrefsManager.h index c95692a1e6..4caee949fc 100644 --- a/src/PrefsManager.h +++ b/src/PrefsManager.h @@ -253,6 +253,9 @@ public: // after pressed. Preference m_fPadStickSeconds; + // Lead in time before recording starts in edit mode. + Preference m_EditRecordModeLeadIn; + // Useful for non 4:3 displays and resolutions < 640x480 where texels don't // map directly to pixels. Preference m_bForceMipMaps; diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 8f5d5221fe..f9d17bc839 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -3200,9 +3200,9 @@ void ScreenEdit::TransitionEditState( EditState em ) if( bStateChanging ) AdjustSync::ResetOriginalSyncData(); - /* Give a 1 second lead-in. If we're loading Player, this must be done first. + /* Give a lead-in. If we're loading Player, this must be done first. * Also be sure to get the right timing. */ - float fSeconds = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_iStartPlayingAt) ) - 1; + float fSeconds = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_iStartPlayingAt) ) - PREFSMAN->m_EditRecordModeLeadIn; GAMESTATE->UpdateSongPosition( fSeconds, GetAppropriateTiming(), RageZeroTimer ); GAMESTATE->m_bGameplayLeadIn.Set( false ); diff --git a/src/ScreenOptionsMasterPrefs.cpp b/src/ScreenOptionsMasterPrefs.cpp index 8dca990655..c8395978e8 100644 --- a/src/ScreenOptionsMasterPrefs.cpp +++ b/src/ScreenOptionsMasterPrefs.cpp @@ -680,6 +680,16 @@ static void GlobalOffsetSeconds( int &sel, bool ToSel, const ConfOption *pConfOp MoveMap( sel, pConfOption, ToSel, mapping, ARRAYLEN(mapping) ); } +static void EditRecordModeLeadIn(int &sel, bool to_sel, const ConfOption* conf_option) +{ + float mapping[32]; + for(int i= 0; i < 32; ++i) + { + mapping[i]= static_cast(i); + } + MoveMap(sel, conf_option, to_sel, mapping, ARRAYLEN(mapping)); +} + static vector g_ConfOptions; static void InitializeConfOptions() { @@ -725,6 +735,14 @@ static void InitializeConfOptions() ADD( ConfOption( "AutogenGroupCourses", MovePref, "Off","On" ) ); ADD( ConfOption( "FastLoad", MovePref, "Off","On" ) ); ADD( ConfOption( "FastLoadAdditionalSongs", MovePref, "Off","On" ) ); + { + ConfOption c("EditRecordModeLeadIn", EditRecordModeLeadIn); + for(int i= 0; i < 32; ++i) + { + c.AddOption(ssprintf("%+i s", i)); + } + ADD(c); + } // Background options ADD( ConfOption( "RandomBackgroundMode", MovePref, "Off","Animations","Random Movies" ) ); From 603b9d95fc426cee7a9a2d49a30a78fcb234cfd5 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 1 Feb 2015 10:09:59 -0500 Subject: [PATCH 30/40] Allow OnlyShowActiveSection wheel to include current section title IIDX hides inactive sections, but unlike the current behavior of our OnlyShowActiveSection metric (based on certain Pumps according to DaisukeMaster's comment), it retains the section title so that the section can be closed normally. New metric: MusicWheel.HideActiveSectionTitle, defaulting to true in _fallback. A bit unfortunate, but keeps backward compatibility with the existing behavior of OnlyShowActiveSection. If neither metric is true, wheel displays all titles plus the contents of the active section (if any). If OSAS is true but HAST is false, wheel displays the active section title plus its contents, or all titles if no section is active. If both are true, wheel displays only the active section's contents, or all titles if no section is active. --- Themes/_fallback/metrics.ini | 1 + src/MusicWheel.cpp | 18 +++++++++++++----- src/MusicWheel.h | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 2d823d19dc..9d961f7f39 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -906,6 +906,7 @@ SwitchSeconds=0.10 RandomPicksLockedSongs=true UseSectionsWithPreferredGroup=false OnlyShowActiveSection=false +HideActiveSectionTitle=true RemindWheelPositions=false # RouletteSwitchSeconds=0.05 diff --git a/src/MusicWheel.cpp b/src/MusicWheel.cpp index e1a3b9b641..c70537a21b 100644 --- a/src/MusicWheel.cpp +++ b/src/MusicWheel.cpp @@ -87,6 +87,7 @@ void MusicWheel::Load( RString sType ) SHOW_EASY_FLAG .Load(sType,"UseEasyMarkerFlag"); USE_SECTIONS_WITH_PREFERRED_GROUP .Load(sType,"UseSectionsWithPreferredGroup"); HIDE_INACTIVE_SECTIONS .Load(sType,"OnlyShowActiveSection"); + HIDE_ACTIVE_SECTION_TITLE .Load(sType,"HideActiveSectionTitle"); REMIND_WHEEL_POSITIONS .Load(sType,"RemindWheelPositions"); vector vsModeChoiceNames; split( MODE_MENU_CHOICE_NAMES, ",", vsModeChoiceNames ); @@ -1358,14 +1359,21 @@ void MusicWheel::SetOpenSection( RString group ) for( unsigned i = 0; i < from.size(); ++i ) { MusicWheelItemData &d = *from[i]; - // Don't show songs outside the current group + + // Hide songs/courses which are not in the active section if( (d.m_Type == WheelItemDataType_Song || d.m_Type == WheelItemDataType_Course) && !d.m_sText.empty() && d.m_sText != group ) continue; - // In certain situations (e.g. simulating Pump it Up), themes may - // want to hide inactive group headings as well. - if( HIDE_INACTIVE_SECTIONS && d.m_Type == WheelItemDataType_Section && group != "" ) - continue; + + // In certain situations (e.g. simulating Pump it Up or IIDX), + // themes may want to hide inactive section headings as well. + if( HIDE_INACTIVE_SECTIONS && d.m_Type == WheelItemDataType_Section && group != "" ) { + // Based on the HideActiveSectionTitle metric, we either + // hide all section titles, or only those which are not + // currently open. + if ( HIDE_ACTIVE_SECTION_TITLE || d.m_sText != group ) + continue; + } // If AUTO_SET_STYLE, hide courses that prefer a style that isn't available. if( d.m_Type == WheelItemDataType_Course && CommonMetrics::AUTO_SET_STYLE ) diff --git a/src/MusicWheel.h b/src/MusicWheel.h index d0fb3c107f..83c20aa29f 100644 --- a/src/MusicWheel.h +++ b/src/MusicWheel.h @@ -90,6 +90,7 @@ protected: // sm-ssc additions: ThemeMetric USE_SECTIONS_WITH_PREFERRED_GROUP; ThemeMetric HIDE_INACTIVE_SECTIONS; + ThemeMetric HIDE_ACTIVE_SECTION_TITLE; ThemeMetric REMIND_WHEEL_POSITIONS; ThemeMetric ROULETTE_COLOR; ThemeMetric RANDOM_COLOR; From 5a7199d8fede0e074d8abcabd9af3a08b77de5ec Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Sun, 1 Feb 2015 09:35:16 -0700 Subject: [PATCH 31/40] =?UTF-8?q?EditRecordModeLeadIn=20translations=20fro?= =?UTF-8?q?m=20Jaros=C5=82aw=20Pietras.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Themes/_fallback/Languages/pl.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Themes/_fallback/Languages/pl.ini b/Themes/_fallback/Languages/pl.ini index ef9712cf90..0b20d9cb48 100644 --- a/Themes/_fallback/Languages/pl.ini +++ b/Themes/_fallback/Languages/pl.ini @@ -361,6 +361,7 @@ Edit Courses=Edycja Maratonów Edit Songs/Steps=Edycja Piosenek Practice Songs/Steps=Testowanie Piosenek EditorShowBGChangesPlay=Wyświetlanie tła piosenki podczas gry. +EditRecordModeLeadIn=Ustaw czas przejścia przed rozpoczęciem nagrywania. Effect=Efekt EffectsReceptor=Dodaje alternatywne zachowanie dla receptorów i strzałek. EffectsArrow=Dodaje alternatywne zachowanie dla receptorów i strzałek. @@ -898,6 +899,7 @@ Edit scrolling factor=Edycja parametru przesuwania Edit fake=Modyfikacja segmentu Fake Editor options=Opcje EditorShowBGChangesPlay=Pokaż tła +EditRecordModeLeadIn=Nagrywaj przejście Erase step timing=Skasuj timing Effect=Efekt EffectsReceptor=Efekty na Receptorach From 6570db599cfd3c9b9325d199e9cd3983562f6471 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Sun, 1 Feb 2015 17:01:15 -0700 Subject: [PATCH 32/40] AMV animation state system added. --- Docs/Changelog_sm5.txt | 5 + Docs/Luadoc/Lua.xml | 17 ++ Docs/Luadoc/LuaDocumentation.xml | 57 +++++ src/ActorMultiVertex.cpp | 405 +++++++++++++++++++++++++++++++ src/ActorMultiVertex.h | 43 +++- src/Sprite.cpp | 26 +- 6 files changed, 534 insertions(+), 19 deletions(-) diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 3da66d0f3f..5a1990f4e5 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,6 +4,11 @@ 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/02/01 +---------- +* [ActorMultiVertex] Animation state system added. AMVs can now have + animated textures controlled by states written in lua. [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..3f4d8e4006 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -489,22 +489,39 @@
+ + + + + + + + + + + + + + + + + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 23eaa72ec0..f6b128d2d4 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -1517,6 +1517,63 @@ 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. +
+ + 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 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. diff --git a/src/ActorMultiVertex.cpp b/src/ActorMultiVertex.cpp index b3c1102311..4162d997fc 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,10 @@ ActorMultiVertex::ActorMultiVertex() _splines[i].redimension(3); _splines[i].m_owned_by_actor= true; } + _skip_next_update= true; + _use_animation_state= false; + _secs_into_state= 0.0f; + _cur_state= 0; } ActorMultiVertex::~ActorMultiVertex() @@ -86,6 +92,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 +384,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) + { + _Texture->DecodeSeconds(max(0, time_passed)); + } +} + void ActorMultiVertex::SetCurrentTweenStart() { AMV_start= AMV_current; @@ -719,6 +923,189 @@ 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); + 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(1)-1); + COMMON_RETURN_SELF; + } + static int SetTexture( T* p, lua_State *L ) { RageTexture *Texture = Luna::check(L, 1); @@ -763,6 +1150,24 @@ 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); + // Copy from RageTexture ADD_METHOD( SetTexture ); ADD_METHOD( GetTexture ); diff --git a/src/ActorMultiVertex.h b/src/ActorMultiVertex.h index 5b598cb310..1da004c7df 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,39 @@ 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; + virtual void PushSelf( lua_State *L ); private: @@ -130,6 +166,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/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; } From 2d4c431c3ef5f0089f8cc041acf7437b2eff19a8 Mon Sep 17 00:00:00 2001 From: freem Date: Sun, 1 Feb 2015 23:24:31 -0600 Subject: [PATCH 33/40] 32-bit verinc.exe; I don't know what you guys were thinking --- src/archutils/Win32/verinc.exe | Bin 98304 -> 2560 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/archutils/Win32/verinc.exe b/src/archutils/Win32/verinc.exe index 9d1bda4622e876bd6ddceccc4eee9ad402d1dd56..b4d83adbb69d91970ef8c8581ff8d012fce9f31a 100644 GIT binary patch literal 2560 zcmeHIPiP!f82{ERvQ5)uEjdW>&)aneVu(wM9wat0hmQ2n;%E)jicBUmvzw9GnPFzO zi3hc+7Yl=UNJ#_*d$LFm74}eJ(x$zM0ZYg4d-GxwDOJ>q;)CD! z``-8OeeZkA?1|H>FaQ8$M4khr$h2fG?=Rj)_weIi48!O9F4fY~@k_N==IxOAf!7Z7 z4l{Ju^&&QBv7qZR+hs>zJjptqX*CZV7;K2rsseDl^uV5j`YYLUM!z6Cde&Idf_u_$+N6DOjBQipt0gQ3&pB0tb{b$&Wti=eA9HZe=Uvz=J zW1y?fHcdU!(It})4Lok|Xg~&7L(gAYGx3j0Tcyp)0Mtdz}<&?0@%GomsUw*n9h;I)Oi3r1 zbecy*-MOaW`>+5{*skGpO^ekrvpHWM?7E@db}f@Rp4(=I=Y|mzMK!nEVbe@<%;yI2 z+h+wXGv@U`1dabL7(aU!M%SgV{%67?yK^^u_d6Y{-QAr_EChB$ z3f&jU@eJJ&dKd*Qp||R+{`C|1747-G&j0`b literal 98304 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 vcrDm@>|*kH01|o-GI|J7+52r From 989ea5d86230a8ce2f7050cdb3488888ffebda17 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Mon, 2 Feb 2015 09:19:51 -0700 Subject: [PATCH 34/40] Added script function for finding what needs to be translated. --- Docs/Changelog_sm5.txt | 12 ++++ .../Scripts/find_missing_lang_strings.lua | 64 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 Themes/_fallback/Scripts/find_missing_lang_strings.lua diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 54d5565f51..cd12b3eda8 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,6 +4,18 @@ 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/02/02 +---------- +* [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 + like this: + find_missing_strings_in_theme_translations("_fallback", "en.ini") + The first arg is the folder name of the theme to look at. + 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] + 2015/01/31 ---------- * [EditMode] Want more lead in time before recording starts? diff --git a/Themes/_fallback/Scripts/find_missing_lang_strings.lua b/Themes/_fallback/Scripts/find_missing_lang_strings.lua new file mode 100644 index 0000000000..3d4584558e --- /dev/null +++ b/Themes/_fallback/Scripts/find_missing_lang_strings.lua @@ -0,0 +1,64 @@ +-- All other languages are compared to the master language to determine what is +-- missing and what is unused. +-- The list of missing strings for a language is saved to lang_missing.ini, +-- where lang is the name of the language. +-- The list of unused strings is saved to lang_unused.ini. + +function find_missing_strings_in_theme_translations(theme_name, master_name) + local lang_folder= "Themes/"..theme_name.."/Languages/" + local master_language= IniFile.ReadFile(lang_folder .. master_name) + local other_languages= {} + local language_names= FILEMAN:GetDirListing(lang_folder) + -- Load all languages. + for i, name in ipairs(language_names) do + if name ~= master_name and not name:find("missing") + and not name:find("unused") then + other_languages[#other_languages+1]= { + name= name, data= IniFile.ReadFile(lang_folder .. name)} + end + end + -- Find out what is missing from each language. + local missing_list= {} + local function find_missing_in_section(section_name, section) + local function add_str_to_missing(str_name, str_value) + for i, other in ipairs(other_languages) do + if not other.data[section_name] or + not other.data[section_name][str_name] then + if not missing_list[other.name] then + missing_list[other.name]= {} + end + if not missing_list[other.name][section_name] then + missing_list[other.name][section_name]= {} + end + missing_list[other.name][section_name][str_name]= str_value + end + end + end + foreach_ordered(section, add_str_to_missing) + end + foreach_ordered(master_language, find_missing_in_section) + local function save_missing_data(lang_name, data) + IniFile.WriteFile( + lang_folder .. lang_name:sub(1, -5) .. "_missing.ini", data) + end + foreach_ordered(missing_list, save_missing_data) + -- Find out what is extra in each language. + for i, other in ipairs(other_languages) do + local unused= {} + local function find_unused_in_section(section_name, section) + local function add_str_to_unused(str_name, str_value) + if not master_language[section_name] or + not master_language[section_name][str_name] then + if not unused[section_name] then + unused[section_name]= {} + end + unused[section_name][str_name]= str_value + end + end + foreach_ordered(section, add_str_to_unused) + end + foreach_ordered(other.data, find_unused_in_section) + IniFile.WriteFile( + lang_folder .. other.name:sub(1, -5) .. "_unused.ini", unused) + end +end From c76b40ae99adc941650ce99381fe21dfa8206b61 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Mon, 2 Feb 2015 10:31:12 -0700 Subject: [PATCH 35/40] Changed ScreenSyncOverlay to not stick off screen. --- src/ScreenSyncOverlay.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ScreenSyncOverlay.cpp b/src/ScreenSyncOverlay.cpp index be23fc1c08..8c87f6bb0f 100644 --- a/src/ScreenSyncOverlay.cpp +++ b/src/ScreenSyncOverlay.cpp @@ -29,13 +29,14 @@ void ScreenSyncOverlay::Init() Screen::Init(); m_quad.SetDiffuse( RageColor(0,0,0,0) ); - m_quad.SetHorizAlign( align_left ); - m_quad.SetXY( SCREEN_CENTER_X+10, SCREEN_TOP+100 ); + m_quad.SetHorizAlign(align_right); + m_quad.SetVertAlign(align_top); + m_quad.SetXY(SCREEN_WIDTH, SCREEN_TOP); this->AddChild( &m_quad ); m_textHelp.LoadFromFont( THEME->GetPathF("Common", "normal") ); m_textHelp.SetHorizAlign( align_left ); - m_textHelp.SetXY( SCREEN_CENTER_X+20, SCREEN_TOP+100 ); + m_textHelp.SetVertAlign(align_top); m_textHelp.SetDiffuseAlpha( 0 ); m_textHelp.SetShadowLength( 2 ); m_textHelp.SetText( @@ -48,6 +49,8 @@ void ScreenSyncOverlay::Init() MACHINE_OFFSET.GetValue()+":\n" " Shift + F11/F12\n" + HOLD_ALT.GetValue() ); + m_textHelp.SetXY(SCREEN_WIDTH - m_textHelp.GetZoomedWidth() - 10, + SCREEN_TOP + 10); this->AddChild( &m_textHelp ); m_quad.ZoomToWidth( m_textHelp.GetZoomedWidth()+20 ); From c400794d50da0df1d02e9cd22cadc36166f4b54b Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Mon, 2 Feb 2015 23:28:33 -0700 Subject: [PATCH 36/40] Added ForceStateUpdate and SetDecodeMovie to AMV. Fixed SetStateProperties and SetQuadState off by one errors. Added size lua functions to RageTexture. Added GetBGChanges to Song. --- Docs/Changelog_sm5.txt | 7 +++++++ Docs/Luadoc/Lua.xml | 9 +++++++++ Docs/Luadoc/LuaDocumentation.xml | 29 +++++++++++++++++++++++++++++ src/ActorMultiVertex.cpp | 22 +++++++++++++++++++--- src/ActorMultiVertex.h | 1 + src/RageTexture.cpp | 12 ++++++++++++ src/Song.cpp | 28 ++++++++++++++++++++++++++++ 7 files changed, 105 insertions(+), 3 deletions(-) diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 5a1990f4e5..0ca43e906e 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/02/02 +---------- +* [ActorMultiVertex] ForceStateUpdate and SetDecodeMovie added. [kyzentun] +* [RageTexture] GetWidth and Height functions added. + ( means Source, Texture, or Image) [kyzentun] +* [Song] GetBGChanges added. [kyzentun] + 2015/02/01 ---------- * [ActorMultiVertex] Animation state system added. AMVs can now have diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 3f4d8e4006..d9b2f8bb93 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -491,6 +491,7 @@ + @@ -510,6 +511,7 @@ + @@ -1382,6 +1384,12 @@ + + + + + + @@ -1505,6 +1513,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index f6b128d2d4..9da591575b 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -1525,6 +1525,9 @@ save yourself some time, copy this for undocumented things: {{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. @@ -1544,6 +1547,9 @@ save yourself some time, copy this for undocumented things: 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. @@ -4044,6 +4050,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. @@ -4352,6 +4376,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 4162d997fc..4420007683 100644 --- a/src/ActorMultiVertex.cpp +++ b/src/ActorMultiVertex.cpp @@ -72,6 +72,7 @@ ActorMultiVertex::ActorMultiVertex() _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; @@ -571,7 +572,7 @@ void ActorMultiVertex::Update(float fDelta) wrap(_secs_into_state, GetAnimationLengthSeconds()); } UpdateAnimationState(); - if(!skip_this_movie_update) + if(!skip_this_movie_update && _decode_movie) { _Texture->DecodeSeconds(max(0, time_passed)); } @@ -897,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; } @@ -1053,7 +1057,7 @@ public: new_states.resize(num_states); for(size_t i= 0; i < num_states; ++i) { - lua_rawgeti(L, 1, i); + lua_rawgeti(L, 1, i+1); FillStateFromLua(L, new_states[i], tex, -1); lua_pop(L, 1); } @@ -1102,7 +1106,17 @@ public: } static int SetQuadState(T* p, lua_State *L) { - p->SetQuadState(QuadStateIndex(p, L, 1), IArg(1)-1); + 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; } @@ -1167,6 +1181,8 @@ public: ADD_METHOD(RemoveQuadState); ADD_METHOD(GetQuadState); ADD_METHOD(SetQuadState); + ADD_METHOD(ForceStateUpdate); + ADD_METHOD(SetDecodeMovie); // Copy from RageTexture ADD_METHOD( SetTexture ); diff --git a/src/ActorMultiVertex.h b/src/ActorMultiVertex.h index 1da004c7df..2c4f949aec 100644 --- a/src/ActorMultiVertex.h +++ b/src/ActorMultiVertex.h @@ -146,6 +146,7 @@ public: 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 ); 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 ); From cf02a8fe509bffdb759ec000941f9f5d9a8e46a9 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Wed, 4 Feb 2015 12:04:48 -0700 Subject: [PATCH 37/40] Removed setmetatable mistake from default's ScreenFilter and ProTiming lua option rows. --- Themes/default/Scripts/02 ThemePrefs.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Themes/default/Scripts/02 ThemePrefs.lua b/Themes/default/Scripts/02 ThemePrefs.lua index c95d3fd7d5..f13ce3130f 100644 --- a/Themes/default/Scripts/02 ThemePrefs.lua +++ b/Themes/default/Scripts/02 ThemePrefs.lua @@ -116,7 +116,7 @@ end -- screen filter function OptionRowScreenFilter() - local t = { + return { Name="ScreenFilter", LayoutType = "ShowAllInRow", SelectType = "SelectOne", @@ -147,14 +147,12 @@ function OptionRowScreenFilter() end end end, - }; - setmetatable(t, t) - return t + } end -- protiming function OptionRowProTiming() - local t = { + return { Name = "ProTiming", LayoutType = "ShowAllInRow", SelectType = "SelectOne", @@ -181,8 +179,6 @@ function OptionRowProTiming() SetUserPref("UserPrefProtiming" .. ToEnumShortString(pn), bSave) end } - setmetatable(t, t) - return t end --[[ end option rows ]] From 3f429a1bcc1a6bd6c47b8d1bb00cdd905214b04a Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Wed, 4 Feb 2015 14:43:55 -0700 Subject: [PATCH 38/40] Added bgchanges format doc. --- Docs/Userdocs/bgchanges_format.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Docs/Userdocs/bgchanges_format.txt diff --git a/Docs/Userdocs/bgchanges_format.txt b/Docs/Userdocs/bgchanges_format.txt new file mode 100644 index 0000000000..574264e179 --- /dev/null +++ b/Docs/Userdocs/bgchanges_format.txt @@ -0,0 +1,22 @@ +The BGCHANGES line in a simfile is used to control what backgrounds are loaded by the simfile and when they appear. + +The data is between the colon and the semicolon. +Each entry is separated from the next by a comma. +Each entry is composed of 1 to 11 values separated by equals. +The meanings of the values are as follows: +1. start beat +2. file or folder name +3. play rate +4. Backward compatible transition type. CrossFade is used if this is not 0. +5. Backward compatible effect flag. StretchRewind is used if this is not 0. +6. Backward compatible effect flag. StretchNoLoop is used if this is not 0. +7. Name of the effect file to use. The BackgroundEffects folder will be searched for a match. +8. Name of the second file. +9. Name of the transition file to use. The BackgroundTransitions folder will be searched for a match. +10. Color string in either "1.0^0.5^0.75^0.25" or "#ff7fcf3f" form. The fourth channel is optional. +11. Second color string, same format. + +The file names (values 2 and 8) and the colors (values 10 and 11) are passed to the effect file as thread variables. Most effects do not use the second file. + + +The FGCHANGES line uses the same format, except only the start beat and first file are used (values 1 and 2). From ad877b6dcb4c06cba28e7df9724606e707fbd4ab Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Thu, 5 Feb 2015 00:37:08 -0700 Subject: [PATCH 39/40] Changed BackgroundEffects files to apply the playback rate to videos. Added GetUpdateRate to ActorFrame and Get/SetDecodeMovie to Sprite. Hope nobody is caught off guard by checkerboard no longer playing videos back at 4x rate. --- BackgroundEffects/Centered.lua | 8 +++++- BackgroundEffects/Checkerboard1File2x2.lua | 24 ++++++++++++++--- BackgroundEffects/Checkerboard2File2x2.lua | 27 ++++++++++++++++--- BackgroundEffects/Checkerboard2x2.lua | 16 ++++++++--- BackgroundEffects/File2Flash.lua | 16 +++++++++-- BackgroundEffects/File2Normal.lua | 16 +++++++++-- BackgroundEffects/Kaleidoscope2x2.lua | 23 +++++++++++++--- BackgroundEffects/SongBgWithMovieViz.lua | 12 ++++++++- BackgroundEffects/StretchNoLoop.lua | 9 ++++++- BackgroundEffects/StretchNormal.lua | 4 +++ BackgroundEffects/StretchNormalAlignLeft.lua | 8 +++++- BackgroundEffects/StretchNormalBlue.lua | 8 +++++- BackgroundEffects/StretchNormalGreen.lua | 8 +++++- BackgroundEffects/StretchNormalRed.lua | 8 +++++- BackgroundEffects/StretchPaused.lua | 8 +++++- BackgroundEffects/StretchRewind.lua | 4 +++ BackgroundEffects/UpperLeft.lua | 8 +++++- BackgroundEffects/Visualization2File.lua | 16 +++++++++-- BackgroundEffects/Visualization2FileFlash.lua | 20 +++++++++++--- Docs/Changelog_sm5.txt | 11 +++++++- Docs/Luadoc/Lua.xml | 4 +++ Docs/Luadoc/LuaDocumentation.xml | 12 +++++++++ src/Actor.h | 1 + src/ActorFrame.cpp | 2 ++ src/ActorFrame.h | 1 + src/ActorMultiVertex.cpp | 2 ++ src/Background.cpp | 14 +++++++--- src/Sprite.cpp | 12 ++++++++- src/Sprite.h | 2 ++ 29 files changed, 263 insertions(+), 41 deletions(-) diff --git a/BackgroundEffects/Centered.lua b/BackgroundEffects/Centered.lua index 5ee918a626..2e8c7d3d1d 100644 --- a/BackgroundEffects/Centered.lua +++ b/BackgroundEffects/Centered.lua @@ -2,7 +2,13 @@ local Color1 = color(Var "Color1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;diffuse,Color1;effectclock,"music"); + OnCommand= function(self) + self:xy(_screen.cx, _screen.cy):diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/Checkerboard1File2x2.lua b/BackgroundEffects/Checkerboard1File2x2.lua index 6ecca9e692..0352305d10 100644 --- a/BackgroundEffects/Checkerboard1File2x2.lua +++ b/BackgroundEffects/Checkerboard1File2x2.lua @@ -1,16 +1,32 @@ local Color1 = color(Var "Color1"); local a = LoadActor(Var "File1") .. { - OnCommand=cmd(cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color1;effectclock,"music"); + OnCommand= function(self) + self:cropto(_screen.w/2, _screen.h/2):diffuse(Color1) + :effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; local t = Def.ActorFrame { a .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; + a .. { OnCommand=function(self) + self:x(scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; + a .. { OnCommand=function(self) + self:x(scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; + a .. { OnCommand=function(self) + self:x(scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; }; return t; diff --git a/BackgroundEffects/Checkerboard2File2x2.lua b/BackgroundEffects/Checkerboard2File2x2.lua index 515341cc49..eadb73f18b 100644 --- a/BackgroundEffects/Checkerboard2File2x2.lua +++ b/BackgroundEffects/Checkerboard2File2x2.lua @@ -2,12 +2,25 @@ local Color1 = color(Var "Color1"); local Color2 = color(Var "Color2"); local a1 = LoadActor(Var "File1") .. { - OnCommand=cmd(cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color1;effectclock,"music"); + OnCommand= function(self) + self:cropto(SCREEN_WIDTH/2,SCREEN_HEIGHT/2):diffuse(Color1) + :effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; local a2 = LoadActor(Var "File2") .. { - OnCommand=cmd(cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color2;effectclock,"music"); + OnCommand= function(self) + self:cropto(SCREEN_WIDTH/2,SCREEN_HEIGHT/2):diffuse(Color2):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; @@ -15,8 +28,14 @@ local a2 = LoadActor(Var "File2") .. { local t = Def.ActorFrame { a1 .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; a2 .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a2 .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a1 .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; + a2 .. { OnCommand=function(self) + self:x(scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; + a1 .. { OnCommand=function(self) + self:x(scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; }; return t; diff --git a/BackgroundEffects/Checkerboard2x2.lua b/BackgroundEffects/Checkerboard2x2.lua index 4183d12762..399ea05548 100644 --- a/BackgroundEffects/Checkerboard2x2.lua +++ b/BackgroundEffects/Checkerboard2x2.lua @@ -3,10 +3,18 @@ local Color2 = color(Var "Color2"); -- Alternating files being played back at once local t = Def.ActorFrame { - LoadActor(Var "File1") .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM);cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color1;rate,0.25) }; - LoadActor(Var "File2") .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM);cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color2;rate,0.25) }; - LoadActor(Var "File2") .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM);cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color1;rate,0.25) }; - LoadActor(Var "File1") .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM);cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color2;rate,0.25) }; + LoadActor(Var "File1") .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM);cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color1) }; + LoadActor(Var "File2") .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM);cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color2) }; + LoadActor(Var "File2") .. { + OnCommand=function(self) + self:x(scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)):cropto(SCREEN_WIDTH/2,SCREEN_HEIGHT/2):diffuse(Color1) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; + LoadActor(Var "File1") .. { + OnCommand=function(self) + self:x(scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)):cropto(SCREEN_WIDTH/2,SCREEN_HEIGHT/2):diffuse(Color2) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; }; return t diff --git a/BackgroundEffects/File2Flash.lua b/BackgroundEffects/File2Flash.lua index dd98d708ff..e2b649fc20 100644 --- a/BackgroundEffects/File2Flash.lua +++ b/BackgroundEffects/File2Flash.lua @@ -4,14 +4,26 @@ local Color2 = color(Var "Color2"); local t = Def.ActorFrame {}; t[#t+1] = LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; if Var("File2") ~= nil then t[#t+1] = LoadActor(Var("File2")) .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color2;effectclock,"music";linear,1;diffusealpha,0); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color2):effectclock("music"):linear(1):diffusealpha(0) + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/File2Normal.lua b/BackgroundEffects/File2Normal.lua index 85fd768f48..5125fda9c0 100644 --- a/BackgroundEffects/File2Normal.lua +++ b/BackgroundEffects/File2Normal.lua @@ -4,14 +4,26 @@ local Color2 = color(Var "Color2"); local t = Def.ActorFrame {}; t[#t+1] = LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) +self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; if Var("File2") ~= nil then t[#t+1] = LoadActor(Var("File2")) .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/Kaleidoscope2x2.lua b/BackgroundEffects/Kaleidoscope2x2.lua index 834dd4f7a4..a2994bfe52 100644 --- a/BackgroundEffects/Kaleidoscope2x2.lua +++ b/BackgroundEffects/Kaleidoscope2x2.lua @@ -1,16 +1,31 @@ local Color1 = color(Var "Color1"); local a = LoadActor(Var "File1") .. { - OnCommand=cmd(cropto,SCREEN_WIDTH/2,SCREEN_HEIGHT/2;diffuse,Color1;zoomx,self:GetZoomX()*-1;zoomy,self:GetZoomY()*-1;effectclock,"music"); + OnCommand= function(self) + self:cropto(SCREEN_WIDTH/2,SCREEN_HEIGHT/2):diffuse(Color1):zoomx(self:GetZoomX()*-1):zoomy(self:GetZoomY()*-1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; local t = Def.ActorFrame { a .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a .. { OnCommand=cmd(x,scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; - a .. { OnCommand=cmd(x,scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT);y,scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)); }; + a .. { OnCommand=function(self) + self:x(scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(1,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; + a .. { OnCommand=function(self) + self:x(scale(1,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; + a .. { OnCommand=function(self) + self:x(scale(3,0,4,SCREEN_LEFT,SCREEN_RIGHT)):y(scale(3,0,4,SCREEN_TOP,SCREEN_BOTTOM)) + if self.SetDecodeMovie then self:SetDecodeMovie(false) end + end }; }; return t; diff --git a/BackgroundEffects/SongBgWithMovieViz.lua b/BackgroundEffects/SongBgWithMovieViz.lua index 7ae5d6d02f..800f5c8460 100644 --- a/BackgroundEffects/SongBgWithMovieViz.lua +++ b/BackgroundEffects/SongBgWithMovieViz.lua @@ -9,11 +9,21 @@ local t = Def.ActorFrame { self:scale_or_crop_background(); self:diffuse(Color1) self:effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end end; }; LoadActor(Var "File1") .. { - OnCommand=cmd(blend,"BlendMode_Add";x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color2;effectclock,"music"); + OnCommand=function(self) + self:blend("BlendMode_Add"):x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color2):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end; GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/StretchNoLoop.lua b/BackgroundEffects/StretchNoLoop.lua index 0051588fea..84e630326d 100644 --- a/BackgroundEffects/StretchNoLoop.lua +++ b/BackgroundEffects/StretchNoLoop.lua @@ -6,7 +6,14 @@ local t = Def.ActorFrame { self:xy(SCREEN_CENTER_X,SCREEN_CENTER_Y) self:scale_or_crop_background() self:diffuse(Color1) - if self.loop ~= nil then + -- The playback rate in the simfile is used to set the update rate + -- on the ActorFrame, but effectclock("music") causes the sprite to + -- ignore the passed in delta time and use the music time instead. + -- So this passes the update rate in to the texture. -Kyz + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + if self.loop then self:loop(false) -- make videos start at beginning to prevent sticking on last frame self:position(0) diff --git a/BackgroundEffects/StretchNormal.lua b/BackgroundEffects/StretchNormal.lua index c814a02284..4fc137bb90 100644 --- a/BackgroundEffects/StretchNormal.lua +++ b/BackgroundEffects/StretchNormal.lua @@ -7,6 +7,10 @@ local t = Def.ActorFrame { self:scale_or_crop_background(); self:diffuse(Color1) self:effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end end; GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); diff --git a/BackgroundEffects/StretchNormalAlignLeft.lua b/BackgroundEffects/StretchNormalAlignLeft.lua index b145ef69d2..3ea7362832 100644 --- a/BackgroundEffects/StretchNormalAlignLeft.lua +++ b/BackgroundEffects/StretchNormalAlignLeft.lua @@ -3,7 +3,13 @@ local Color1 = color(Var "Color1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/StretchNormalBlue.lua b/BackgroundEffects/StretchNormalBlue.lua index b5b7c629fb..2deae2baff 100644 --- a/BackgroundEffects/StretchNormalBlue.lua +++ b/BackgroundEffects/StretchNormalBlue.lua @@ -3,7 +3,13 @@ local Color1 = color("0,0,1,1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/StretchNormalGreen.lua b/BackgroundEffects/StretchNormalGreen.lua index f90436b90f..225f6ac936 100644 --- a/BackgroundEffects/StretchNormalGreen.lua +++ b/BackgroundEffects/StretchNormalGreen.lua @@ -3,7 +3,13 @@ local Color1 = color("0,1,0,1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/StretchNormalRed.lua b/BackgroundEffects/StretchNormalRed.lua index caa8ccbfa9..56b7de63f5 100644 --- a/BackgroundEffects/StretchNormalRed.lua +++ b/BackgroundEffects/StretchNormalRed.lua @@ -3,7 +3,13 @@ local Color1 = color("1,0,0,1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/StretchPaused.lua b/BackgroundEffects/StretchPaused.lua index 41c81bdd52..46a534f398 100644 --- a/BackgroundEffects/StretchPaused.lua +++ b/BackgroundEffects/StretchPaused.lua @@ -2,7 +2,13 @@ local Color1 = color(Var "Color1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;pause;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):pause():effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/StretchRewind.lua b/BackgroundEffects/StretchRewind.lua index 9d649131f2..4f3ccf23d2 100644 --- a/BackgroundEffects/StretchRewind.lua +++ b/BackgroundEffects/StretchRewind.lua @@ -10,6 +10,10 @@ local t = Def.ActorFrame { self:position(0) end self:effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end end; GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); diff --git a/BackgroundEffects/UpperLeft.lua b/BackgroundEffects/UpperLeft.lua index 0987f8f1c4..7e04b9992e 100644 --- a/BackgroundEffects/UpperLeft.lua +++ b/BackgroundEffects/UpperLeft.lua @@ -3,7 +3,13 @@ local Color1 = color(Var "Color1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/Visualization2File.lua b/BackgroundEffects/Visualization2File.lua index c729ed7b9d..5212c256b1 100644 --- a/BackgroundEffects/Visualization2File.lua +++ b/BackgroundEffects/Visualization2File.lua @@ -3,13 +3,25 @@ local Color2 = color(Var "Color2"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; LoadActor(Var "File2") .. { - OnCommand=cmd(blend,"BlendMode_Add";x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scaletoclipped,SCREEN_WIDTH,SCREEN_HEIGHT;diffuse,Color2;effectclock,"music"); + OnCommand=function(self) + self:blend("BlendMode_Add"):x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scaletoclipped(SCREEN_WIDTH,SCREEN_HEIGHT):diffuse(Color2):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/Visualization2FileFlash.lua b/BackgroundEffects/Visualization2FileFlash.lua index 9bc9f3e13f..d01a22a4d6 100644 --- a/BackgroundEffects/Visualization2FileFlash.lua +++ b/BackgroundEffects/Visualization2FileFlash.lua @@ -4,14 +4,26 @@ local Color2 = color(Var "Color2"); local t = Def.ActorFrame {}; t[#t+1] = LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;effectclock,"music"); - GainFocusCommand=cmd(play); - LoseFocusCommand=cmd(pause); + OnCommand=function(self) + self:x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color1):effectclock("music") + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, + GainFocusCommand=cmd(play); + LoseFocusCommand=cmd(pause); }; if Var("File2") ~= nil then t[#t+1] = LoadActor(Var("File2")) .. { - OnCommand=cmd(blend,"BlendMode_Add";x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color2;effectclock,"music";linear,1;diffusealpha,0); + OnCommand=function(self) + self:blend("BlendMode_Add"):x(SCREEN_CENTER_X):y(SCREEN_CENTER_Y):scale_or_crop_background():diffuse(Color2):effectclock("music"):linear(1):diffusealpha(0) + -- Explanation in StretchNoLoop.lua. + if self.GetTexture then + self:GetTexture():rate(self:GetParent():GetUpdateRate()) + end + end, GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index a0725f1321..c4105c1886 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,9 +4,18 @@ 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/02/04 +---------- +* [ActorFrame] GetUpdateRate added. [kyzentun] +* [Background] Using the wrong transition name no longer crashes. + Playback rate is applied to videos. + Checkerboard and other BackgroundEffects that use the same file twice no + longer play videos back at multiplied speed. [kyzentun] +* [Sprite] Get/SetDecodeMovie added. [kyzentun] + 2015/02/02 ---------- -* [ActorMultiVertex] ForceStateUpdate and SetDecodeMovie added. [kyzentun] +* [ActorMultiVertex] ForceStateUpdate and Get/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 diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 8379721827..4e15aac781 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -454,6 +454,7 @@ + @@ -499,6 +500,7 @@ + @@ -592,6 +594,7 @@
+ @@ -604,6 +607,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 0024206366..8b171b3261 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -1429,6 +1429,9 @@ save yourself some time, copy this for undocumented things: Returns the number of children in the ActorFrame. + + Gets the update function's rate. + Plays the sCommandName command on the ActorFrame's children. @@ -1561,6 +1564,9 @@ save yourself some time, copy this for undocumented things: Returns the id of the current state. + + Gets whether the AMV should call the decode function for its texture during updates. + Sets whether the AMV should call the decode function for its texture during updates. @@ -4753,6 +4759,9 @@ save yourself some time, copy this for undocumented things: Returns the length of the animation in seconds. + + Gets whether the Sprite should call the decode function for its texture during updates. + Return the number of states this Sprite has. @@ -4793,6 +4802,9 @@ save yourself some time, copy this for undocumented things: Turns off the custom pos coords for the sprite. + + Sets whether the Sprite should call the decode function for its texture during updates. + Set the to mode. diff --git a/src/Actor.h b/src/Actor.h index 0acd10d96a..ccce22d864 100644 --- a/src/Actor.h +++ b/src/Actor.h @@ -609,6 +609,7 @@ public: virtual float GetAnimationLengthSeconds() const { return 0; } virtual void SetSecondsIntoAnimation( float ) {} virtual void SetUpdateRate( float ) {} + virtual float GetUpdateRate() { return 1.0f; } HiddenPtr m_pLuaInstance; diff --git a/src/ActorFrame.cpp b/src/ActorFrame.cpp index 174fe1ba64..26591e1eb3 100644 --- a/src/ActorFrame.cpp +++ b/src/ActorFrame.cpp @@ -625,6 +625,7 @@ public: static int propagate( T* p, lua_State *L ) { p->SetPropagateCommands( BIArg(1) ); COMMON_RETURN_SELF; } static int fov( T* p, lua_State *L ) { p->SetFOV( FArg(1) ); COMMON_RETURN_SELF; } static int SetUpdateRate( T* p, lua_State *L ) { p->SetUpdateRate( FArg(1) ); COMMON_RETURN_SELF; } + DEFINE_METHOD(GetUpdateRate, GetUpdateRate()); static int SetFOV( T* p, lua_State *L ) { p->SetFOV( FArg(1) ); COMMON_RETURN_SELF; } static int vanishpoint( T* p, lua_State *L ) { p->SetVanishPoint( FArg(1), FArg(2) ); COMMON_RETURN_SELF; } static int GetChild( T* p, lua_State *L ) @@ -738,6 +739,7 @@ public: ADD_METHOD( propagate ); // deprecated ADD_METHOD( fov ); ADD_METHOD( SetUpdateRate ); + ADD_METHOD( GetUpdateRate ); ADD_METHOD( SetFOV ); ADD_METHOD( vanishpoint ); ADD_METHOD( GetChild ); diff --git a/src/ActorFrame.h b/src/ActorFrame.h index 978b9760ac..274da11ba4 100644 --- a/src/ActorFrame.h +++ b/src/ActorFrame.h @@ -76,6 +76,7 @@ public: virtual void HurryTweening( float factor ); void SetUpdateRate( float fUpdateRate ) { m_fUpdateRate = fUpdateRate; } + float GetUpdateRate() { return m_fUpdateRate; } void SetFOV( float fFOV ) { m_fFOV = fFOV; } void SetVanishPoint( float fX, float fY) { m_fVanishX = fX; m_fVanishY = fY; } diff --git a/src/ActorMultiVertex.cpp b/src/ActorMultiVertex.cpp index 4420007683..d59b20ad33 100644 --- a/src/ActorMultiVertex.cpp +++ b/src/ActorMultiVertex.cpp @@ -1114,6 +1114,7 @@ public: p->UpdateAnimationState(true); COMMON_RETURN_SELF; } + DEFINE_METHOD(GetDecodeMovie, _decode_movie); static int SetDecodeMovie(T* p, lua_State *L) { p->_decode_movie= BArg(1); @@ -1182,6 +1183,7 @@ public: ADD_METHOD(GetQuadState); ADD_METHOD(SetQuadState); ADD_METHOD(ForceStateUpdate); + ADD_METHOD(GetDecodeMovie); ADD_METHOD(SetDecodeMovie); // Copy from RageTexture diff --git a/src/Background.cpp b/src/Background.cpp index e70e86e6e9..d75abfbd49 100644 --- a/src/Background.cpp +++ b/src/Background.cpp @@ -765,10 +765,16 @@ void BackgroundImpl::Layer::UpdateCurBGChange( const Song *pSong, float fLastMus if( !change.m_sTransition.empty() ) { map::const_iterator lIter = mapNameToTransition.find( change.m_sTransition ); - ASSERT( lIter != mapNameToTransition.end() ); - const BackgroundTransition &bt = lIter->second; - m_pFadingBGA->RunCommandsOnLeaves( *bt.cmdLeaves ); - m_pFadingBGA->RunCommands( *bt.cmdRoot ); + if(lIter == mapNameToTransition.end()) + { + LuaHelpers::ReportScriptErrorFmt("'%s' is not the name of a BackgroundTransition file.", change.m_sTransition.c_str()); + } + else + { + const BackgroundTransition &bt = lIter->second; + m_pFadingBGA->RunCommandsOnLeaves( *bt.cmdLeaves ); + m_pFadingBGA->RunCommands( *bt.cmdRoot ); + } } } } diff --git a/src/Sprite.cpp b/src/Sprite.cpp index 51d1b238da..da98194523 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -26,6 +26,7 @@ Sprite::Sprite() m_bUsingCustomTexCoords = false; m_bUsingCustomPosCoords = false; m_bSkipNextUpdate = true; + m_DecodeMovie= true; m_EffectMode = EffectMode_Normal; m_fRememberedClipWidth = -1; @@ -62,6 +63,7 @@ Sprite::Sprite( const Sprite &cpy ): CPY( m_bUsingCustomTexCoords ); CPY( m_bUsingCustomPosCoords ); CPY( m_bSkipNextUpdate ); + CPY( m_DecodeMovie ); CPY( m_EffectMode ); memcpy( m_CustomTexCoords, cpy.m_CustomTexCoords, sizeof(m_CustomTexCoords) ); memcpy( m_CustomPosCoords, cpy.m_CustomPosCoords, sizeof(m_CustomPosCoords) ); @@ -408,7 +410,7 @@ void Sprite::Update( float fDelta ) UpdateAnimationState(); // If the texture is a movie, decode frames. - if( !bSkipThisMovieUpdate ) + if(!bSkipThisMovieUpdate && m_DecodeMovie) m_pTexture->DecodeSeconds( max(0, fTimePassed) ); // update scrolling @@ -1210,6 +1212,12 @@ public: p->SetAllStateDelays(FArg(-1)); COMMON_RETURN_SELF; } + DEFINE_METHOD(GetDecodeMovie, m_DecodeMovie); + static int SetDecodeMovie(T* p, lua_State *L) + { + p->m_DecodeMovie= BArg(1); + COMMON_RETURN_SELF; + } LunaSprite() { @@ -1235,6 +1243,8 @@ public: ADD_METHOD( SetEffectMode ); ADD_METHOD( GetNumStates ); ADD_METHOD( SetAllStateDelays ); + ADD_METHOD(GetDecodeMovie); + ADD_METHOD(SetDecodeMovie); } }; diff --git a/src/Sprite.h b/src/Sprite.h index 9c136b91f5..30da184a7c 100644 --- a/src/Sprite.h +++ b/src/Sprite.h @@ -90,6 +90,8 @@ public: void SetAllStateDelays(float fDelay); + bool m_DecodeMovie; + protected: void LoadFromTexture( RageTextureID ID ); From 8ccf1d8a3ebc22313245e7e4d3bcbbae4904e978 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Thu, 5 Feb 2015 12:09:52 -0700 Subject: [PATCH 40/40] Added HAST to changelog and find_missing_strings_in_theme_translations to docs. --- Docs/Changelog_sm5.txt | 4 ++++ Docs/Luadoc/Lua.xml | 1 + Docs/Luadoc/LuaDocumentation.xml | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index c4105c1886..f8390a8729 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -13,6 +13,10 @@ ________________________________________________________________________________ longer play videos back at multiplied speed. [kyzentun] * [Sprite] Get/SetDecodeMovie added. [kyzentun] +2015/02/03 +---------- +* [MusicWheel] HideActiveSectionTitle metric added. [djpohly] + 2015/02/02 ---------- * [ActorMultiVertex] ForceStateUpdate and Get/SetDecodeMovie added. [kyzentun] diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 4e15aac781..9fe1ca6ca5 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -212,6 +212,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 8b171b3261..08a634a4d4 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -145,6 +145,12 @@ save yourself some time, copy this for undocumented things: [02 Utilities.lua] Old name for approach. + + This function creates files in the theme's Languages folder listing all the strings that have no translation and all the strings that are unused.
+ Strings that do not have an entry in the master language are considered unused.
+ master_lang_name is the name of the ini file that contains the language with all strings used by the theme.
+ Example: find_missing_strings_in_theme_translations("my_best_theme", "en.ini") +
[02 Utilities.lua] Return the index of a true value in list.