diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index e9ccf1cc69..2263fcc511 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -8,6 +8,26 @@ ________________________________________________________________________________ StepMania 5.0 Preview 2 | 20110??? -------------------------------------------------------------------------------- +2011/06/10 +---------- +* [ScreenEdit] Split the Area Menu into the Area Menu and Alter Menu. Use the + "A" key to enter the Alter Menu when a selection of notes/rows are + highlighted. Use the Enter key to enter the old Area Menu for the options that + do not depend on selecting NoteData. [Wolfman2000] +* [ScreenEdit] Allow converting selections to Delays, Warps, or Fakes as well + as Stops. Remember, use the Alter Menu for this. [Wolfman2000] + +2011/06/09 +---------- +* [ScreenSyncOverlay] Ensure that F11 / F12 work with all charts due to Split + Timing. [Wolfman2000] + +2011/06/08 +---------- +* Any notes in a fake segment or warp segment are completely ignored for + scoring purposes. Now you can get 100% on your warping goodness! + [Wolfman2000] + 2011/06/06 ---------- * [PlayerOptions] Removed the ScoreDisplay mod choices. Use lua to make your diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 201f0b6312..4ad7b93c44 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -39,11 +39,8 @@ - - - @@ -57,6 +54,7 @@ + @@ -130,7 +128,6 @@ - @@ -143,6 +140,8 @@ + + @@ -1026,6 +1025,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1093,6 +1138,13 @@ + + + + + + + @@ -2061,5 +2113,5 @@ StepMania 5 v5.0 Preview 1a - 2011-06-04 + 2011-06-11 diff --git a/Docs/Luadoc/Lua.xsl b/Docs/Luadoc/Lua.xsl index 2861185f63..01f3657220 100644 --- a/Docs/Luadoc/Lua.xsl +++ b/Docs/Luadoc/Lua.xsl @@ -395,7 +395,12 @@ $type='bool' or $type='table' or $type='color' or - $type='Enum'"> + $type='Enum' or + $type='ThreadVariable' or + $type='LuaOptionRow' or + $type='ActorDef' or + $type='chunk' or + $type='various'"> diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 81723d3615..4bdf6c7716 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -807,12 +807,16 @@ Clear Machine Edits=Clear Machine Edits Clear Machine Stats=Clear Machine Scores Clear USB edits=Clear USB edits Clear area=Clear area +Clear clipboard=Clear clipboard CoinMode=Coin Mode CoinModeNoHome=Coin Mode CoinsPerCredit=Coins Per Credit Connection=Connection Convert pause to beats=Convert pause to beats Convert selection to pause=Convert selection to pause +Convert selection to delay=Convert selection to delay +Convert selection to warp=Convert selection to Warp Segment +Convert selection to fake=Convert selection to Fake Segment Copy=Copy CourseSortOrder=Course Sort Create New=Create New @@ -1257,7 +1261,7 @@ Enter a new min BPM.=Enter the minimum displayed BPM. Enter a new max BPM.=Enter the maximum displayed BPM. More than %d notes per measure is not allowed. This change has been reverted.=More than %d notes per measure is not allowed. This change has been reverted. No backgrounds available=No backgrounds available -EditHelpText=Up/Down:\n change beat\nLeft/Right:\n change snap\nNumber keys:\n add/remove\n tap note\nN and M keys:\n swap tap notes\nCreate hold note:\n Hold a number\n while moving\n Up or Down\nCreate roll note:\n Hold Shift,\n then create a\n hold note.\nSpace bar:\n Set area\n marker\nEnter:\n Area Menu\nEscape:\n Main Menu\nF4:\n Timing Menu\nF1:\n Show help\n +EditHelpText=Up/Down:\n change beat\nLeft/Right:\n change snap\nNumber keys:\n add/remove\n tap note\nN and M keys:\n swap tap notes\nCreate hold note:\n Hold a number\n while moving\n Up or Down\nCreate roll note:\n Hold Shift,\n then create a\n hold note.\nSpace bar:\n Set area\n marker\nT key:\n Switch Timing\nEnter:\n Area Menu\nA Key: Alter Menu\nEscape:\n Main Menu\nF4:\n Timing Menu\nF1:\n Show help\n PlayRecordHelpText=Press START to end Save successful.=Save successful. Saved as SM and DWI.=Saved as SM and DWI. @@ -1269,6 +1273,7 @@ This change creates notes past the end of the music and is not allowed.=This cha This will destroy all unsaved changes.=This will destroy all unsaved changes. Undo=Undo You must be in Song Timing Mode to edit BG Changes.=You must be in Song Timing Mode to edit BG Changes. +You must have an area selected to enter the Alter Menu.=You must have an area selected to enter the Alter Menu. Current beat=Current beat Current second=Current second diff --git a/Themes/_fallback/Scripts/01 alias.lua b/Themes/_fallback/Scripts/01 alias.lua index 126d9c69ef..e0e3841f07 100644 --- a/Themes/_fallback/Scripts/01 alias.lua +++ b/Themes/_fallback/Scripts/01 alias.lua @@ -21,3 +21,11 @@ local _screen = { cx = SCREEN_CENTER_X, cy = SCREEN_CENTER_Y } + +if Screen.String then + ScreenString = Screen.String +end + +if Screen.Metric then + ScreenMetric = Screen.Metric +end \ No newline at end of file diff --git a/Themes/_fallback/Scripts/02 Other.lua b/Themes/_fallback/Scripts/02 Other.lua index f9f2532a6f..ea544d1dd4 100644 --- a/Themes/_fallback/Scripts/02 Other.lua +++ b/Themes/_fallback/Scripts/02 Other.lua @@ -98,16 +98,15 @@ end -- Get a metric from the currently-loading screen. This is only valid while loading -- an actor, such as from File or InitCommand attributes; not from commands. -Screen = { - Metric = function ( sName ) - local sClass = Var "LoadingScreen" - return THEME:GetMetric( sClass, sName ) - end, - String = function ( sName ) - local sClass = Var "LoadingScreen"; - return THEME:GetString( sClass, sName ) - end -}; +Screen.Metric = function ( sName ) + local sClass = Var "LoadingScreen" + return THEME:GetMetric( sClass, sName ) +end + +Screen.String = function ( sName ) + local sClass = Var "LoadingScreen"; + return THEME:GetString( sClass, sName ) +end function TextBannerAfterSet(self,param) local Title=self:GetChild("Title"); diff --git a/Themes/_fallback/Scripts/03 ProductivityHelpers.lua b/Themes/_fallback/Scripts/03 ProductivityHelpers.lua index 0d9fbd2816..4297389fc6 100644 --- a/Themes/_fallback/Scripts/03 ProductivityHelpers.lua +++ b/Themes/_fallback/Scripts/03 ProductivityHelpers.lua @@ -148,7 +148,9 @@ end -- MaskSource([clearzbuffer]) -- Sets an actor up as the source for a mask. Clears zBuffer by default. function Actor:MaskSource(noclear) - self:clearzbuffer(noclear or true) + if noclear == true then + self:clearzbuffer(true) + end self:zwrite(true) self:blend('BlendMode_NoEffect') end diff --git a/Themes/_fallback/Scripts/03 ThemePrefs.lua b/Themes/_fallback/Scripts/03 ThemePrefs.lua index dfa9e3e695..90d7d73a19 100644 --- a/Themes/_fallback/Scripts/03 ThemePrefs.lua +++ b/Themes/_fallback/Scripts/03 ThemePrefs.lua @@ -150,7 +150,7 @@ function InitUserPrefs() end; function UserPrefScoringMode() - local baseChoices = { 'DDR 1stMIX', 'DDR 4thMIX', 'DDR Extreme', 'DDR SuperNOVA', 'DDR SuperNOVA 2', 'MIGS' }; --'[SSC] Radar Master' + local baseChoices = { 'DDR 1stMIX', 'DDR 4thMIX', 'DDR Extreme', 'DDR SuperNOVA', 'DDR SuperNOVA 2', 'MIGS', 'HYBRID' }; --'[SSC] Radar Master' local t = { Name = "UserPrefScoringMode"; LayoutType = "ShowAllInRow"; diff --git a/Themes/_fallback/Scripts/04 Scoring.lua b/Themes/_fallback/Scripts/04 Scoring.lua index 27a1d6155a..fd579db996 100644 --- a/Themes/_fallback/Scripts/04 Scoring.lua +++ b/Themes/_fallback/Scripts/04 Scoring.lua @@ -122,6 +122,48 @@ r['DDR Extreme'] = function(params, pss) Shared.CurrentStep[pn] = 0; -- Reset for the next song. end; end; + +----------------------------------------------------------- +--HYBRID Scoring +----------------------------------------------------------- +r['HYBRID'] = function(params, pss) + local multLookup = + { + ['TapNoteScore_W1'] = 10, + ['TapNoteScore_W2'] = 9, + ['TapNoteScore_W3'] = 5 + }; + setmetatable(multLookup, ZeroIfNotFound); + local radarValues = GetDirectRadar(params.Player); + local totalItems = GetTotalItems(radarValues) + - radarValues:GetValue('RadarCategory_Lifts'); + -- 1+2+3+...+totalItems の値 + local sTotal = (totalItems+1)*totalItems/2; + -- [en] Score for one song + -- [ja] 1つあたりのスコア + local sOne = math.floor(100000000/sTotal); + -- [ja] 端数は最後の1ステップで加算するのでその値を取得 + local sLast = 100000000-(sOne*sTotal); + -- [ja] 現在何個目の譜面か + pss:SetCurMaxScore(pss:GetCurMaxScore()+1); + -- [en] current score + -- [ja] 今回のスコア + local vScore = sOne*(pss:GetCurMaxScore()); + if (params.HoldNoteScore == 'HoldNoteScore_Held') then + vScore = vScore; + else + if (params.HoldNoteScore == 'HoldNoteScore_LetGo') then + vScore = 0; + else + vScore = vScore*multLookup[params.TapNoteScore]/10; + end; + end; + if ((vScore > 0) and (pss:GetCurMaxScore() == totalItems)) then + vScore = vScore+sLast; + end; + pss:SetScore(pss:GetScore()+vScore); +end; + ----------------------------------------------------------- --DDR SuperNOVA(-esque) scoring ----------------------------------------------------------- @@ -148,24 +190,47 @@ end; r['DDR SuperNOVA 2'] = function(params, pss) local multLookup = { - ['TapNoteScore_W1'] = 1, - ['TapNoteScore_W2'] = 1, - ['TapNoteScore_W3'] = 0.5 + ['TapNoteScore_W1'] = 10, + ['TapNoteScore_W2'] = 10, + ['TapNoteScore_W3'] = 5 }; setmetatable(multLookup, ZeroIfNotFound); local radarValues = GetDirectRadar(params.Player); - local totalItems = GetTotalItems(radarValues); - local base = 100000 / totalItems; - local hold = base * (params.HoldNoteScore == 'HoldNoteScore_Held' and 1 or 0); - local maxScore = (base * multLookup['TapNoteScore_W1']) + hold; - pss:SetCurMaxScore(pss:GetCurMaxScore() + (math.round(maxScore) * 10)); - local preW1 = base * multLookup[params.TapNoteScore]; - local buildScore = (preW1 - (IsW1Allowed(params.TapNoteScore) and 10 or 0)) + hold; - pss:SetScore(pss:GetScore() + (math.round(buildScore) * 10)); + local numLifts = radarValues:GetCategory('RadarCategory_Lifts') + local numFakes = radarValues:GetCategory('RadarCategory_Fakes') + local totalItems = GetTotalItems(radarValues) - (numLifts + numFakes); + + -- handle holds + local maxAdd = 0 + if params.HoldNoteScore == 'HoldNoteScore_Held' then + maxAdd = 10 + else + if params.HoldNoteScore == 'HoldNoteScore_LetGo' then + maxAdd = 0 + else + maxAdd = multLookup[params.TapNoteScore] + if params.TapNoteScore == 'TapNoteScore_W2' or 'TapNoteScore_W3' then + -- [ja] 超最終手段 + pss:SetCurMaxScore( pss:GetCurMaxScore() + 1000000 ) + end + end + end + pss:SetCurMaxScore(pss:GetCurMaxScore() + maxAdd); + + --[[ + [ja] パフェ数取得 この方法で取得するとロングノートの場合2つカウントされる そのため使えない + pss:GetTapNoteScores('TapNoteScore_W2') + 仕方がないのでパフェ数を 1000000 単位で GetCurMaxScore に記録 + その後、情報を分解して取り出す + --]] + + local vScore = pss:GetCurMaxScore() % 1000000 + local vSub = math.floor( pss:GetCurMaxScore()/1000000 ) + pss:SetScore( math.floor(10000*vScore/totalItems) * 10 - (vSub*10) ); end; ----------------------------------------------------------- ---Radar Master (doesn't work in 1.2.1, disabled) ---don't try to "fix it up", either. you *cannot* make it work in 1.2.1. +--Radar Master (doesn't work in sm-ssc 1.2.1, disabled) +--todo: get this working with StepMania 5 ----------------------------------------------------------- r['[SSC] Radar Master'] = function(params, pss) local masterTable = { diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 18712e9a16..e504462b0e 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -3752,6 +3752,9 @@ TitleX=SCREEN_CENTER_X-80 [ScreenMiniMenuAreaMenu] Fallback="ScreenMiniMenu" +[ScreenMiniMenuAlterMenu] +Fallback="ScreenMiniMenu" + [ScreenMiniMenuStepsInformation] Fallback="ScreenMiniMenu" diff --git a/Themes/default/BGAnimations/ScreenEvaluation background/default.lua b/Themes/default/BGAnimations/ScreenEvaluation background/default.lua index ac0c710158..5cd05e9772 100644 --- a/Themes/default/BGAnimations/ScreenEvaluation background/default.lua +++ b/Themes/default/BGAnimations/ScreenEvaluation background/default.lua @@ -5,7 +5,10 @@ t[#t+1] = Def.ActorFrame { Def.Sprite { OnCommand=function(self) if GAMESTATE:GetCurrentSong() then - self:LoadBackground( GAMESTATE:GetCurrentSong():GetBackgroundPath() ); + local song = GAMESTATE:GetCurrentSong(); + if song:HasBackground() then + self:LoadBackground(song:GetBackgroundPath()); + end; self:scaletoclipped( SCREEN_WIDTH+1,SCREEN_HEIGHT ); (cmd(fadebottom,0.25;fadetop,0.25;croptop,48/480;cropbottom,48/480))(self); else diff --git a/Themes/default/BGAnimations/ScreenPlayerOptions background.lua b/Themes/default/BGAnimations/ScreenPlayerOptions background.lua index a02c0291d9..e7cafc1283 100644 --- a/Themes/default/BGAnimations/ScreenPlayerOptions background.lua +++ b/Themes/default/BGAnimations/ScreenPlayerOptions background.lua @@ -6,7 +6,10 @@ t[#t+1] = Def.ActorFrame { Condition=not GAMESTATE:IsCourseMode(); OnCommand=function(self) if GAMESTATE:GetCurrentSong() then - self:LoadBackground( GAMESTATE:GetCurrentSong():GetBackgroundPath() ); + local song = GAMESTATE:GetCurrentSong(); + if song:HasBackground() then + self:LoadBackground(song:GetBackgroundPath()); + end; self:scaletoclipped( SCREEN_WIDTH+1,SCREEN_HEIGHT ); (cmd(fadebottom,0.25;fadetop,0.25;croptop,48/480;cropbottom,48/480))(self); else diff --git a/src/Background.cpp b/src/Background.cpp index 2761fd3b18..19e21abf7a 100644 --- a/src/Background.cpp +++ b/src/Background.cpp @@ -27,7 +27,6 @@ static ThemeMetric LEFT_EDGE ("Background","LeftEdge"); static ThemeMetric TOP_EDGE ("Background","TopEdge"); static ThemeMetric RIGHT_EDGE ("Background","RightEdge"); static ThemeMetric BOTTOM_EDGE ("Background","BottomEdge"); -#define RECT_BACKGROUND RectF (LEFT_EDGE,TOP_EDGE,RIGHT_EDGE,BOTTOM_EDGE) static ThemeMetric CLAMP_OUTPUT_PERCENT ("Background","ClampOutputPercent"); static ThemeMetric SHOW_DANCING_CHARACTERS ("Background","ShowDancingCharacters"); static ThemeMetric USE_STATIC_BG ("Background","UseStaticBackground"); diff --git a/src/GameSoundManager.cpp b/src/GameSoundManager.cpp index a9e71fce24..27933d5d82 100644 --- a/src/GameSoundManager.cpp +++ b/src/GameSoundManager.cpp @@ -142,8 +142,10 @@ static void StartMusic( MusicToPlay &ToPlay ) { LOG->Trace( "Found '%s'", ToPlay.m_sTimingFile.c_str() ); Song song; - if( GetExtension(ToPlay.m_sTimingFile) == ".ssc" && - SSCLoader::LoadFromSSCFile(ToPlay.m_sTimingFile, song) ) + SSCLoader loaderSSC; + SMLoader loaderSM; + if(GetExtension(ToPlay.m_sTimingFile) == ".ssc" && + loaderSSC.LoadFromSimfile(ToPlay.m_sTimingFile, song) ) { ToPlay.HasTiming = true; ToPlay.m_TimingData = song.m_SongTiming; @@ -152,8 +154,8 @@ static void StartMusic( MusicToPlay &ToPlay ) if( pStepsCabinetLights ) pStepsCabinetLights->GetNoteData( ToPlay.m_LightsData ); } - else if( GetExtension(ToPlay.m_sTimingFile) == ".sm" && - SMLoader::LoadFromSMFile(ToPlay.m_sTimingFile, song) ) + else if(GetExtension(ToPlay.m_sTimingFile) == ".sm" && + loaderSM.LoadFromSimfile(ToPlay.m_sTimingFile, song) ) { ToPlay.HasTiming = true; ToPlay.m_TimingData = song.m_SongTiming; diff --git a/src/GameState.cpp b/src/GameState.cpp index a720dcbd89..825d051fda 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -106,6 +106,7 @@ static Preference g_Premium( "Premium", Premium_Off ); Preference GameState::m_bAutoJoin( "AutoJoin", false ); GameState::GameState() : + processedTiming( NULL ), m_pCurGame( Message_CurrentGameChanged ), m_pCurStyle( Message_CurrentStyleChanged ), m_PlayMode( Message_PlayModeChanged ), @@ -200,6 +201,16 @@ void GameState::SetMasterPlayerNumber(const PlayerNumber p) this->masterPlayerNumber = p; } +TimingData * GameState::GetProcessedTimingData() const +{ + return this->processedTiming; +} + +void GameState::SetProcessedTimingData(TimingData * t) +{ + this->processedTiming = t; +} + void GameState::ApplyGameCommand( const RString &sCommand, PlayerNumber pn ) { GameCommand m; diff --git a/src/GameState.h b/src/GameState.h index 79618aa711..0b2463f3c7 100644 --- a/src/GameState.h +++ b/src/GameState.h @@ -37,6 +37,8 @@ class GameState { /** @brief The player number used with Styles where one player controls both sides. */ PlayerNumber masterPlayerNumber; + /** @brief The TimingData that is used for processing certain functions. */ + TimingData * processedTiming; public: /** @brief Set up the GameState with initial values. */ GameState(); @@ -149,6 +151,16 @@ public: * @brief Set the master player number. * @param p the master player number. */ void SetMasterPlayerNumber(const PlayerNumber p); + + /** + * @brief Retrieve the present timing data being processed. + * @return the timing data pointer. */ + TimingData * GetProcessedTimingData() const; + + /** + * @brief Set the timing data to be used with processing. + * @param t the timing data. */ + void SetProcessedTimingData(TimingData * t); bool IsCourseMode() const; bool IsBattleMode() const; // not Rave diff --git a/src/GameplayAssist.cpp b/src/GameplayAssist.cpp index 4ecd6be014..e42920f15c 100644 --- a/src/GameplayAssist.cpp +++ b/src/GameplayAssist.cpp @@ -47,7 +47,7 @@ void GameplayAssist::PlayTicks( const NoteData &nd, const PlayerState *ps ) if( nd.IsThereATapOrHoldHeadAtRow( r ) ) iClapRow = r; - if( iClapRow != -1 && !timing.IsWarpAtRow( iClapRow ) && !timing.IsFakeAtRow( iClapRow ) ) + if( iClapRow != -1 && timing.IsJudgableAtRow(iClapRow)) { const float fTickBeat = NoteRowToBeat( iClapRow ); const float fTickSecond = timing.GetElapsedTimeFromBeatNoOffset( fTickBeat ); diff --git a/src/NoteData.cpp b/src/NoteData.cpp index eac5bc42b4..50265d5b65 100644 --- a/src/NoteData.cpp +++ b/src/NoteData.cpp @@ -9,6 +9,7 @@ #include "RageUtil.h" #include "RageLog.h" #include "XmlFile.h" +#include "GameState.h" // blame radar calculations. #include "Foreach.h" #include "RageUtil_AutoPtr.h" @@ -467,7 +468,8 @@ int NoteData::GetNumTapNotes( int iStartIndex, int iEndIndex ) const { const TapNote &tn = GetTapNote(t, r); if( tn.type != TapNote::empty && tn.type != TapNote::mine - && tn.type != TapNote::lift && tn.type != TapNote::fake ) + && tn.type != TapNote::lift && tn.type != TapNote::fake + && GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r) ) iNumNotes++; } } @@ -485,7 +487,7 @@ int NoteData::GetNumRowsWithTap( int iStartIndex, int iEndIndex ) const { int iNumNotes = 0; FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, r, iStartIndex, iEndIndex ) - if( IsThereATapAtRow(r) ) + if( IsThereATapAtRow(r) && GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r) ) iNumNotes++; return iNumNotes; @@ -498,7 +500,8 @@ int NoteData::GetNumMines( int iStartIndex, int iEndIndex ) const for( int t=0; tGetProcessedTimingData()->IsJudgableAtRow(r)) iNumMines++; } @@ -509,7 +512,7 @@ int NoteData::GetNumRowsWithTapOrHoldHead( int iStartIndex, int iEndIndex ) cons { int iNumNotes = 0; FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, r, iStartIndex, iEndIndex ) - if( IsThereATapOrHoldHeadAtRow(r) ) + if( IsThereATapOrHoldHeadAtRow(r) && GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r) ) iNumNotes++; return iNumNotes; @@ -560,7 +563,8 @@ int NoteData::GetNumRowsWithSimultaneousPresses( int iMinSimultaneousPresses, in { if( !RowNeedsAtLeastSimultaneousPresses(iMinSimultaneousPresses,r) ) continue; - + if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r)) + continue; iNum++; } @@ -576,7 +580,10 @@ int NoteData::GetNumRowsWithSimultaneousTaps( int iMinTaps, int iStartIndex, int for( int t=0; tGetProcessedTimingData()->IsJudgableAtRow(r)) iNumNotesThisIndex++; } if( iNumNotesThisIndex >= iMinTaps ) @@ -598,6 +605,8 @@ int NoteData::GetNumHoldNotes( int iStartIndex, int iEndIndex ) const if( lBegin->second.type != TapNote::hold_head || lBegin->second.subType != TapNote::hold_head_hold ) continue; + if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(lBegin->first)) + continue; iNumHolds++; } } @@ -616,6 +625,8 @@ int NoteData::GetNumRolls( int iStartIndex, int iEndIndex ) const if( lBegin->second.type != TapNote::hold_head || lBegin->second.subType != TapNote::hold_head_roll ) continue; + if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(lBegin->first)) + continue; iNumRolls++; } } @@ -629,7 +640,8 @@ int NoteData::GetNumLifts( int iStartIndex, int iEndIndex ) const for( int t=0; tGetProcessedTimingData()->IsJudgableAtRow(r)) iNumLifts++; } @@ -643,7 +655,8 @@ int NoteData::GetNumFakes( int iStartIndex, int iEndIndex ) const for( int t=0; tGetProcessedTimingData()->IsJudgableAtRow(r)) iNumFakes++; } diff --git a/src/NoteDataUtil.cpp b/src/NoteDataUtil.cpp index ccb9faf2b4..7ecbbe46ac 100644 --- a/src/NoteDataUtil.cpp +++ b/src/NoteDataUtil.cpp @@ -799,6 +799,11 @@ RadarStats CalculateRadarStatsFast( const NoteData &in, RadarStats &out ) { FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( in, t, r, 0, MAX_NOTE_ROW ) { + /* This function deals strictly with taps, jumps, hands, and quads. + * As such, all rows in here have to be judgable. */ + if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r)) + continue; + const TapNote &tn = in.GetTapNote(t, r); switch( tn.type ) { diff --git a/src/NoteTypes.h b/src/NoteTypes.h index 3d99c63252..cd00810e0a 100644 --- a/src/NoteTypes.h +++ b/src/NoteTypes.h @@ -164,7 +164,7 @@ struct TapNote { if (type_ > TapNote::fake ) { - LOG->Trace(ssprintf("Invalid tap note type %d (most likely) due to random vanish issues. Assume it doesn't need judging.", type_ ) ); + LOG->Trace("Invalid tap note type %d (most likely) due to random vanish issues. Assume it doesn't need judging.", (int)type_ ); type = TapNote::empty; } } diff --git a/src/NotesLoader.cpp b/src/NotesLoader.cpp index 88f68787aa..983f35cedb 100644 --- a/src/NotesLoader.cpp +++ b/src/NotesLoader.cpp @@ -32,20 +32,23 @@ bool NotesLoader::LoadFromDir( const RString &sPath, Song &out, set &Bl vector list; BlacklistedImages.clear(); - SSCLoader::GetApplicableFiles( sPath, list ); + SSCLoader loaderSSC; + loaderSSC.GetApplicableFiles( sPath, list ); if( !list.empty() ) { - if( !SSCLoader::LoadFromDir( sPath, out ) ) + if( !loaderSSC.LoadFromDir( sPath, out ) ) return false; - SSCLoader::TidyUpData( out, false ); + loaderSSC.TidyUpData( out, false ); return true; } - SMALoader::GetApplicableFiles( sPath, list ); + SMALoader loaderSMA; + loaderSMA.GetApplicableFiles( sPath, list ); if (!list.empty() ) - return SMALoader::LoadFromDir( sPath, out ); - SMLoader::GetApplicableFiles( sPath, list ); + return loaderSMA.LoadFromDir( sPath, out ); + SMLoader loaderSM; + loaderSM.GetApplicableFiles( sPath, list ); if (!list.empty() ) - return SMLoader::LoadFromDir( sPath, out ); + return loaderSM.LoadFromDir( sPath, out ); DWILoader::GetApplicableFiles( sPath, list ); if( !list.empty() ) return DWILoader::LoadFromDir( sPath, out, BlacklistedImages ); diff --git a/src/NotesLoaderSM.cpp b/src/NotesLoaderSM.cpp index f18c556056..35562c1efd 100644 --- a/src/NotesLoaderSM.cpp +++ b/src/NotesLoaderSM.cpp @@ -13,10 +13,39 @@ #include "Attack.h" #include "PrefsManager.h" -/** @brief The maximum file size for edits. */ -const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB +bool SMLoader::LoadFromDir( const RString &sPath, Song &out ) +{ + vector aFileNames; + GetApplicableFiles( sPath, aFileNames ); + + if( aFileNames.size() > 1 ) + { + // Need to break this up first. + RString tmp = "Song " + sPath + " has more than one"; + LOG->UserLog(tmp, this->GetFileExtension(), "file. There can only be one!"); + return false; + } + + ASSERT( aFileNames.size() == 1 ); + return LoadFromSimfile( sPath + aFileNames[0], out ); +} -void SMLoader::LoadFromSMTokens( +float SMLoader::RowToBeat( RString line, const int rowsPerBeat ) +{ + RString backup = line; + Trim(line, "r"); + Trim(line, "R"); + if( backup != line ) + { + return StringToFloat( line ) / rowsPerBeat; + } + else + { + return StringToFloat( line ); + } +} + +void SMLoader::LoadFromTokens( RString sStepsType, RString sDescription, RString sDifficulty, @@ -34,7 +63,7 @@ void SMLoader::LoadFromSMTokens( Trim( sDifficulty ); Trim( sNoteData ); - // LOG->Trace( "Steps::LoadFromSMTokens()" ); + // LOG->Trace( "Steps::LoadFromTokens()" ); // insert stepstype hacks from GameManager.cpp here? -aj out.m_StepsType = GAMEMAN->StringToStepsType( sStepsType ); @@ -79,11 +108,6 @@ void SMLoader::LoadFromSMTokens( out.TidyUpData(); } -void SMLoader::GetApplicableFiles( const RString &sPath, vector &out ) -{ - GetDirListing( sPath + RString("*.sm"), out ); -} - void SMLoader::ProcessBGChanges( Song &out, const RString &sValueName, const RString &sPath, const RString &sParam ) { BackgroundLayer iLayer = BACKGROUND_LAYER_1; @@ -171,10 +195,10 @@ void SMLoader::ProcessInstrumentTracks( Song &out, const RString &sParam ) } } -bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam ) +bool SMLoader::ProcessBPMs( TimingData &out, const RString line, const int rowsPerBeat ) { vector arrayBPMChangeExpressions; - split( sParam, ",", arrayBPMChangeExpressions ); + split( line, ",", arrayBPMChangeExpressions ); // prepare storage variables for negative BPMs -> Warps. float negBeat = -1; @@ -196,7 +220,7 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam ) bNotEmpty = true; - const float fBeat = StringToFloat( arrayBPMChangeValues[0] ); + const float fBeat = RowToBeat( arrayBPMChangeValues[0], rowsPerBeat ); const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] ); if( fNewBPM < 0.0f ) @@ -242,10 +266,10 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam ) return bNotEmpty; } -void SMLoader::ProcessStops( TimingData &out, const RString sParam ) +void SMLoader::ProcessStops( TimingData &out, const RString line, const int rowsPerBeat ) { vector arrayFreezeExpressions; - split( sParam, ",", arrayFreezeExpressions ); + split( line, ",", arrayFreezeExpressions ); // Prepare variables for negative stop conversion. float negBeat = -1; @@ -263,7 +287,7 @@ void SMLoader::ProcessStops( TimingData &out, const RString sParam ) continue; } - const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] ); + const float fFreezeBeat = RowToBeat( arrayFreezeValues[0], rowsPerBeat ); const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] ); // Process the prior stop. @@ -308,10 +332,10 @@ void SMLoader::ProcessStops( TimingData &out, const RString sParam ) } } -void SMLoader::ProcessDelays( TimingData &out, const RString sParam ) +void SMLoader::ProcessDelays( TimingData &out, const RString line, const int rowsPerBeat ) { vector arrayDelayExpressions; - split( sParam, ",", arrayDelayExpressions ); + split( line, ",", arrayDelayExpressions ); for( unsigned f=0; f vs1; - split( sParam, ",", vs1 ); + split( line, ",", vs1 ); FOREACH_CONST( RString, vs1, s1 ) { @@ -358,9 +382,9 @@ void SMLoader::ProcessTimeSignatures( TimingData &out, const RString sParam ) continue; } - const float fBeat = StringToFloat( vs2[0] ); + const float fBeat = RowToBeat( vs2[0], rowsPerBeat ); - TimeSignatureSegment seg( BeatToNoteRow( fBeat ), StringToInt( vs2[1] ), StringToInt( vs2[2] )); + TimeSignatureSegment seg( fBeat, StringToInt( vs2[1] ), StringToInt( vs2[2] )); if( fBeat < 0 ) { @@ -384,10 +408,10 @@ void SMLoader::ProcessTimeSignatures( TimingData &out, const RString sParam ) } } -void SMLoader::ProcessTickcounts( TimingData &out, const RString sParam ) +void SMLoader::ProcessTickcounts( TimingData &out, const RString line, const int rowsPerBeat ) { vector arrayTickcountExpressions; - split( sParam, ",", arrayTickcountExpressions ); + split( line, ",", arrayTickcountExpressions ); for( unsigned f=0; f vs1; + split( line, ",", vs1 ); + + FOREACH_CONST( RString, vs1, s1 ) + { + vector vs2; + split( *s1, "=", vs2 ); + + if( vs2[0] == 0 && vs2.size() == 2 ) // First one always seems to have 2. + { + vs2.push_back("0"); + } + + if( vs2.size() == 3 ) // use beats by default. + { + vs2.push_back("0"); + } + + if( vs2.size() < 4 ) + { + LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with %i values.", (int)vs2.size() ); + continue; + } + + const float fBeat = RowToBeat( vs2[0], rowsPerBeat ); + + SpeedSegment seg( fBeat, StringToFloat( vs2[1] ), StringToFloat( vs2[2] )); + seg.SetUnit(StringToInt(vs2[3])); + + if( fBeat < 0 ) + { + LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f.", fBeat ); + continue; + } + + if( seg.GetLength() < 0 ) + { + LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f, length %f.", fBeat, seg.GetLength() ); + continue; + } + + out.AddSpeedSegment( seg ); + } +} + +void SMLoader::ProcessFakes( TimingData &out, const RString line, const int rowsPerBeat ) +{ + vector arrayFakeExpressions; + split( line, ",", arrayFakeExpressions ); + + for( unsigned b=0; b arrayFakeValues; + split( arrayFakeExpressions[b], "=", arrayFakeValues ); + // XXX: Hard to tell which file caused this. + if( arrayFakeValues.size() != 2 ) + { + LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #FAKES value \"%s\" (must have exactly one '='), ignored.", + arrayFakeExpressions[b].c_str() ); + continue; + } + + const float fBeat = RowToBeat( arrayFakeValues[0], rowsPerBeat ); + const float fNewBeat = StringToFloat( arrayFakeValues[1] ); + + if(fNewBeat > 0) + out.AddFakeSegment( FakeSegment(fBeat, fNewBeat) ); + else + { + LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Fake at beat %f, BPM %f.", fBeat, fNewBeat ); + } + } +} + bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChangeExpression ) { vector aBGChangeValues; @@ -495,7 +595,7 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString return aBGChangeValues.size() >= 2; } -bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache ) +bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache ) { LOG->Trace( "Song::LoadFromSMFile(%s)", sPath.c_str() ); @@ -719,7 +819,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache } Steps* pNewNotes = out.CreateSteps(); - LoadFromSMTokens( + LoadFromTokens( sParams[1], sParams[2], sParams[3], @@ -743,33 +843,6 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache return true; } -bool SMLoader::LoadFromDir( const RString &sPath, Song &out ) -{ - vector aFileNames; - GetApplicableFiles( sPath, aFileNames ); - - if( aFileNames.size() > 1 ) - { - LOG->UserLog( "Song", sPath, "has more than one SM file. There can be only one (unless you are using TougaKiryuu's AnimeMix files somehow, which assume a different version of StepMania)!" ); - return false; - /* - for( unsigned i=0; i= 1 ); - - return LoadFromSMFile( sPath + aFileNames[0], out ); -} - bool SMLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ) { LOG->Trace( "SMLoader::LoadEditFromFile(%s)", sEditFilePath.c_str() ); @@ -853,7 +926,7 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath return true; Steps* pNewNotes = pSong->CreateSteps(); - LoadFromSMTokens( + LoadFromTokens( sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], *pNewNotes); @@ -881,6 +954,11 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath } +void SMLoader::GetApplicableFiles( const RString &sPath, vector &out ) +{ + GetDirListing( sPath + RString("*" + this->GetFileExtension() ), out ); +} + void SMLoader::TidyUpData( Song &song, bool bFromCache ) { /* diff --git a/src/NotesLoaderSM.h b/src/NotesLoaderSM.h index 94dfdd2dfe..0e03988b58 100644 --- a/src/NotesLoaderSM.h +++ b/src/NotesLoaderSM.h @@ -15,33 +15,157 @@ class TimingData; * This was brought in from StepMania 4's recent betas. */ const float FAST_BPM_WARP = 9999999.f; -/** @brief Reads a Song from an .SM file. */ -namespace SMLoader -{ - void LoadFromSMTokens( RString sStepsType, RString sDescription, RString sDifficulty, - RString sMeter, RString sRadarValues, RString sNoteData, Steps &out ); - - bool LoadFromDir( const RString &sPath, Song &out ); - void TidyUpData( Song &song, bool bFromCache ); +/** @brief The maximum file size for edits. */ +const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB - bool LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache = false ); - void GetApplicableFiles( const RString &sPath, vector &out ); - bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ); - bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot ); - bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ); - bool LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChangeExpression ); +/** @brief Reads a Song from an .SM file. */ +struct SMLoader +{ + SMLoader() : fileExt(".sm") {} + SMLoader(RString ext) : fileExt(ext) {} - bool ProcessBPMs( TimingData &, const RString ); - void ProcessStops( TimingData &, const RString ); - void ProcessDelays( TimingData &, const RString ); - void ProcessTimeSignatures( TimingData &, const RString ); - void ProcessTickcounts( TimingData &, const RString ); - void ProcessBGChanges( Song &out, const RString &sValueName, + virtual ~SMLoader() {} + + /** + * @brief Attempt to load a song from a specified path. + * @param sPath a const reference to the path on the hard drive to check. + * @param out a reference to the Song that will retrieve the song information. + * @return its success or failure. + */ + virtual bool LoadFromDir( const RString &sPath, Song &out ); + /** + * @brief Perform some cleanup on the loaded song. + * @param song a reference to the song that may need cleaning up. + * @param bFromCache a flag to determine if this song is loaded from a cache file. + */ + virtual void TidyUpData( Song &song, bool bFromCache ); + + /** + * @brief Attempt to load the specified sm file. + * @param sPath a const reference to the path on the hard drive to check. + * @param out a reference to the Song that will retrieve the song information. + * @param bFromCache a check to see if we are getting certain information from the cache file. + * @return its success or failure. + */ + virtual bool LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache = false ); + /** + * @brief Retrieve the list of .sm files. + * @param sPath a const reference to the path on the hard drive to check. + * @param out a vector of files found in the path. + */ + virtual void GetApplicableFiles( const RString &sPath, vector &out ); + virtual bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ); + virtual bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot ); + virtual bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ); + virtual bool LoadFromBGChangesString(BackgroundChange &change, + const RString &sBGChangeExpression ); + + /** + * @brief Process the BPM Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. + * @return true if there was at least one segment found, false otherwise. */ + bool ProcessBPMs(TimingData & out, + const RString line, + const int rowsPerBeat = -1); + /** + * @brief Process the Stop Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. */ + void ProcessStops(TimingData & out, + const RString line, + const int rowsPerBeat = -1); + /** + * @brief Process the Delay Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. */ + void ProcessDelays(TimingData & out, + const RString line, + const int rowsPerBeat = -1); + /** + * @brief Process the Time Signature Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. */ + void ProcessTimeSignatures(TimingData & out, + const RString line, + const int rowsPerBeat = -1); + /** + * @brief Process the Tickcount Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. */ + void ProcessTickcounts(TimingData & out, + const RString line, + const int rowsPerBeat = -1); + + /** + * @brief Process the Speed Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. */ + virtual void ProcessSpeeds(TimingData & out, + const RString line, + const int rowsPerBeat = -1); + + virtual void ProcessCombos(TimingData & out, + const RString line, + const int rowsPerBeat = -1) {} + + /** + * @brief Process the Fake Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. */ + virtual void ProcessFakes(TimingData & out, + const RString line, + const int rowsPerBeat = -1); + + virtual void ProcessBGChanges( Song &out, const RString &sValueName, const RString &sPath, const RString &sParam ); void ProcessAttacks( Song &out, MsdFile::value_t sParams ); void ProcessInstrumentTracks( Song &out, const RString &sParam ); -} + + /** + * @brief Convert a row value to the proper beat value. + * + * This is primarily used for assistance with converting SMA files. + * @param line The line that contains the value. + * @param rowsPerBeat the number of rows per beat according to the original file. + * @return the converted beat value. */ + float RowToBeat(RString line, const int rowsPerBeat); + +protected: + /** + * @brief Process the different tokens we have available to get NoteData. + * @param stepsType The current StepsType. + * @param description The description of the chart. + * @param difficulty The difficulty (in words) of the chart. + * @param meter the difficulty (in numbers) of the chart. + * @param radarValues the calculated radar values. + * @param noteData the note data itself. + * @param out the Steps getting the data. */ + virtual void LoadFromTokens(RString sStepsType, + RString sDescription, + RString sDifficulty, + RString sMeter, + RString sRadarValues, + RString sNoteData, + Steps &out); + + /** + * @brief Retrieve the file extension associated with this loader. + * @return the file extension. */ + RString GetFileExtension() const { return fileExt; } + +private: + /** @brief The file extension in use. */ + const RString fileExt; +}; #endif diff --git a/src/NotesLoaderSMA.cpp b/src/NotesLoaderSMA.cpp index c902b1f103..44f6f5026f 100644 --- a/src/NotesLoaderSMA.cpp +++ b/src/NotesLoaderSMA.cpp @@ -14,240 +14,6 @@ #include "Steps.h" #include "Attack.h" -/** - * @brief A custom .edit file can only be so big before we have to reject it. - */ -const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60 KB - -bool SMALoader::LoadFromBGChangesString( BackgroundChange &change, - const RString &sBGChangeExpression ) -{ - return SMLoader::LoadFromBGChangesString(change, sBGChangeExpression); -} - -bool SMALoader::LoadFromDir( const RString &sPath, Song &out ) -{ - vector aFileNames; - GetApplicableFiles( sPath, aFileNames ); - - if( aFileNames.size() > 1 ) - { - LOG->UserLog( "Song", sPath, "has more than one SMA file. Only one SMA file is allowed per song." ); - return false; - } - ASSERT( aFileNames.size() == 1 ); - return LoadFromSMAFile( sPath + aFileNames[0], out ); -} - -float SMALoader::RowToBeat( RString sLine, const int iRowsPerBeat ) -{ - if( sLine.find("R") || sLine.find("r") ) - { - sLine = sLine.Left(sLine.size()-1); - return StringToFloat( sLine ) / iRowsPerBeat; - } - else - { - return StringToFloat( sLine ); - } -} - -bool SMALoader::ProcessBPMs( TimingData &out, const int iRowsPerBeat, const RString sParam ) -{ - vector arrayBPMChangeExpressions; - split( sParam, ",", arrayBPMChangeExpressions ); - - // prepare storage variables for negative BPMs -> Warps. - float negBeat = -1; - float negBPM = 1; - float highspeedBeat = -1; - bool bNotEmpty = false; - - for( unsigned b=0; b arrayBPMChangeValues; - split( arrayBPMChangeExpressions[b], "=", arrayBPMChangeValues ); - // XXX: Hard to tell which file caused this. - if( arrayBPMChangeValues.size() != 2 ) - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #BPMs value \"%s\" (must have exactly one '='), ignored.", - arrayBPMChangeExpressions[b].c_str() ); - continue; - } - - bNotEmpty = true; - - const float fBeat = RowToBeat( arrayBPMChangeValues[0], iRowsPerBeat ); - const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] ); - - if( fNewBPM < 0.0f ) - { - out.m_bHasNegativeBpms = true; - negBeat = fBeat; - negBPM = fNewBPM; - } - else if( fNewBPM > 0.0f ) - { - // add in a warp. - if( negBPM < 0 ) - { - float endBeat = fBeat + (fNewBPM / -negBPM) * (fBeat - negBeat); - WarpSegment new_seg(negBeat, endBeat - negBeat); - out.AddWarpSegment( new_seg ); - - negBeat = -1; - negBPM = 1; - } - // too fast. make it a warp. - if( fNewBPM > FAST_BPM_WARP ) - { - highspeedBeat = fBeat; - } - else - { - // add in a warp. - if( highspeedBeat > 0 ) - { - WarpSegment new_seg(highspeedBeat, fBeat - highspeedBeat); - out.AddWarpSegment( new_seg ); - highspeedBeat = -1; - } - { - BPMSegment new_seg( BeatToNoteRow( fBeat ), fNewBPM ); - out.AddBPMSegment( new_seg ); - } - } - } - } - - return bNotEmpty; -} - -void SMALoader::ProcessStops( TimingData &out, const int iRowsPerBeat, const RString sParam ) -{ - vector arrayFreezeExpressions; - split( sParam, ",", arrayFreezeExpressions ); - - // Prepare variables for negative stop conversion. - float negBeat = -1; - float negPause = 0; - - for( unsigned f=0; f arrayFreezeValues; - split( arrayFreezeExpressions[f], "=", arrayFreezeValues ); - if( arrayFreezeValues.size() != 2 ) - { - // XXX: Hard to tell which file caused this. - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #STOPS value \"%s\" (must have exactly one '='), ignored.", - arrayFreezeExpressions[f].c_str() ); - continue; - } - - const float fFreezeBeat = RowToBeat( arrayFreezeValues[0], iRowsPerBeat ); - const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] ); - - // Process the prior stop. - if( negPause > 0 ) - { - BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat)); - float fSecondsPerBeat = 60 / oldBPM.GetBPM(); - float fSkipBeats = negPause / fSecondsPerBeat; - - if( negBeat + fSkipBeats > fFreezeBeat ) - fSkipBeats = fFreezeBeat - negBeat; - - WarpSegment ws( negBeat, fSkipBeats); - out.AddWarpSegment( ws ); - - negBeat = -1; - negPause = 0; - } - - if( fFreezeSeconds < 0.0f ) - { - negBeat = fFreezeBeat; - negPause = -fFreezeSeconds; - } - else if( fFreezeSeconds > 0.0f ) - { - StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds ); - out.AddStopSegment( ss ); - } - - } - - // Process the prior stop if there was one. - if( negPause > 0 ) - { - BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat)); - float fSecondsPerBeat = 60 / oldBPM.GetBPM(); - float fSkipBeats = negPause / fSecondsPerBeat; - - WarpSegment ws( negBeat, fSkipBeats); - out.AddWarpSegment( ws ); - } -} - -void SMALoader::ProcessDelays( TimingData &out, const int iRowsPerBeat, const RString sParam ) -{ - vector arrayDelayExpressions; - split( sParam, ",", arrayDelayExpressions ); - - for( unsigned f=0; f arrayDelayValues; - split( arrayDelayExpressions[f], "=", arrayDelayValues ); - if( arrayDelayValues.size() != 2 ) - { - // XXX: Hard to tell which file caused this. - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #DELAYS value \"%s\" (must have exactly one '='), ignored.", - arrayDelayExpressions[f].c_str() ); - continue; - } - - const float fFreezeBeat = RowToBeat( arrayDelayValues[0], iRowsPerBeat ); - const float fFreezeSeconds = StringToFloat( arrayDelayValues[1] ); - - StopSegment new_seg( fFreezeBeat, fFreezeSeconds, true ); - // XXX: Remove Negatives Bug? - new_seg.SetBeat(fFreezeBeat); - new_seg.SetPause(fFreezeSeconds); - - // LOG->Trace( "Adding a delay segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds ); - - if(fFreezeSeconds > 0.0f) - out.AddStopSegment( new_seg ); - else - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid delay at beat %f, length %f.", fFreezeBeat, fFreezeSeconds ); - } -} - -void SMALoader::ProcessTickcounts( TimingData &out, const int iRowsPerBeat, const RString sParam ) -{ - vector arrayTickcountExpressions; - split( sParam, ",", arrayTickcountExpressions ); - - for( unsigned f=0; f arrayTickcountValues; - split( arrayTickcountExpressions[f], "=", arrayTickcountValues ); - if( arrayTickcountValues.size() != 2 ) - { - // XXX: Hard to tell which file caused this. - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #TICKCOUNTS value \"%s\" (must have exactly one '='), ignored.", - arrayTickcountExpressions[f].c_str() ); - continue; - } - - const float fTickcountBeat = RowToBeat( arrayTickcountValues[0], iRowsPerBeat ); - int iTicks = clamp(atoi( arrayTickcountValues[1] ), 0, ROWS_PER_BEAT); - - TickcountSegment new_seg( BeatToNoteRow(fTickcountBeat), iTicks ); - out.AddTickcountSegment( new_seg ); - } -} - void SMALoader::ProcessMultipliers( TimingData &out, const int iRowsPerBeat, const RString sParam ) { vector arrayMultiplierExpressions; @@ -306,15 +72,10 @@ void SMALoader::ProcessBeatsPerMeasure( TimingData &out, const RString sParam ) } } -float BeatToSeconds(float fromBeat, RString toSomething) -{ - return 0; -} - -void SMALoader::ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RString sParam ) +void SMALoader::ProcessSpeeds( TimingData &out, const RString line, const int rowsPerBeat ) { vector vs1; - split( sParam, ",", vs1 ); + split( line, ",", vs1 ); FOREACH_CONST( RString, vs1, s1 ) { @@ -335,7 +96,7 @@ void SMALoader::ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RS continue; } - const float fBeat = RowToBeat( vs2[0], iRowsPerBeat ); + const float fBeat = RowToBeat( vs2[0], rowsPerBeat ); RString backup = vs2[2]; Trim(vs2[2], "s"); @@ -362,57 +123,7 @@ void SMALoader::ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RS } } -void SMALoader::ProcessFakes( TimingData &out, const int iRowsPerBeat, const RString sParam ) -{ - vector arrayFakeExpressions; - split( sParam, ",", arrayFakeExpressions ); - - for( unsigned b=0; b arrayFakeValues; - split( arrayFakeExpressions[b], "=", arrayFakeValues ); - // XXX: Hard to tell which file caused this. - if( arrayFakeValues.size() != 2 ) - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #FAKES value \"%s\" (must have exactly one '='), ignored.", - arrayFakeExpressions[b].c_str() ); - continue; - } - - const float fBeat = RowToBeat( arrayFakeValues[0], iRowsPerBeat ); - const float fNewBeat = StringToFloat( arrayFakeValues[1] ); - - if(fNewBeat > 0) - out.AddFakeSegment( FakeSegment(fBeat, fNewBeat) ); - else - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Fake at beat %f, BPM %f.", fBeat, fNewBeat ); - } - } -} - - -void SMALoader::LoadFromSMATokens( - RString sStepsType, - RString sDescription, - RString sDifficulty, - RString sMeter, - RString sRadarValues, - RString sNoteData, - Steps &out -) -{ - SMLoader::LoadFromSMTokens( sStepsType, sDescription, - sDifficulty, sMeter, sRadarValues, - sNoteData, out ); -} - -void SMALoader::TidyUpData( Song &song, bool bFromCache ) -{ - SMLoader::TidyUpData( song, bFromCache ); -} - -bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out ) +bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache ) { LOG->Trace( "Song::LoadFromSMAFile(%s)", sPath.c_str() ); @@ -635,28 +346,28 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out ) { TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); - ProcessBPMs( timing, iRowsPerBeat, sParams[1] ); + ProcessBPMs( timing, sParams[1], iRowsPerBeat ); } else if( sValueName=="STOPS" || sValueName=="FREEZES" ) { TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); - ProcessStops( timing, iRowsPerBeat, sParams[1] ); + ProcessStops( timing, sParams[1], iRowsPerBeat ); } else if( sValueName=="DELAYS" ) { TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); - ProcessDelays( timing, iRowsPerBeat, sParams[1] ); + ProcessDelays( timing, sParams[1], iRowsPerBeat ); } else if( sValueName=="TICKCOUNT" ) { TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); - ProcessTickcounts( timing, iRowsPerBeat, sParams[1] ); + ProcessTickcounts( timing, sParams[1], iRowsPerBeat ); } else if( sValueName=="SPEED" ) @@ -665,7 +376,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out ) ? pNewNotes->m_Timing : out.m_SongTiming); RString tmp = sParams[1]; Trim( tmp ); - ProcessSpeeds( timing, iRowsPerBeat, tmp ); + ProcessSpeeds( timing, tmp, iRowsPerBeat ); } else if( sValueName=="MULTIPLIER" ) @@ -679,7 +390,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out ) { TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); - ProcessFakes( timing, iRowsPerBeat, sParams[1] ); + ProcessFakes( timing, sParams[1], iRowsPerBeat ); } else if( sValueName=="METERTYPE" ) @@ -706,7 +417,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out ) continue; } - LoadFromSMATokens( + LoadFromTokens( sParams[1], sParams[2], sParams[3], @@ -727,121 +438,6 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out ) return true; } -void SMALoader::GetApplicableFiles( const RString &sPath, vector &out ) -{ - GetDirListing( sPath + RString("*.sma"), out ); -} - -bool SMALoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ) -{ - LOG->Trace( "SMALoader::LoadEditFromFile(%s)", sEditFilePath.c_str() ); - - int iBytes = FILEMAN->GetFileSizeInBytes( sEditFilePath ); - if( iBytes > MAX_EDIT_STEPS_SIZE_BYTES ) - { - LOG->UserLog( "Edit file", sEditFilePath, "is unreasonably large. It won't be loaded." ); - return false; - } - - MsdFile msd; - if( !msd.ReadFile( sEditFilePath, true ) ) // unescape - { - LOG->UserLog( "Edit file", sEditFilePath, "couldn't be opened: %s", msd.GetError().c_str() ); - return false; - } - - return LoadEditFromMsd( msd, sEditFilePath, slot, bAddStepsToSong ); -} - -bool SMALoader::LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot ) -{ - MsdFile msd; - msd.ReadFromString( sBuffer, true ); // unescape - return LoadEditFromMsd( msd, sEditFilePath, slot, true ); -} - -bool SMALoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ) -{ - Song* pSong = NULL; - - for( unsigned i=0; iUserLog( "Edit file", sEditFilePath, "has more than one #SONG tag." ); - return false; - } - - RString sSongFullTitle = sParams[1]; - sSongFullTitle.Replace( '\\', '/' ); - - pSong = SONGMAN->FindSong( sSongFullTitle ); - if( pSong == NULL ) - { - LOG->UserLog( "Edit file", sEditFilePath, "requires a song \"%s\" that isn't present.", sSongFullTitle.c_str() ); - return false; - } - - if( pSong->GetNumStepsLoadedFromProfile(slot) >= MAX_EDITS_PER_SONG_PER_PROFILE ) - { - LOG->UserLog( "Song file", sSongFullTitle, "already has the maximum number of edits allowed for ProfileSlotP%d.", slot+1 ); - return false; - } - } - - else if( sValueName=="NOTES" ) - { - if( pSong == NULL ) - { - LOG->UserLog( "Edit file", sEditFilePath, "doesn't have a #SONG tag preceeding the first #NOTES tag." ); - return false; - } - - if( iNumParams < 7 ) - { - LOG->UserLog( "Edit file", sEditFilePath, "has %d fields in a #NOTES tag, but should have at least 7.", iNumParams ); - continue; - } - - if( !bAddStepsToSong ) - return true; - - Steps* pNewNotes = pSong->CreateSteps(); - LoadFromSMATokens( - sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], - *pNewNotes); - - pNewNotes->SetLoadedFromProfile( slot ); - pNewNotes->SetDifficulty( Difficulty_Edit ); - pNewNotes->SetFilename( sEditFilePath ); - - if( pSong->IsEditAlreadyLoaded(pNewNotes) ) - { - LOG->UserLog( "Edit file", sEditFilePath, "is a duplicate of another edit that was already loaded." ); - SAFE_DELETE( pNewNotes ); - return false; - } - - pSong->AddSteps( pNewNotes ); - return true; // Only allow one Steps per edit file! - } - else - { - LOG->UserLog( "Edit file", sEditFilePath, "has an unexpected value \"%s\".", sValueName.c_str() ); - } - } - - return true; -} - /** * @file * @author Aldo Fregoso, Jason Felds (c) 2009-2011 diff --git a/src/NotesLoaderSMA.h b/src/NotesLoaderSMA.h index c9e3dfa811..f38acac470 100644 --- a/src/NotesLoaderSMA.h +++ b/src/NotesLoaderSMA.h @@ -2,6 +2,7 @@ #define NOTES_LOADER_SMA_H #include "GameConstantsAndTypes.h" +#include "NotesLoaderSM.h" #include "BackgroundUtil.h" class MsdFile; @@ -20,37 +21,21 @@ enum SMALoadingStates }; /** @brief Reads a Song from a .SMA file. */ -namespace SMALoader -{ - void LoadFromSMATokens( RString sStepsType, - RString sDescription, - RString sDifficulty, - RString sMeter, - RString sRadarValues, - RString sNoteData, - Steps &out ); +struct SMALoader : public SMLoader +{ + SMALoader() : SMLoader(".sma") {} - bool LoadFromDir( const RString &sPath, Song &out ); - void TidyUpData( Song &song, bool bFromCache ); - - bool LoadFromSMAFile( const RString &sPath, Song &out ); - void GetApplicableFiles( const RString &sPath, vector &out ); - - bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ); - bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot ); - bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ); - bool LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChangeExpression ); + virtual bool LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache = false ); + void ProcessBeatsPerMeasure( TimingData &out, const RString sParam ); - bool ProcessBPMs( TimingData &out, const int iRowsPerBeat, const RString sParam ); - void ProcessStops( TimingData &out, const int iRowsPerBeat, const RString sParam ); - void ProcessDelays( TimingData &out, const int iRowsPerBeat, const RString sParam ); - void ProcessTickcounts( TimingData &out, const int iRowsPerBeat, const RString sParam ); void ProcessMultipliers( TimingData &out, const int iRowsPerBeat, const RString sParam ); - void ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RString sParam ); - void ProcessFakes( TimingData &out, const int iRowsPerBeat, const RString sParam ); - - float RowToBeat( RString sLine, const int iRowsPerBeat ); + /** + * @brief Process the Speed Segments from the string. + * @param out the TimingData being modified. + * @param line the string in question. + * @param rowsPerBeat the number of rows per beat for this purpose. */ + virtual void ProcessSpeeds( TimingData &out, const RString line, const int rowsPerBeat ); }; #endif diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index 04a4fdc167..3c13899ff7 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -14,38 +14,6 @@ #include "Attack.h" #include "PrefsManager.h" -/** - * @brief A custom .edit file can only be so big before we have to reject it. - */ -const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60 KB - -/** - * @brief Attempt to load any background changes in use by this song. - * @param change a reference to the background change. - * @param sBGChangeExpression a reference to the list of changes to be made. - * @return its success or failure. - */ -bool LoadFromBGSSCChangesString( BackgroundChange &change, const RString &sBGChangeExpression ) -{ - return SMLoader::LoadFromBGChangesString( change, sBGChangeExpression ); -} - -bool SSCLoader::LoadFromDir( const RString &sPath, Song &out ) -{ - vector aFileNames; - GetApplicableFiles( sPath, aFileNames ); - - if( aFileNames.size() > 1 ) - { - LOG->UserLog( "Song", sPath, "has more than one SSC file. Only one SSC file is allowed per song." ); - return false; - } - - ASSERT( aFileNames.size() == 1 ); // Ensure one was found entirely. - - return LoadFromSSCFile( sPath + aFileNames[0], out ); -} - void SSCLoader::ProcessWarps( TimingData &out, const RString sParam, const float fVersion ) { vector arrayWarpExpressions; @@ -108,10 +76,10 @@ void SSCLoader::ProcessLabels( TimingData &out, const RString sParam ) } } -void SSCLoader::ProcessCombos( TimingData &out, const RString sParam ) +void SSCLoader::ProcessCombos( TimingData &out, const RString line, const int rowsPerBeat ) { vector arrayComboExpressions; - split( sParam, ",", arrayComboExpressions ); + split( line, ",", arrayComboExpressions ); for( unsigned f=0; f vs1; - split( sParam, ",", vs1 ); - - FOREACH_CONST( RString, vs1, s1 ) - { - vector vs2; - split( *s1, "=", vs2 ); - - if( vs2[0] == 0 && vs2.size() == 2 ) // First one always seems to have 2. - { - vs2.push_back("0"); - } - - if( vs2.size() == 3 ) // use beats by default. - { - vs2.push_back("0"); - } - - if( vs2.size() < 4 ) - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with %i values.", (int)vs2.size() ); - continue; - } - - const float fBeat = StringToFloat( vs2[0] ); - - SpeedSegment seg( fBeat, StringToFloat( vs2[1] ), StringToFloat( vs2[2] )); - seg.SetUnit(StringToInt(vs2[3])); - - if( fBeat < 0 ) - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f.", fBeat ); - continue; - } - - if( seg.GetLength() < 0 ) - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f, length %f.", fBeat, seg.GetLength() ); - continue; - } - - out.AddSpeedSegment( seg ); - } -} - void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam ) { vector vs1; @@ -207,37 +128,8 @@ void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam ) } } -void SSCLoader::ProcessFakes( TimingData &out, const RString sParam ) -{ - vector arrayFakeExpressions; - split( sParam, ",", arrayFakeExpressions ); - - for( unsigned b=0; b arrayFakeValues; - split( arrayFakeExpressions[b], "=", arrayFakeValues ); - // XXX: Hard to tell which file caused this. - if( arrayFakeValues.size() != 2 ) - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #FAKES value \"%s\" (must have exactly one '='), ignored.", - arrayFakeExpressions[b].c_str() ); - continue; - } - - const float fBeat = StringToFloat( arrayFakeValues[0] ); - const float fNewBeat = StringToFloat( arrayFakeValues[1] ); - - if(fNewBeat > 0) - out.AddFakeSegment( FakeSegment(fBeat, fNewBeat) ); - else - { - LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Fake at beat %f, BPM %f.", fBeat, fNewBeat ); - } - } -} - -bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCache ) +bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache ) { LOG->Trace( "Song::LoadFromSSCFile(%s)", sPath.c_str() ); @@ -421,7 +313,7 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach for( unsigned b=0; b &out ) -{ - GetDirListing( sPath + RString("*.ssc"), out ); -} - bool SSCLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ) { LOG->Trace( "SSCLoader::LoadEditFromFile(%s)", sEditFilePath.c_str() ); @@ -883,9 +770,13 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat else { pNewNotes = pSong->CreateSteps(); - SMLoader::LoadFromSMTokens( - sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], - *pNewNotes); + LoadFromTokens(sParams[1], + sParams[2], + sParams[3], + sParams[4], + sParams[5], + sParams[6], + *pNewNotes); } pNewNotes->SetLoadedFromProfile( slot ); @@ -913,11 +804,6 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat return bSSCFormat; } -void SSCLoader::TidyUpData( Song &song, bool bFromCache ) -{ - SMLoader::TidyUpData(song, bFromCache); -} - /* * (c) 2011 Jason Felds * All rights reserved. diff --git a/src/NotesLoaderSSC.h b/src/NotesLoaderSSC.h index 936a24e219..3031932841 100644 --- a/src/NotesLoaderSSC.h +++ b/src/NotesLoaderSSC.h @@ -3,6 +3,7 @@ #define NotesLoaderSSC_H #include "GameConstantsAndTypes.h" +#include "NotesLoaderSM.h" class MsdFile; class Song; @@ -29,15 +30,10 @@ const float VERSION_SPLIT_TIMING = 0.7f; /** * @brief The SSCLoader handles all of the parsing needed for .ssc files. */ -namespace SSCLoader +struct SSCLoader : public SMLoader { - /** - * @brief Attempt to load a song from a specified path. - * @param sPath a const reference to the path on the hard drive to check. - * @param out a reference to the Song that will retrieve the song information. - * @return its success or failure. - */ - bool LoadFromDir( const RString &sPath, Song &out ); + SSCLoader() : SMLoader(".ssc") {} + /** * @brief Attempt to load the specified ssc file. * @param sPath a const reference to the path on the hard drive to check. @@ -45,13 +41,8 @@ namespace SSCLoader * @param bFromCache a check to see if we are getting certain information from the cache file. * @return its success or failure. */ - bool LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCache = false ); - /** - * @brief Retrieve the list of .ssc files. - * @param sPath a const reference to the path on the hard drive to check. - * @param out a vector of files found in the path. - */ - void GetApplicableFiles( const RString &sPath, vector &out ); + virtual bool LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache = false ); + /** * @brief Attempt to load an edit from the hard drive. * @param sEditFilePath a path on the hard drive to check. @@ -69,21 +60,14 @@ namespace SSCLoader * @return its success or failure. */ bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong ); - /** - * @brief Perform some cleanup on the loaded song. - * @param song a reference to the song that may need cleaning up. - * @param bFromCache a flag to determine if this song is loaded from a cache file. - */ - void TidyUpData( Song &song, bool bFromCache ); void ProcessWarps( TimingData &, const RString, const float ); void ProcessLabels( TimingData &, const RString ); - void ProcessCombos( TimingData &, const RString ); - void ProcessSpeeds( TimingData &, const RString ); + virtual void ProcessCombos( TimingData &, const RString, const int = -1 ); void ProcessScrolls( TimingData &, const RString ); - void ProcessFakes( TimingData &, const RString ); -} +}; + #endif /** * @file diff --git a/src/Player.cpp b/src/Player.cpp index 6179b16d83..c853fc86f9 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -1538,7 +1538,7 @@ int Player::GetClosestNoteDirectional( int col, int iStartRow, int iEndRow, bool // Is this the row we want? do { const TapNote &tn = begin->second; - if( m_Timing->IsWarpAtRow( begin->first ) || m_Timing->IsFakeAtRow( begin->first ) ) + if (!m_Timing->IsJudgableAtRow( begin->first )) break; if( tn.type == TapNote::empty ) break; @@ -1594,7 +1594,7 @@ int Player::GetClosestNonEmptyRowDirectional( int iStartRow, int iEndRow, bool b ++iter; continue; } - if( m_Timing->IsWarpAtRow( iter.Row() ) || m_Timing->IsFakeAtRow( iter.Row() ) ) + if (!m_Timing->IsJudgableAtRow(iter.Row())) { ++iter; continue; @@ -2118,7 +2118,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b // Stepped too close to mine? if( !bRelease && ( REQUIRE_STEP_ON_MINES == !bHeld ) && fSecondsFromExact <= GetWindowSeconds(TW_Mine) && - !m_Timing->IsWarpAtRow(iSongRow) && !m_Timing->IsFakeAtRow(iSongRow)) + m_Timing->IsJudgableAtRow(iSongRow)) score = TNS_HitMine; break; @@ -2616,7 +2616,7 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds ) continue; // Ignore all notes in WarpSegments or FakeSegments. - if( m_Timing->IsWarpAtRow( iter.Row() ) || m_Timing->IsFakeAtRow( iter.Row() ) ) + if (!m_Timing->IsJudgableAtRow(iter.Row())) continue; if( tn.type == TapNote::mine ) @@ -2651,7 +2651,7 @@ void Player::UpdateJudgedRows() int iRow = iter.Row(); // Do not judge arrows in WarpSegments or FakeSegments - if( m_Timing->IsWarpAtRow(iRow) || m_Timing->IsFakeAtRow(iRow) ) + if (!m_Timing->IsJudgableAtRow(iRow)) continue; if( iLastSeenRow != iRow ) @@ -2852,7 +2852,8 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now ) // check to see if there's a note at the crossed row if( m_pPlayerState->m_PlayerController != PC_HUMAN ) { - if( tn.type != TapNote::empty && tn.type != TapNote::fake && tn.result.tns == TNS_None ) + if(tn.type != TapNote::empty && tn.type != TapNote::fake && tn.result.tns == TNS_None + && this->m_Timing->IsJudgableAtRow(iRow) ) { Step( iTrack, iRow, now, false, false ); if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY ) @@ -2878,7 +2879,7 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now ) } else if( CHECKPOINTS_USE_TIME_SIGNATURES ) { - TimeSignatureSegment tSignature = m_Timing->GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iLastRowCrossed ) ); + TimeSignatureSegment & tSignature = m_Timing->GetTimeSignatureSegmentAtRow( iLastRowCrossed ); // Most songs are in 4/4 time. The frequency for checking tick counts should reflect that. iCheckpointFrequencyRows = ROWS_PER_BEAT * tSignature.GetDen() / (tSignature.GetNum() * 4); @@ -2921,8 +2922,11 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now ) bool bHoldOverlapsRow = iFirstCheckpointOfHold <= r && r <= iLastCheckpointOfHold; if( !bHoldOverlapsRow ) continue; + + viColsWithHold.push_back( iTrack ); + if( tn.HoldResult.fLife > 0 ) { ++iNumHoldsHeldThisRow; @@ -2934,6 +2938,7 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now ) ++tn.HoldResult.iCheckpointsMissed; } } + GAMESTATE->SetProcessedTimingData(this->m_Timing); // TODO: Find a better way of handling hold checkpoints with other taps. if( !viColsWithHold.empty() && ( CHECKPOINTS_TAPS_SEPARATE_JUDGMENT || m_NoteData.GetNumTapNotesInRow( iLastRowCrossed ) == 0 ) ) @@ -2990,7 +2995,7 @@ void Player::HandleTapRowScore( unsigned row ) #endif // Do not score rows in WarpSegments or FakeSegments - if( m_Timing->IsWarpAtRow( row ) || m_Timing->IsFakeAtRow( row ) ) + if (!m_Timing->IsJudgableAtRow(row)) return; if( GAMESTATE->m_bDemonstrationOrJukebox ) @@ -3094,7 +3099,7 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH #endif // WarpSegments and FakeSegments aren't judged in any way. - if( m_Timing->IsWarpAtRow( iRow ) || m_Timing->IsFakeAtRow( iRow ) ) + if (!m_Timing->IsJudgableAtRow(iRow)) return; // don't accumulate combo if AutoPlay is on. diff --git a/src/RageSoundReader_Extend.cpp b/src/RageSoundReader_Extend.cpp index 0c316c7a50..38beacfa35 100644 --- a/src/RageSoundReader_Extend.cpp +++ b/src/RageSoundReader_Extend.cpp @@ -15,6 +15,7 @@ RageSoundReader_Extend::RageSoundReader_Extend( RageSoundReader *pSource ): RageSoundReader_Filter( pSource ) { + ASSERT_M(pSource, "The music file was not found! Was it deleted or moved while the game was on?"); m_iPositionFrames = pSource->GetNextSourceFrame(); m_StopMode = M_STOP; diff --git a/src/ScoreKeeperNormal.cpp b/src/ScoreKeeperNormal.cpp index ce3874aa5a..d5b3494a5f 100644 --- a/src/ScoreKeeperNormal.cpp +++ b/src/ScoreKeeperNormal.cpp @@ -120,6 +120,7 @@ void ScoreKeeperNormal::Load( * forced and not chosen by the user. */ NoteDataUtil::TransformNoteData( nd, aa, pSteps->m_StepsType, pSong ); RadarValues rvPre; + GAMESTATE->SetProcessedTimingData(&pSteps->m_Timing); NoteDataUtil::CalculateRadarValues( nd, pSong->m_fMusicLengthSeconds, rvPre ); /* Apply user transforms to find out how the notes will really look. @@ -132,6 +133,7 @@ void ScoreKeeperNormal::Load( NoteDataUtil::TransformNoteData( nd, m_pPlayerState->m_PlayerOptions.GetStage(), pSteps->m_StepsType ); RadarValues rvPost; NoteDataUtil::CalculateRadarValues( nd, pSong->m_fMusicLengthSeconds, rvPost ); + GAMESTATE->SetProcessedTimingData(NULL); iTotalPossibleDancePoints += this->GetPossibleDancePoints( rvPre, rvPost ); iTotalPossibleGradePoints += this->GetPossibleGradePoints( rvPre, rvPost ); @@ -217,6 +219,8 @@ void ScoreKeeperNormal::OnNextSong( int iSongInCourseIndex, const Steps* pSteps, ASSERT( m_iMaxPossiblePoints >= 0 ); m_iMaxScoreSoFar += m_iMaxPossiblePoints; + GAMESTATE->SetProcessedTimingData(const_cast(&pSteps->m_Timing)); + m_iNumTapsAndHolds = pNoteData->GetNumRowsWithTapOrHoldHead() + pNoteData->GetNumHoldNotes() + pNoteData->GetNumRolls(); @@ -230,6 +234,8 @@ void ScoreKeeperNormal::OnNextSong( int iSongInCourseIndex, const Steps* pSteps, ASSERT( m_iPointBonus >= 0 ); m_iTapNotesHit = 0; + + GAMESTATE->SetProcessedTimingData(NULL); } static int GetScore(int p, int Z, int S, int n) diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index c6905545f4..b5ff78ffae 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -61,6 +61,7 @@ const float RECORD_HOLD_SECONDS = 0.3f; AutoScreenMessage( SM_UpdateTextInfo ); AutoScreenMessage( SM_BackFromMainMenu ); AutoScreenMessage( SM_BackFromAreaMenu ); +AutoScreenMessage( SM_BackFromAlterMenu ); AutoScreenMessage( SM_BackFromStepsInformation ); AutoScreenMessage( SM_BackFromOptions ); AutoScreenMessage( SM_BackFromSongInformation ); @@ -170,6 +171,7 @@ void ScreenEdit::InitEditMappings() m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; + // Escape, Enter = exit play/record m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); @@ -280,6 +282,7 @@ void ScreenEdit::InitEditMappings() m_EditMappingsMenuButton.button[EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; m_EditMappingsMenuButton.button[EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_AREA_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_ALTER_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Ca); m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_INPUT_HELP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F1); m_EditMappingsDeviceInput.button[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); @@ -505,27 +508,58 @@ static MenuDef g_MainMenu( MenuRowDef( ScreenEdit::exit, "Exit Edit Mode", true, EditMode_Practice, true, true, 0, NULL ) ); +static MenuDef g_AlterMenu( + "ScreenMiniMenuAlterMenu", + MenuRowDef(ScreenEdit::cut, "Cut", true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::copy, "Copy", true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::clear, "Clear area", true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::quantize, "Quantize", true, + EditMode_Practice, true, true, 0, + "4th","8th","12th","16th","24th","32nd","48th","64th","192nd"), + MenuRowDef(ScreenEdit::turn, "Turn", true, + EditMode_Practice, true, true, 0, "Left","Right","Mirror","Shuffle","SuperShuffle" ), + MenuRowDef(ScreenEdit::transform, "Transform", true, + EditMode_Practice, true, true, 0, "NoHolds","NoMines","Little","Wide", + "Big","Quick","Skippy","Mines","Echo","Stomp","Planted","Floored", + "Twister","NoJumps","NoHands","NoQuads","NoStretch" ), + MenuRowDef(ScreenEdit::alter, "Alter", true, + EditMode_Practice, true, true, 0, "Autogen To Fill Width","Backwards","Swap Sides", + "Copy Left To Right","Copy Right To Left","Clear Left","Clear Right", + "Collapse To One","Collapse Left","Shift Left","Shift Right" ), + MenuRowDef(ScreenEdit::tempo, "Tempo", true, + EditMode_Full, true, true, 0, "Compress 2x","Compress 3->2", + "Compress 4->3","Expand 3->4","Expand 2->3","Expand 2x" ), + MenuRowDef(ScreenEdit::play, "Play selection", true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::record, "Record in selection", true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_pause, "Convert selection to pause", true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_delay, "Convert selection to delay", true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_warp, "Convert selection to warp", true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_fake, "Convert selection to fake", true, + EditMode_Full, true, true, 0, NULL ) +); + static MenuDef g_AreaMenu( "ScreenMiniMenuAreaMenu", - MenuRowDef( ScreenEdit::cut, "Cut", true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::copy, "Copy", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::paste_at_current_beat, "Paste at current beat", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::paste_at_begin_marker, "Paste at begin marker", true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::clear, "Clear area", true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::quantize, "Quantize", true, EditMode_Practice, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd"), - MenuRowDef( ScreenEdit::turn, "Turn", true, EditMode_Practice, true, true, 0, "Left","Right","Mirror","Shuffle","SuperShuffle" ), - MenuRowDef( ScreenEdit::transform, "Transform", true, EditMode_Practice, true, true, 0, "NoHolds","NoMines","Little","Wide","Big","Quick","Skippy","Mines","Echo","Stomp","Planted","Floored","Twister","NoJumps","NoHands","NoQuads","NoStretch" ), - MenuRowDef( ScreenEdit::alter, "Alter", true, EditMode_Practice, true, true, 0, "Autogen To Fill Width","Backwards","Swap Sides","Copy Left To Right","Copy Right To Left","Clear Left","Clear Right","Collapse To One","Collapse Left","Shift Left","Shift Right" ), - MenuRowDef( ScreenEdit::tempo, "Tempo", true, EditMode_Full, true, true, 0, "Compress 2x","Compress 3->2","Compress 4->3","Expand 3->4","Expand 2->3","Expand 2x" ), - MenuRowDef( ScreenEdit::play, "Play selection", true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::record, "Record in selection", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::insert_and_shift, "Insert beat and shift down", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::delete_and_shift, "Delete beat and shift up", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::shift_pauses_forward, "Shift all timing changes down", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::shift_pauses_backward, "Shift all timing changes up", true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::convert_to_pause, "Convert selection to pause", true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::convert_pause_to_beat, "Convert pause to beats", true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::undo, "Undo", true, EditMode_Practice, true, true, 0, NULL ) + MenuRowDef(ScreenEdit::convert_pause_to_beat, "Convert pause to beats", true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::undo, "Undo", true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::clear_clipboard, "Clear clipboard", true, + EditMode_Practice, true, true, 0, NULL ) + ); static MenuDef g_StepsInformation( @@ -1152,6 +1186,9 @@ void ScreenEdit::UpdateTextInfo() sText += ssprintf( TAP_NOTE_TYPE_FORMAT.GetValue(), TAP_NOTE_TYPE.GetValue().c_str(), TapNoteTypeToString( m_selectedTap.type ).c_str() ); break; } + + GAMESTATE->SetProcessedTimingData(&m_pSteps->m_Timing); + sText += ssprintf( NUM_STEPS_FORMAT.GetValue(), TAP_STEPS.GetValue().c_str(), m_NoteDataEdit.GetNumTapNotes() ); sText += ssprintf( NUM_JUMPS_FORMAT.GetValue(), JUMPS.GetValue().c_str(), m_NoteDataEdit.GetNumJumps() ); sText += ssprintf( NUM_HANDS_FORMAT.GetValue(), HANDS.GetValue().c_str(), m_NoteDataEdit.GetNumHands() ); @@ -1182,6 +1219,8 @@ void ScreenEdit::UpdateTextInfo() } m_textInfo.SetText( sText ); + + GAMESTATE->SetProcessedTimingData(NULL); } void ScreenEdit::DrawPrimitives() @@ -1294,7 +1333,7 @@ static int FindAttackAtTime( const AttackArray& attacks, float fStartTime ) } static LocalizedString BG_CHANGE_STEP_TIMING ( "ScreenEdit", "You must be in Song Timing Mode to edit BG Changes." ); - +static LocalizedString ALTER_MENU_NO_SELECTION ( "ScreenEdit", "You must have an area selected to enter the Alter Menu." ); static LocalizedString SWITCHED_TO ( "ScreenEdit", "Switched to" ); static LocalizedString NO_BACKGROUNDS_AVAILABLE ( "ScreenEdit", "No backgrounds available" ); static ThemeMetric INVERT_SCROLL_BUTTONS ( "ScreenEdit", "InvertScrollSpeedButtons" ); @@ -1556,24 +1595,27 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) case EDIT_BUTTON_OPEN_AREA_MENU: { // update enabled/disabled in g_AreaMenu - bool bAreaSelected = m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1; - g_AreaMenu.rows[cut].bEnabled = bAreaSelected; - g_AreaMenu.rows[copy].bEnabled = bAreaSelected; g_AreaMenu.rows[paste_at_current_beat].bEnabled = !m_Clipboard.IsEmpty(); g_AreaMenu.rows[paste_at_begin_marker].bEnabled = !m_Clipboard.IsEmpty() != 0 && m_NoteFieldEdit.m_iBeginMarker!=-1; - g_AreaMenu.rows[clear].bEnabled = bAreaSelected; - g_AreaMenu.rows[quantize].bEnabled = bAreaSelected; - g_AreaMenu.rows[turn].bEnabled = bAreaSelected; - g_AreaMenu.rows[transform].bEnabled = bAreaSelected; - g_AreaMenu.rows[alter].bEnabled = bAreaSelected; - g_AreaMenu.rows[tempo].bEnabled = bAreaSelected; - g_AreaMenu.rows[play].bEnabled = bAreaSelected; - g_AreaMenu.rows[record].bEnabled = bAreaSelected; - g_AreaMenu.rows[convert_to_pause].bEnabled = bAreaSelected; g_AreaMenu.rows[undo].bEnabled = m_bHasUndo; EditMiniMenu( &g_AreaMenu, SM_BackFromAreaMenu ); } break; + case EDIT_BUTTON_OPEN_ALTER_MENU: + { + bool bAreaSelected = m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1; + if (!bAreaSelected) + { + SCREENMAN->SystemMessage( ALTER_MENU_NO_SELECTION ); + SCREENMAN->PlayInvalidSound(); + } + else + { + EditMiniMenu(&g_AlterMenu, SM_BackFromAlterMenu); + } + break; + + } case EDIT_BUTTON_OPEN_EDIT_MENU: EditMiniMenu( &g_MainMenu, SM_BackFromMainMenu ); break; @@ -2039,7 +2081,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) case EDIT_BUTTON_RECORD_SELECTION: if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ) { - HandleAreaMenuChoice( record ); + HandleAlterMenuChoice( record ); } else { @@ -2639,6 +2681,10 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) { HandleAreaMenuChoice( (AreaMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); } + else if( SM == SM_BackFromAlterMenu ) + { + HandleAlterMenuChoice( (AlterMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); + } else if( SM == SM_BackFromStepsInformation ) { HandleStepsInformationChoice( (StepsInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); @@ -3237,12 +3283,13 @@ static LocalizedString SAVE_CHANGES_BEFORE_EXITING ( "ScreenEdit", "Do you want void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAnswers ) { + GAMESTATE->SetProcessedTimingData(&m_pSteps->m_Timing); switch( c ) { DEFAULT_FAIL( c ); case play_selection: if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ) - HandleAreaMenuChoice( play ); + HandleAlterMenuChoice( play ); else if( m_NoteFieldEdit.m_iBeginMarker!=-1 ) HandleMainMenuChoice( play_selection_start_to_end ); else @@ -3455,36 +3502,277 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAns } break; }; + GAMESTATE->SetProcessedTimingData(NULL); +} + +void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAnswers, bool bAllowUndo) +{ + bool bSaveUndo = true; + switch (c) + { + case play: + case record: + case cut: + case copy: + { + bSaveUndo = false; + } + default: + break; + } + + if( bSaveUndo ) + SetDirty( true ); + + /* We call HandleAreaMenuChoice recursively. Only the outermost + * HandleAreaMenuChoice should allow Undo so that the inner calls don't + * also save Undo and mess up the outermost */ + if( !bAllowUndo ) + bSaveUndo = false; + + if( bSaveUndo ) + SaveUndo(); + + switch(c) + { + case cut: + { + HandleAlterMenuChoice( copy ); + HandleAlterMenuChoice( clear ); + } + break; + case copy: + { + ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); + m_Clipboard.ClearAll(); + m_Clipboard.CopyRange( m_NoteDataEdit, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); + } + break; + case clear: + { + ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); + m_NoteDataEdit.ClearRange( m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); + } + break; + case quantize: + { + NoteType nt = (NoteType)iAnswers[c]; + NoteDataUtil::SnapToNearestNoteType(m_NoteDataEdit, nt, nt, + m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker ); + break; + } + case turn: + { + const NoteData OldClipboard( m_Clipboard ); + HandleAlterMenuChoice( cut ); + + StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; + TurnType tt = (TurnType)iAnswers[c]; + switch( tt ) + { + DEFAULT_FAIL( tt ); + case left: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::left ); break; + case right: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::right ); break; + case mirror: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::mirror ); break; + case shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::shuffle ); break; + case super_shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::super_shuffle ); break; + } + + HandleAreaMenuChoice( paste_at_begin_marker ); + m_Clipboard = OldClipboard; + } + break; + case transform: + { + int iBeginRow = m_NoteFieldEdit.m_iBeginMarker; + int iEndRow = m_NoteFieldEdit.m_iEndMarker; + TransformType tt = (TransformType)iAnswers[c]; + StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; + + switch( tt ) + { + DEFAULT_FAIL( tt ); + case noholds: NoteDataUtil::RemoveHoldNotes( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nomines: NoteDataUtil::RemoveMines( m_NoteDataEdit, iBeginRow, iBeginRow ); break; + case little: NoteDataUtil::Little( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case wide: NoteDataUtil::Wide( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case big: NoteDataUtil::Big( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case quick: NoteDataUtil::Quick( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case skippy: NoteDataUtil::Skippy( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case add_mines: NoteDataUtil::AddMines( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case echo: NoteDataUtil::Echo( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case stomp: NoteDataUtil::Stomp( m_NoteDataEdit, st, iBeginRow, iEndRow ); break; + case planted: NoteDataUtil::Planted( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case floored: NoteDataUtil::Floored( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case twister: NoteDataUtil::Twister( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nojumps: NoteDataUtil::RemoveJumps( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nohands: NoteDataUtil::RemoveHands( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case noquads: NoteDataUtil::RemoveQuads( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nostretch: NoteDataUtil::RemoveStretch( m_NoteDataEdit, st, iBeginRow, iBeginRow );break; + } + + // bake in the additions + NoteDataUtil::ConvertAdditionsToRegular( m_NoteDataEdit ); + break; + } + case alter: + { + const NoteData OldClipboard( m_Clipboard ); + HandleAlterMenuChoice( cut ); + + AlterType at = (AlterType)iAnswers[c]; + switch( at ) + { + DEFAULT_FAIL( at ); + case autogen_to_fill_width: + { + NoteData temp( m_Clipboard ); + int iMaxNonEmptyTrack = NoteDataUtil::GetMaxNonEmptyTrack( temp ); + if( iMaxNonEmptyTrack == -1 ) + break; + temp.SetNumTracks( iMaxNonEmptyTrack+1 ); + NoteDataUtil::LoadTransformedSlidingWindow( temp, m_Clipboard, m_Clipboard.GetNumTracks() ); + NoteDataUtil::RemoveStretch( m_Clipboard, GAMESTATE->m_pCurSteps[0]->m_StepsType ); + } + break; + case backwards: NoteDataUtil::Backwards( m_Clipboard ); break; + case swap_sides: NoteDataUtil::SwapSides( m_Clipboard ); break; + case copy_left_to_right: NoteDataUtil::CopyLeftToRight( m_Clipboard ); break; + case copy_right_to_left: NoteDataUtil::CopyRightToLeft( m_Clipboard ); break; + case clear_left: NoteDataUtil::ClearLeft( m_Clipboard ); break; + case clear_right: NoteDataUtil::ClearRight( m_Clipboard ); break; + case collapse_to_one: NoteDataUtil::CollapseToOne( m_Clipboard ); break; + case collapse_left: NoteDataUtil::CollapseLeft( m_Clipboard ); break; + case shift_left: NoteDataUtil::ShiftLeft( m_Clipboard ); break; + case shift_right: NoteDataUtil::ShiftRight( m_Clipboard ); break; + } + + HandleAreaMenuChoice( paste_at_begin_marker ); + m_Clipboard = OldClipboard; + break; + } + case tempo: + { + // This affects all steps. + AlterType at = (AlterType)iAnswers[c]; + float fScale = -1; + + switch( at ) + { + DEFAULT_FAIL( at ); + case compress_2x: fScale = 0.5f; break; + case compress_3_2: fScale = 2.0f/3; break; + case compress_4_3: fScale = 0.75f; break; + case expand_4_3: fScale = 4.0f/3; break; + case expand_3_2: fScale = 1.5f; break; + case expand_2x: fScale = 2; break; + } + + int iStartIndex = m_NoteFieldEdit.m_iBeginMarker; + int iEndIndex = m_NoteFieldEdit.m_iEndMarker; + int iNewEndIndex = iEndIndex + lrintf( (iEndIndex - iStartIndex) * (fScale - 1) ); + + // scale currently editing notes + NoteDataUtil::ScaleRegion( m_NoteDataEdit, fScale, iStartIndex, iEndIndex ); + + // scale timing data + GetAppropriateTiming().ScaleRegion(fScale, + m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker, true ); + + m_NoteFieldEdit.m_iEndMarker = iNewEndIndex; + break; + + } + + case play: + ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); + m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; + m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; + TransitionEditState( STATE_PLAYING ); + break; + case record: + ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); + m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; + m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; + TransitionEditState( STATE_RECORDING ); + break; + case convert_to_pause: + { + ASSERT_M( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1, "Attempted to convert beats outside the notefield to pauses!" ); + float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); + float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); + + // The length of the stop segment we're going to create. This includes time spent in any + // stops in the selection, which will be deleted and subsumed into the new stop. + float fStopLength = fMarkerEnd - fMarkerStart; + + // be sure not to clobber the row at the start - a row at the end + // can be dropped safely, though + NoteDataUtil::DeleteRows( m_NoteDataEdit, + m_NoteFieldEdit.m_iBeginMarker + 1, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker + ); + GetAppropriateTiming().DeleteRows( m_NoteFieldEdit.m_iBeginMarker + 1, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker ); + GetAppropriateTiming().SetStopAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength ); + m_NoteFieldEdit.m_iBeginMarker = -1; + m_NoteFieldEdit.m_iEndMarker = -1; + break; + } + case convert_to_delay: + { + ASSERT_M( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1, "Attempted to convert beats outside the notefield to pauses!" ); + float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); + float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); + + // The length of the delay segment we're going to create. This includes time spent in any + // stops in the selection, which will be deleted and subsumed into the new stop. + float fStopLength = fMarkerEnd - fMarkerStart; + + NoteDataUtil::DeleteRows( m_NoteDataEdit, + m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker + ); + GetAppropriateTiming().DeleteRows( m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker ); + GetAppropriateTiming().SetDelayAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength ); + m_NoteFieldEdit.m_iBeginMarker = -1; + m_NoteFieldEdit.m_iEndMarker = -1; + break; + } + case convert_to_warp: + { + float startBeat = NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker); + float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - startBeat; + GetAppropriateTiming().SetWarpAtBeat(startBeat,lengthBeat); + SetDirty(true); + break; + } + case convert_to_fake: + { + float startBeat = NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker); + float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - startBeat; + GetAppropriateTiming().SetFakeAtBeat(startBeat,lengthBeat); + SetDirty(true); + break; + } + + } + } void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAnswers, bool bAllowUndo ) { - bool bSaveUndo = false; + bool bSaveUndo = true; switch( c ) { - DEFAULT_FAIL( c ); - case cut: - case copy: - case play: - case record: + case clear_clipboard: case undo: bSaveUndo = false; break; - case paste_at_current_beat: - case paste_at_begin_marker: - case clear: - case quantize: - case turn: - case transform: - case alter: - case tempo: - case insert_and_shift: - case delete_and_shift: - case shift_pauses_forward: - case shift_pauses_backward: - case convert_to_pause: - case convert_pause_to_beat: - bSaveUndo = true; + default: break; } @@ -3503,19 +3791,7 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns switch( c ) { DEFAULT_FAIL( c ); - case cut: - { - HandleAreaMenuChoice( copy ); - HandleAreaMenuChoice( clear ); - } - break; - case copy: - { - ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); - m_Clipboard.ClearAll(); - m_Clipboard.CopyRange( m_NoteDataEdit, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); - } - break; + case paste_at_current_beat: case paste_at_begin_marker: { @@ -3536,172 +3812,6 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns m_NoteDataEdit.CopyRange( m_Clipboard, 0, iRowsToCopy, iDestFirstRow ); } break; - case clear: - { - ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); - m_NoteDataEdit.ClearRange( m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); - } - break; - case quantize: - { - NoteType nt = (NoteType)iAnswers[c]; - NoteDataUtil::SnapToNearestNoteType( m_NoteDataEdit, nt, nt, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); - } - break; - case turn: - { - const NoteData OldClipboard( m_Clipboard ); - HandleAreaMenuChoice( cut ); - - StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; - TurnType tt = (TurnType)iAnswers[c]; - switch( tt ) - { - DEFAULT_FAIL( tt ); - case left: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::left ); break; - case right: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::right ); break; - case mirror: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::mirror ); break; - case shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::shuffle ); break; - case super_shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::super_shuffle ); break; - } - - HandleAreaMenuChoice( paste_at_begin_marker ); - m_Clipboard = OldClipboard; - } - break; - case transform: - { - int iBeginRow = m_NoteFieldEdit.m_iBeginMarker; - int iEndRow = m_NoteFieldEdit.m_iEndMarker; - TransformType tt = (TransformType)iAnswers[c]; - StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; - - switch( tt ) - { - DEFAULT_FAIL( tt ); - case noholds: NoteDataUtil::RemoveHoldNotes( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nomines: NoteDataUtil::RemoveMines( m_NoteDataEdit, iBeginRow, iBeginRow ); break; - case little: NoteDataUtil::Little( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case wide: NoteDataUtil::Wide( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case big: NoteDataUtil::Big( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case quick: NoteDataUtil::Quick( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case skippy: NoteDataUtil::Skippy( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case add_mines: NoteDataUtil::AddMines( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case echo: NoteDataUtil::Echo( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case stomp: NoteDataUtil::Stomp( m_NoteDataEdit, st, iBeginRow, iEndRow ); break; - case planted: NoteDataUtil::Planted( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case floored: NoteDataUtil::Floored( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case twister: NoteDataUtil::Twister( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nojumps: NoteDataUtil::RemoveJumps( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nohands: NoteDataUtil::RemoveHands( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case noquads: NoteDataUtil::RemoveQuads( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nostretch: NoteDataUtil::RemoveStretch( m_NoteDataEdit, st, iBeginRow, iBeginRow );break; - } - - // bake in the additions - NoteDataUtil::ConvertAdditionsToRegular( m_NoteDataEdit ); - } - break; - case alter: - { - const NoteData OldClipboard( m_Clipboard ); - HandleAreaMenuChoice( cut ); - - AlterType at = (AlterType)iAnswers[c]; - switch( at ) - { - DEFAULT_FAIL( at ); - case autogen_to_fill_width: - { - NoteData temp( m_Clipboard ); - int iMaxNonEmptyTrack = NoteDataUtil::GetMaxNonEmptyTrack( temp ); - if( iMaxNonEmptyTrack == -1 ) - break; - temp.SetNumTracks( iMaxNonEmptyTrack+1 ); - NoteDataUtil::LoadTransformedSlidingWindow( temp, m_Clipboard, m_Clipboard.GetNumTracks() ); - NoteDataUtil::RemoveStretch( m_Clipboard, GAMESTATE->m_pCurSteps[0]->m_StepsType ); - } - break; - case backwards: NoteDataUtil::Backwards( m_Clipboard ); break; - case swap_sides: NoteDataUtil::SwapSides( m_Clipboard ); break; - case copy_left_to_right: NoteDataUtil::CopyLeftToRight( m_Clipboard ); break; - case copy_right_to_left: NoteDataUtil::CopyRightToLeft( m_Clipboard ); break; - case clear_left: NoteDataUtil::ClearLeft( m_Clipboard ); break; - case clear_right: NoteDataUtil::ClearRight( m_Clipboard ); break; - case collapse_to_one: NoteDataUtil::CollapseToOne( m_Clipboard ); break; - case collapse_left: NoteDataUtil::CollapseLeft( m_Clipboard ); break; - case shift_left: NoteDataUtil::ShiftLeft( m_Clipboard ); break; - case shift_right: NoteDataUtil::ShiftRight( m_Clipboard ); break; - } - - HandleAreaMenuChoice( paste_at_begin_marker ); - m_Clipboard = OldClipboard; - } - break; - case tempo: - { - // This affects all steps. - AlterType at = (AlterType)iAnswers[c]; - float fScale = -1; - - switch( at ) - { - DEFAULT_FAIL( at ); - case compress_2x: fScale = 0.5f; break; - case compress_3_2: fScale = 2.0f/3; break; - case compress_4_3: fScale = 0.75f; break; - case expand_4_3: fScale = 4.0f/3; break; - case expand_3_2: fScale = 1.5f; break; - case expand_2x: fScale = 2; break; - } - - int iStartIndex = m_NoteFieldEdit.m_iBeginMarker; - int iEndIndex = m_NoteFieldEdit.m_iEndMarker; - int iNewEndIndex = iEndIndex + lrintf( (iEndIndex - iStartIndex) * (fScale - 1) ); - - // scale currently editing notes - NoteDataUtil::ScaleRegion( m_NoteDataEdit, fScale, iStartIndex, iEndIndex ); - - // scale timing data - GetAppropriateTiming().ScaleRegion( fScale, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker, true ); - - // scale all other steps. - /* - const vector sIter = m_pSong->GetAllSteps(); - RString sTempStyle, sTempDiff; - for( unsigned i = 0; i < sIter.size(); i++ ) - { - if( sIter[i]->IsAutogen() ) - continue; - - // XXX: Edits are distinguished by description. - // Compare vs m_pSteps. - if( (sIter[i]->m_StepsType == GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType) && - (sIter[i]->GetDifficulty() == GAMESTATE->m_pCurSteps[PLAYER_1]->GetDifficulty()) ) - continue; - - NoteData ndTemp; - sIter[i]->GetNoteData( ndTemp ); - NoteDataUtil::ScaleRegion( ndTemp, fScale, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); - sIter[i]->SetNoteData( ndTemp ); - } - */ - m_NoteFieldEdit.m_iEndMarker = iNewEndIndex; - - } - break; - case play: - ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); - m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; - m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; - TransitionEditState( STATE_PLAYING ); - break; - case record: - ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ); - m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; - m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; - TransitionEditState( STATE_RECORDING ); - break; case insert_and_shift: NoteDataUtil::InsertRows( m_NoteDataEdit, BeatToNoteRow( GetBeat() ), BeatToNoteRow(1) ); break; @@ -3714,29 +3824,7 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns case shift_pauses_backward: GetAppropriateTiming().DeleteRows( GetRow() + 1, BeatToNoteRow(1) ); break; - case convert_to_pause: - { - ASSERT_M( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1, "Attempted to convert beats outside the notefield to pauses!" ); - float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); - float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); - // The length of the stop segment we're going to create. This includes time spent in any - // stops in the selection, which will be deleted and subsumed into the new stop. - float fStopLength = fMarkerEnd - fMarkerStart; - - // be sure not to clobber the row at the start - a row at the end - // can be dropped safely, though - NoteDataUtil::DeleteRows( m_NoteDataEdit, - m_NoteFieldEdit.m_iBeginMarker + 1, - m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker - ); - GetAppropriateTiming().DeleteRows( m_NoteFieldEdit.m_iBeginMarker + 1, - m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker ); - GetAppropriateTiming().SetStopAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength ); - m_NoteFieldEdit.m_iBeginMarker = -1; - m_NoteFieldEdit.m_iEndMarker = -1; - break; - } case convert_pause_to_beat: { // TODO: Convert both Delays and Stops at once. @@ -3753,6 +3841,11 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns case undo: Undo(); break; + case clear_clipboard: + { + m_Clipboard.ClearAll(); + break; + } }; if( bSaveUndo ) diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 2b92f8bbd5..f3e4a912e5 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -87,6 +87,7 @@ enum EditButton EDIT_BUTTON_OPEN_EDIT_MENU, EDIT_BUTTON_OPEN_TIMING_MENU, + EDIT_BUTTON_OPEN_ALTER_MENU, EDIT_BUTTON_OPEN_AREA_MENU, EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU, EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU, @@ -209,7 +210,12 @@ public: virtual void PushSelf( lua_State *L ); protected: - virtual ScreenType GetScreenType() const { return m_EditState==STATE_PLAYING ? gameplay : ScreenWithMenuElements::GetScreenType(); } + virtual ScreenType GetScreenType() const + { + return m_EditState==STATE_PLAYING ? + gameplay : + ScreenWithMenuElements::GetScreenType(); + } void TransitionEditState( EditState em ); void ScrollTo( float fDestinationBeat ); @@ -268,7 +274,8 @@ protected: BitmapText m_textPlayRecordHelp; // keep track of where we are and what we're doing - float m_fTrailingBeat; // this approaches GAMESTATE->m_fSongBeat, which is the actual beat + float m_fTrailingBeat; + // the above approaches GAMESTATE->m_fSongBeat, which is the actual beat /** * @brief The location we were at when shift was pressed. * @@ -365,12 +372,10 @@ public: void HandleMainMenuChoice( MainMenuChoice c ) { const vector v; HandleMainMenuChoice( c, v ); } MainMenuChoice m_CurrentAction; - enum AreaMenuChoice + enum AlterMenuChoice { cut, copy, - paste_at_current_beat, - paste_at_begin_marker, clear, quantize, turn, @@ -379,17 +384,41 @@ public: tempo, play, record, + convert_to_pause, + convert_to_delay, + convert_to_warp, + convert_to_fake, + NUM_ALTER_MENU_CHOICES + + }; + + enum AreaMenuChoice + { + paste_at_current_beat, + paste_at_begin_marker, insert_and_shift, delete_and_shift, shift_pauses_forward, shift_pauses_backward, - convert_to_pause, convert_pause_to_beat, undo, + clear_clipboard, NUM_AREA_MENU_CHOICES }; + void HandleAlterMenuChoice(AlterMenuChoice c, + const vector &iAnswers, + bool bAllowUndo = true); + void HandleAlterMenuChoice(AlterMenuChoice c, + bool bAllowUndo = true) + { + const vector v; HandleAlterMenuChoice(c, v, bAllowUndo); + } + void HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAnswers, bool bAllowUndo = true ); - void HandleAreaMenuChoice( AreaMenuChoice c, bool bAllowUndo = true ) { const vector v; HandleAreaMenuChoice( c, v, bAllowUndo ); } + void HandleAreaMenuChoice( AreaMenuChoice c, bool bAllowUndo = true ) + { + const vector v; HandleAreaMenuChoice( c, v, bAllowUndo ); + } /** @brief How should the selected notes be transformed? */ enum TurnType { @@ -511,7 +540,8 @@ public: NUM_TIMING_DATA_INFORMATION_CHOICES }; - void HandleTimingDataInformationChoice ( TimingDataInformationChoice c, const vector &iAnswers ); + void HandleTimingDataInformationChoice (TimingDataInformationChoice c, + const vector &iAnswers ); enum BGChangeChoice { @@ -581,7 +611,9 @@ public: MapEditButtonToMenuButton m_RecordPausedMappingsMenuButton; void MakeFilteredMenuDef( const MenuDef* pDef, MenuDef &menu ); - void EditMiniMenu( const MenuDef* pDef, ScreenMessage SM_SendOnOK = SM_None, ScreenMessage SM_SendOnCancel = SM_None ); + void EditMiniMenu(const MenuDef* pDef, + ScreenMessage SM_SendOnOK = SM_None, + ScreenMessage SM_SendOnCancel = SM_None ); private: /** * @brief Retrieve the appropriate TimingData based on GAMESTATE. diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index c4a6ccaeca..a0eb87b581 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -2294,12 +2294,14 @@ void ScreenGameplay::SaveStats() RadarValues rv; PlayerStageStats &pss = *pi->GetPlayerStageStats(); const NoteData &nd = pi->m_pPlayer->GetNoteData(); + PlayerNumber pn = pi->m_pn; + GAMESTATE->SetProcessedTimingData(&GAMESTATE->m_pCurSteps[pn]->m_Timing); NoteDataUtil::CalculateRadarValues( nd, fMusicLen, rv ); pss.m_radarPossible += rv; - NoteDataWithScoring::GetActualRadarValues( nd, pss, fMusicLen, rv ); pss.m_radarActual += rv; + GAMESTATE->SetProcessedTimingData(NULL); } } diff --git a/src/ScreenGameplaySyncMachine.cpp b/src/ScreenGameplaySyncMachine.cpp index 325b3a2e87..3f72cd1976 100644 --- a/src/ScreenGameplaySyncMachine.cpp +++ b/src/ScreenGameplaySyncMachine.cpp @@ -21,10 +21,12 @@ void ScreenGameplaySyncMachine::Init() RString sFile = THEME->GetPathO("ScreenGameplaySyncMachine","music"); // Allow themers to use either a .ssc or .sm file for this. -aj + SSCLoader loaderSSC; + SMLoader loaderSM; if(sFile.Right(4) == ".ssc") - SSCLoader::LoadFromSSCFile( sFile, m_Song ); + loaderSSC.LoadFromSimfile( sFile, m_Song ); else - SMLoader::LoadFromSMFile( sFile, m_Song ); + loaderSM.LoadFromSimfile( sFile, m_Song ); m_Song.SetSongDir( Dirname(sFile) ); m_Song.TidyUpData(); diff --git a/src/ScreenHowToPlay.cpp b/src/ScreenHowToPlay.cpp index b38848bb7d..bb5e021db2 100644 --- a/src/ScreenHowToPlay.cpp +++ b/src/ScreenHowToPlay.cpp @@ -135,10 +135,12 @@ void ScreenHowToPlay::Init() // Allow themers to use either a .ssc or .sm file for this. -aj RString sStepsPath = THEME->GetPathO(m_sName, "steps"); + SSCLoader loaderSSC; + SMLoader loaderSM; if( sStepsPath.Right(4) == ".ssc" ) - SSCLoader::LoadFromSSCFile( sStepsPath, m_Song, false ); + loaderSSC.LoadFromSimfile( sStepsPath, m_Song, false ); else - SMLoader::LoadFromSMFile( sStepsPath, m_Song, false ); + loaderSM.LoadFromSimfile( sStepsPath, m_Song, false ); m_Song.AddAutoGenNotes(); const Style* pStyle = GAMESTATE->GetCurrentStyle(); diff --git a/src/ScreenOptionsEditCourse.cpp b/src/ScreenOptionsEditCourse.cpp index 6f5c6aa111..020f328c1d 100644 --- a/src/ScreenOptionsEditCourse.cpp +++ b/src/ScreenOptionsEditCourse.cpp @@ -145,7 +145,7 @@ void ScreenOptionsEditCourse::Init() sc.m_Tutorial = SongCriteria::Tutorial_No; sc.m_Locked = SongCriteria::Locked_Unlocked; - SongUtil::FilterSongs( sc, SONGMAN->GetAllSongs(), m_vpSongs ); + SongUtil::FilterSongs( sc, SONGMAN->GetAllSongs(), m_vpSongs, true ); SongUtil::SortSongPointerArrayByTitle( m_vpSongs ); } diff --git a/src/ScreenPackagesSSC.cpp b/src/ScreenPackagesSSC.cpp index 48d292612e..73ba2c57ed 100644 --- a/src/ScreenPackagesSSC.cpp +++ b/src/ScreenPackagesSSC.cpp @@ -1,4 +1,6 @@ #include "global.h" + +#if !defined(WITHOUT_NETWORKING) #include "ScreenPackagesSSC.h" #include "ThemeManager.h" #include "RageDisplay.h" @@ -50,6 +52,7 @@ RString ScreenPackagesSSC::JSONParse( const RString &string_in ) return ""; } +#endif /* * (c) 2009 Colby Klein * All rights reserved. diff --git a/src/ScreenPackagesSSC.h b/src/ScreenPackagesSSC.h index c9de431c7b..7db51bfef6 100644 --- a/src/ScreenPackagesSSC.h +++ b/src/ScreenPackagesSSC.h @@ -7,6 +7,8 @@ #include "RageFileManager.h" #include "RageFile.h" #include "Sprite.h" + +#if !defined(WITHOUT_NETWORKING) /** @brief Parses JSON for downloadable content. */ class ScreenPackagesSSC : public ScreenWithMenuElements { @@ -24,6 +26,7 @@ public: private: RString JSONParse( const RString &string_in ); }; +#endif // WITHOUT_NETWORKING? #endif diff --git a/src/ScreenServiceAction.cpp b/src/ScreenServiceAction.cpp index 13e1222260..c28086a72c 100644 --- a/src/ScreenServiceAction.cpp +++ b/src/ScreenServiceAction.cpp @@ -197,7 +197,8 @@ static void CopyEdits( const RString &sFromProfileDir, const RString &sToProfile iNumErrored++; // Test whether the song we need for this edit is present and ignore this edit if not present. - if( !SSCLoader::LoadEditFromFile( sFromDir+*i, ProfileSlot_Machine, false ) ) + SSCLoader loaderSSC; + if( !loaderSSC.LoadEditFromFile( sFromDir+*i, ProfileSlot_Machine, false ) ) { iNumIgnored++; continue; diff --git a/src/ScreenSyncOverlay.cpp b/src/ScreenSyncOverlay.cpp index 3596c91e70..e91cf6569d 100644 --- a/src/ScreenSyncOverlay.cpp +++ b/src/ScreenSyncOverlay.cpp @@ -253,7 +253,14 @@ bool ScreenSyncOverlay::OverlayInput( const InputEventPlus &input ) case ChangeSongOffset: if( GAMESTATE->m_pCurSong != NULL ) + { GAMESTATE->m_pCurSong->m_SongTiming.m_fBeat0OffsetInSeconds += fDelta; + const vector& vpSteps = GAMESTATE->m_pCurSong->GetAllSteps(); + FOREACH( Steps*, const_cast&>(vpSteps), s ) + { + (*s)->m_Timing.m_fBeat0OffsetInSeconds += fDelta; + } + } break; } } diff --git a/src/Song.cpp b/src/Song.cpp index 62696a93e2..8cf6d352a7 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -41,7 +41,7 @@ * @brief The internal version of the cache for StepMania. * * Increment this value to invalidate the current cache. */ -const int FILE_CACHE_VERSION = 179; +const int FILE_CACHE_VERSION = 180; /** @brief How long does a song sample last by default? */ const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; @@ -235,12 +235,14 @@ bool Song::LoadFromSongDir( RString sDir ) if( bUseCache ) { // LOG->Trace( "Loading '%s' from cache file '%s'.", m_sSongDir.c_str(), GetCacheFilePath().c_str() ); - bool bLoadedFromSSC = SSCLoader::LoadFromSSCFile( sCacheFilePath, *this, true ); + SSCLoader loaderSSC; + bool bLoadedFromSSC = loaderSSC.LoadFromSimfile( sCacheFilePath, *this, true ); if( !bLoadedFromSSC ) { // load from .sm - SMLoader::LoadFromSMFile( sCacheFilePath, *this, true ); - SMLoader::TidyUpData( *this, true ); + SMLoader loaderSM; + loaderSM.LoadFromSimfile( sCacheFilePath, *this, true ); + loaderSM.TidyUpData( *this, true ); } } else @@ -1502,9 +1504,10 @@ public: static int GetBackgroundPath( T* p, lua_State *L ) { RString s = p->GetBackgroundPath(); - if( s.empty() ) - s = ""; - lua_pushstring(L, s); + if( !s.empty() ) + lua_pushstring(L, s); + else + lua_pushnil(L); return 1; } static int GetCDTitlePath( T* p, lua_State *L ) { RString s = p->GetCDTitlePath(); if( s.empty() ) return 0; LuaHelpers::Push(L, s); return 1; } diff --git a/src/SongManager.cpp b/src/SongManager.cpp index 804bc2aa5b..56afe8f93e 100644 --- a/src/SongManager.cpp +++ b/src/SongManager.cpp @@ -1692,12 +1692,15 @@ void SongManager::LoadStepEditsFromProfileDir( const RString &sProfileDir, Profi for( int i=0; i &vsOut ) } } -void SongUtil::FilterSongs( const SongCriteria &sc, const vector &in, vector &out ) +void SongUtil::FilterSongs( const SongCriteria &sc, const vector &in, + vector &out, bool doCareAboutGame ) { out.reserve( in.size() ); FOREACH_CONST( Song*, in, s ) { - if( sc.Matches( *s ) ) + if( sc.Matches( *s ) && (!doCareAboutGame || IsSongPlayable(*s) ) ) { + out.push_back( *s ); } } @@ -963,6 +965,21 @@ bool SongUtil::IsStepsPlayable( Song *pSong, Steps *pSteps ) return find( vpSteps.begin(), vpSteps.end(), pSteps ) != vpSteps.end(); } +bool SongUtil::IsSongPlayable( Song *s ) +{ + const vector & steps = s->GetAllSteps(); + // I'm sure there is a foreach loop, but I don't + FOREACH( Steps*, const_cast&>(steps), step ) + { + if (IsStepsPlayable(s, *step)) + { + return true; + } + } + + return false; +} + bool SongUtil::GetStepsTypeAndDifficultyFromSortOrder( SortOrder so, StepsType &stOut, Difficulty &dcOut ) { switch( so ) diff --git a/src/SongUtil.h b/src/SongUtil.h index 0c0a18043d..5432d455e3 100644 --- a/src/SongUtil.h +++ b/src/SongUtil.h @@ -163,12 +163,25 @@ namespace SongUtil bool ValidateCurrentStepsCredit( const RString &sAnswer, RString &sErrorOut ); void GetAllSongGenres( vector &vsOut ); - void FilterSongs( const SongCriteria &sc, const vector &in, vector &out ); + /** + * @brief Filter the selection of songs to only match certain criteria. + * @param sc the intended song criteria. + * @param in the starting batch of songs. + * @param out the resulting batch. + * @param doCareAboutGame a flag to see if we should only get playable steps. */ + void FilterSongs( const SongCriteria &sc, const vector &in, vector &out, + bool doCareAboutGame = false ); void GetPlayableStepsTypes( const Song *pSong, set &vOut ); void GetPlayableSteps( const Song *pSong, vector &vOut ); bool IsStepsTypePlayable( Song *pSong, StepsType st ); bool IsStepsPlayable( Song *pSong, Steps *pSteps ); + + /** + * @brief Determine if the song has any playable steps in the present game. + * @param s the current song. + * @return true if the song has playable steps, false otherwise. */ + bool IsSongPlayable( Song *s ); bool GetStepsTypeAndDifficultyFromSortOrder( SortOrder so, StepsType &st, Difficulty &dc ); } diff --git a/src/Steps.cpp b/src/Steps.cpp index c1a79064f5..ee7a27c98f 100644 --- a/src/Steps.cpp +++ b/src/Steps.cpp @@ -14,6 +14,7 @@ #include "global.h" #include "Steps.h" #include "StepsUtil.h" +#include "GameState.h" #include "Song.h" #include "RageUtil.h" #include "RageLog.h" @@ -192,6 +193,7 @@ void Steps::CalculateRadarValues( float fMusicLengthSeconds ) FOREACH_PlayerNumber( pn ) m_CachedRadarValues[pn].Zero(); + GAMESTATE->SetProcessedTimingData(&this->m_Timing); if( tempNoteData.IsComposite() ) { vector vParts; @@ -205,6 +207,7 @@ void Steps::CalculateRadarValues( float fMusicLengthSeconds ) NoteDataUtil::CalculateRadarValues( tempNoteData, fMusicLengthSeconds, m_CachedRadarValues[0] ); fill_n( m_CachedRadarValues + 1, NUM_PLAYERS-1, m_CachedRadarValues[0] ); } + GAMESTATE->SetProcessedTimingData(NULL); } void Steps::Decompress() const @@ -239,11 +242,13 @@ void Steps::Decompress() const { // We have data on disk and not in memory. Load it. Song s; - bool bLoadedFromSSC = SSCLoader::LoadFromSSCFile(m_sFilename, s, true); + SSCLoader loaderSSC; + bool bLoadedFromSSC = loaderSSC.LoadFromSimfile(m_sFilename, s, true); if( !bLoadedFromSSC ) { // try reading from .sm instead - if( !SMLoader::LoadFromSMFile(m_sFilename, s, true) ) + SMLoader loaderSM; + if( !loaderSM.LoadFromSimfile(m_sFilename, s, true) ) { LOG->Warn( "Couldn't load \"%s\"", m_sFilename.c_str() ); return; diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 1e57e455ac..f65c00122e 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -1746,7 +1746,7 @@ public: LuaHelpers::CreateTableFromArray(fBPMs, L); return 1; } - static int HasNegativeBPMs( T* p, lua_State *L ) { lua_pushboolean(L, p->m_bHasNegativeBpms); return 1; } + static int HasNegativeBPMs( T* p, lua_State *L ) { lua_pushboolean(L, p->HasWarps()); return 1; } // formerly in Song.cpp in sm-ssc private beta 1.x: static int GetBPMAtBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBPMAtBeat(FArg(1))); return 1; } static int GetBeatFromElapsedTime( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBeatFromElapsedTime(FArg(1))); return 1; } diff --git a/src/TimingData.h b/src/TimingData.h index e81fc078e7..687016ea5b 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -845,6 +845,20 @@ public: */ void AddFakeSegment( const FakeSegment &seg ); + /** + * @brief Determine if this notes on this row can be judged. + * @param row the row to focus on. + * @return true if the row can be judged, false otherwise. */ + bool IsJudgableAtRow( int row ) const + { + return !(IsWarpAtRow(row) || IsFakeAtRow(row)); + } + /** + * @brief Determine if this notes on this beat can be judged. + * @param beat the beat to focus on. + * @return true if the row can be judged, false otherwise. */ + bool IsJudgableAtBeat( float beat ) const { return IsJudgableAtRow( BeatToNoteRow( beat ) ); } + void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor ); diff --git a/src/Trail.cpp b/src/Trail.cpp index 073d266c93..25ece2abd3 100644 --- a/src/Trail.cpp +++ b/src/Trail.cpp @@ -1,6 +1,7 @@ #include "global.h" #include "Trail.h" #include "Foreach.h" +#include "GameState.h" #include "Steps.h" #include "Song.h" #include "PlayerOptions.h" @@ -116,6 +117,7 @@ const RadarValues &Trail::GetRadarValues() const NoteData nd; pSteps->GetNoteData( nd ); RadarValues rv_orig; + GAMESTATE->SetProcessedTimingData(const_cast(&pSteps->m_Timing)); NoteDataUtil::CalculateRadarValues( nd, e->pSong->m_fMusicLengthSeconds, rv_orig ); PlayerOptions po; po.FromString( e->Modifiers ); @@ -124,6 +126,7 @@ const RadarValues &Trail::GetRadarValues() const NoteDataUtil::TransformNoteData( nd, e->Attacks, pSteps->m_StepsType, e->pSong ); RadarValues transformed_rv; NoteDataUtil::CalculateRadarValues( nd, e->pSong->m_fMusicLengthSeconds, transformed_rv ); + GAMESTATE->SetProcessedTimingData(NULL); rv += transformed_rv; } else