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