Mission accomplished: [warps] are in default.
Let the fun times roll!
This commit is contained in:
@@ -72,6 +72,7 @@ Data/SpeedMods.txt
|
||||
Program/StepMania*.lib
|
||||
crashinfo.txt
|
||||
Utils/temp*
|
||||
*.lnk
|
||||
|
||||
syntax: regexp
|
||||
.*\#.*\#$
|
||||
|
||||
@@ -9,6 +9,9 @@ change to JSON, but it is unsure if this will be done.
|
||||
Implement .ssc at your own risk.
|
||||
________________________________________________________________________________
|
||||
|
||||
[v0.66] - Wolfman2000
|
||||
* Implement #WARPS tag to replace negative bpm/stop gimmicks.
|
||||
|
||||
[v0.55] - Wolfman2000
|
||||
* Add #COMBOS tag to the Song and Steps (does nothing here).
|
||||
|
||||
|
||||
@@ -13,6 +13,13 @@ _____________________________________________________________________________
|
||||
sm-ssc v1.2.4 | 2011032?
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
20110326
|
||||
--------
|
||||
* [NotesLoaderSM, NotesWriterSSC] Officially add the #WARPS tag. The negative
|
||||
tricks of the past are now officially standardized. Please tell us about
|
||||
files which have broken sync or are otherwise unplayable/incorrect.
|
||||
[AJ, Thai, Wolfman2000]
|
||||
|
||||
20110320
|
||||
--------
|
||||
* [PlayerOptions] Added SetNoteSkin(string) Lua binding. [AJ]
|
||||
|
||||
@@ -843,6 +843,7 @@ Edit time signature (top)=Edit time signature (beats per measure)
|
||||
Edit time signature (bottom)=Edit time signature (single beat note value)
|
||||
Edit tickcount=Edit tickcount
|
||||
Edit combo=Edit combo
|
||||
Edit warp=Edit warp
|
||||
Editor options=Options
|
||||
EditorShowBGChangesPlay=Show Backgrounds
|
||||
Effect=Effect
|
||||
@@ -1209,6 +1210,7 @@ Enter a new Time Signature numerator value.=Enter the number of beats per measur
|
||||
Enter a new Time Signature denominator value.=Enter the note value that represents one beat.
|
||||
Enter a new Tickcount value.=Enter a new Tickcount value.
|
||||
Enter a new Combo value.=Enter a new Combo value.
|
||||
Enter a new Warp value.=Enter the beat you will warp to when you reach this point.
|
||||
Enter a new artist transliteration.=Enter a new artist transliteration.
|
||||
Enter a new artist.=Enter a new artist.
|
||||
Enter a new genre.=Enter a new genre.
|
||||
|
||||
@@ -963,6 +963,7 @@ RoutineNoteSkinP2=RoutineSkinP2()
|
||||
BPMColor=color("1,0,0,1")
|
||||
StopColor=color("0.8,0.8,0,1")
|
||||
DelayColor=color("0,0.8,0.8,1")
|
||||
WarpColor=color("1,0,0.5,1")
|
||||
TimeSignatureColor=color("1,0.55,0,1")
|
||||
TickcountColor=color("0,1,0,1")
|
||||
ComboColor=color("0.55,1,0,1")
|
||||
@@ -970,13 +971,15 @@ ComboColor=color("0.55,1,0,1")
|
||||
BPMIsLeftSide=true
|
||||
StopIsLeftSide=true
|
||||
DelayIsLeftSide=true
|
||||
WarpIsLeftSide=false
|
||||
TimeSignatureIsLeftSide=true
|
||||
TickcountIsLeftSide=false
|
||||
ComboIsLeftSide=false
|
||||
#
|
||||
BPMOffsetX=60
|
||||
StopOffsetX=10
|
||||
DelayOffsetX=10
|
||||
StopOffsetX=50
|
||||
DelayOffsetX=120
|
||||
WarpOffsetX=60
|
||||
TimeSignatureOffsetX=30
|
||||
TickcountOffsetX=10
|
||||
ComboOffsetX=30
|
||||
|
||||
+3
-11
@@ -897,7 +897,7 @@ void GameState::ResetMusicStatistics()
|
||||
m_bFreeze = false;
|
||||
m_bDelay = false;
|
||||
m_iWarpBeginRow = -1; // Set to -1 because some song may want to warp to row 0. -aj
|
||||
m_fWarpLength = -1; // Set when a warp is encountered. also see above. -aj
|
||||
m_fWarpDestination = -1; // Set when a warp is encountered. also see above. -aj
|
||||
m_fMusicSecondsVisible = 0;
|
||||
m_fSongBeatVisible = 0;
|
||||
Actor::SetBGMTime( 0, 0, 0, 0 );
|
||||
@@ -943,7 +943,7 @@ void GameState::ResetStageStatistics()
|
||||
}
|
||||
|
||||
static Preference<float> g_fVisualDelaySeconds( "VisualDelaySeconds", 0.0f );
|
||||
// todo: modify for warps -aj
|
||||
|
||||
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp )
|
||||
{
|
||||
if( !timestamp.IsZero() )
|
||||
@@ -957,18 +957,10 @@ void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &ti
|
||||
LOG->Trace( ssprintf("[GameState::UpdateSongPosition] cur BPS = %f, fPositionSeconds = %f",m_fCurBPS,fPositionSeconds) );
|
||||
*/
|
||||
|
||||
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay, m_iWarpBeginRow, m_fWarpLength );
|
||||
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay, m_iWarpBeginRow, m_fWarpDestination );
|
||||
// "Crash reason : -243478.890625 -48695.773438"
|
||||
ASSERT_M( m_fSongBeat > -2000, ssprintf("Song beat %f at %f seconds", m_fSongBeat, fPositionSeconds) );
|
||||
|
||||
//if( m_iWarpBeginRow != -1 || m_iWarpEndRow == -1 )
|
||||
if( m_iWarpBeginRow != -1 && m_fWarpLength > 0.0f )
|
||||
{
|
||||
// There is a warp in this section.
|
||||
LOG->Trace("warp at %i lasts for %f, jumps to %i",m_iWarpBeginRow,m_fWarpLength,m_iWarpBeginRow+BeatToNoteRow(m_fWarpLength));
|
||||
//fPositionSeconds += (m_fWarpLength * m_fCurBPS);
|
||||
}
|
||||
|
||||
m_fMusicSeconds = fPositionSeconds;
|
||||
|
||||
m_fLightSongBeat = timing.GetBeatFromElapsedTime( fPositionSeconds + g_fLightsAheadSeconds );
|
||||
|
||||
+3
-2
@@ -208,9 +208,10 @@ public:
|
||||
bool m_bFreeze;
|
||||
/** @brief A flag to determine if we're in the middle of a delay (Pump style stop). */
|
||||
bool m_bDelay;
|
||||
// used for warping:
|
||||
/** @brief The row used to start a warp. */
|
||||
int m_iWarpBeginRow;
|
||||
float m_fWarpLength;
|
||||
/** @brief The beat to warp to afterwards. */
|
||||
float m_fWarpDestination;
|
||||
RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
|
||||
BroadcastOnChange<bool> m_bGameplayLeadIn;
|
||||
|
||||
|
||||
+6
-6
@@ -864,8 +864,8 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri
|
||||
FOREACH_CONST( Command, cmds.v, c )
|
||||
{
|
||||
const Command& cmd = (*c);
|
||||
RString sName = cmd.GetName();
|
||||
s << "\tself:" << sName << "(";
|
||||
RString local_sName = cmd.GetName();
|
||||
s << "\tself:" << local_sName << "(";
|
||||
|
||||
for( unsigned i=1; i<cmd.m_vsArgs.size(); i++ )
|
||||
{
|
||||
@@ -877,10 +877,10 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri
|
||||
|
||||
if( sArg[0] == '#' ) // HTML color
|
||||
{
|
||||
RageColor c; // in case FromString fails
|
||||
c.FromString( sArg );
|
||||
// c is still valid if FromString fails
|
||||
s << c.r << "," << c.g << "," << c.b << "," << c.a;
|
||||
RageColor col; // in case FromString fails
|
||||
col.FromString( sArg );
|
||||
// col is still valid if FromString fails
|
||||
s << col.r << "," << col.g << "," << col.b << "," << col.a;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+33
-2
@@ -422,18 +422,21 @@ void NoteField::DrawAreaHighlight( int iStartBeat, int iEndBeat )
|
||||
static ThemeMetric<RageColor> BPM_COLOR ( "NoteField", "BPMColor" );
|
||||
static ThemeMetric<RageColor> STOP_COLOR ( "NoteField", "StopColor" );
|
||||
static ThemeMetric<RageColor> DELAY_COLOR ( "NoteField", "DelayColor" );
|
||||
static ThemeMetric<RageColor> WARP_COLOR ( "NoteField", "WarpColor" );
|
||||
static ThemeMetric<RageColor> TIME_SIGNATURE_COLOR ( "NoteField", "TimeSignatureColor" );
|
||||
static ThemeMetric<RageColor> TICKCOUNT_COLOR ( "NoteField", "TickcountColor" );
|
||||
static ThemeMetric<RageColor> COMBO_COLOR ( "NoteField", "ComboColor" );
|
||||
static ThemeMetric<bool> BPM_IS_LEFT_SIDE ( "NoteField", "BPMIsLeftSide" );
|
||||
static ThemeMetric<bool> STOP_IS_LEFT_SIDE ( "NoteField", "StopIsLeftSide" );
|
||||
static ThemeMetric<bool> DELAY_IS_LEFT_SIDE ( "NoteField", "DelayIsLeftSide" );
|
||||
static ThemeMetric<bool> WARP_IS_LEFT_SIDE ( "NoteField", "WarpIsLeftSide" );
|
||||
static ThemeMetric<bool> TIME_SIGNATURE_IS_LEFT_SIDE ( "NoteField", "TimeSignatureIsLeftSide" );
|
||||
static ThemeMetric<bool> TICKCOUNT_IS_LEFT_SIDE ( "NoteField", "TickcountIsLeftSide" );
|
||||
static ThemeMetric<bool> COMBO_IS_LEFT_SIDE ( "NoteField", "ComboIsLeftSide" );
|
||||
static ThemeMetric<float> BPM_OFFSETX ( "NoteField", "BPMOffsetX" );
|
||||
static ThemeMetric<float> STOP_OFFSETX ( "NoteField", "StopOffsetX" );
|
||||
static ThemeMetric<float> DELAY_OFFSETX ( "NoteField", "DelayOffsetX" );
|
||||
static ThemeMetric<float> WARP_OFFSETX ( "NoteField", "WarpOffsetX" );
|
||||
static ThemeMetric<float> TIME_SIGNATURE_OFFSETX ( "NoteField", "TimeSignatureOffsetX" );
|
||||
static ThemeMetric<float> TICKCOUNT_OFFSETX ( "NoteField", "TickcountOffsetX" );
|
||||
static ThemeMetric<float> COMBO_OFFSETX ( "NoteField", "ComboOffsetX" );
|
||||
@@ -481,6 +484,23 @@ void NoteField::DrawFreezeText( const float fBeat, const float fSecs, const floa
|
||||
m_textMeasureNumber.Draw();
|
||||
}
|
||||
|
||||
void NoteField::DrawWarpText( const float fBeat, const float fNewBeat )
|
||||
{
|
||||
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
|
||||
const float fYPos = ArrowEffects::GetYPos( m_pPlayerState, 0, fYOffset, m_fYReverseOffsetPixels );
|
||||
const float fZoom = ArrowEffects::GetZoom( m_pPlayerState );
|
||||
const float xBase = GetWidth()/2.f;
|
||||
const float xOffset = WARP_OFFSETX * fZoom;
|
||||
|
||||
m_textMeasureNumber.SetZoom( fZoom );
|
||||
m_textMeasureNumber.SetHorizAlign( WARP_IS_LEFT_SIDE ? align_right : align_left );
|
||||
m_textMeasureNumber.SetDiffuse( WARP_COLOR );
|
||||
m_textMeasureNumber.SetGlow( RageColor(1,1,1,RageFastCos(RageTimer::GetTimeSinceStartFast()*2)/2+0.5f) );
|
||||
m_textMeasureNumber.SetText( ssprintf("%.3f", fNewBeat) );
|
||||
m_textMeasureNumber.SetXY( (WARP_IS_LEFT_SIDE ? -xBase - xOffset : xBase + xOffset), fYPos );
|
||||
m_textMeasureNumber.Draw();
|
||||
}
|
||||
|
||||
void NoteField::DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator )
|
||||
{
|
||||
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
|
||||
@@ -770,6 +790,19 @@ void NoteField::DrawPrimitives()
|
||||
DrawFreezeText( fBeat, aStopSegments[i].m_fStopSeconds, aStopSegments[i].m_bDelay );
|
||||
}
|
||||
}
|
||||
|
||||
// Warp text
|
||||
const vector<WarpSegment> &aWarpSegments = GAMESTATE->m_pCurSong->m_Timing.m_WarpSegments;
|
||||
for( unsigned i=0; i<aWarpSegments.size(); i++ )
|
||||
{
|
||||
if( aWarpSegments[i].m_iStartRow >= iFirstRowToDraw &&
|
||||
aWarpSegments[i].m_iStartRow <= iLastRowToDraw)
|
||||
{
|
||||
float fBeat = NoteRowToBeat(aWarpSegments[i].m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawWarpText( fBeat, aWarpSegments[i].m_fEndBeat );
|
||||
}
|
||||
}
|
||||
|
||||
// Time Signature text
|
||||
const vector<TimeSignatureSegment> &vTimeSignatureSegments = GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments;
|
||||
@@ -810,8 +843,6 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
}
|
||||
|
||||
// todo: add warp text -aj
|
||||
|
||||
// Course mods text
|
||||
const Course *pCourse = GAMESTATE->m_pCurCourse;
|
||||
if( pCourse )
|
||||
|
||||
@@ -57,6 +57,7 @@ protected:
|
||||
void DrawAreaHighlight( int iStartBeat, int iEndBeat );
|
||||
void DrawBPMText( const float fBeat, const float fBPM );
|
||||
void DrawFreezeText( const float fBeat, const float fBPM, const float bDelay );
|
||||
void DrawWarpText( const float fBeat, const float fNewBeat );
|
||||
void DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator );
|
||||
void DrawTickcountText( const float fBeat, int iTicks );
|
||||
void DrawComboText( const float fBeat, int iCombo );
|
||||
|
||||
+121
-168
@@ -12,7 +12,13 @@
|
||||
#include "Steps.h"
|
||||
#include "PrefsManager.h"
|
||||
|
||||
/** @brief The maximum file size for edits. */
|
||||
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB
|
||||
/**
|
||||
* @brief The highest allowable speed before Warps come in.
|
||||
*
|
||||
* This was brought in from StepMania 4's recent betas. */
|
||||
const float FAST_BPM_WARP = 9999999.f;
|
||||
|
||||
void SMLoader::LoadFromSMTokens(
|
||||
RString sStepsType,
|
||||
@@ -104,9 +110,6 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
out.m_WarpSegments.clear();
|
||||
out.m_vTimeSignatureSegments.clear();
|
||||
|
||||
vector<WarpSegment> arrayWarpsFromNegativeBPMs;
|
||||
//vector<WarpSegment> arrayWarpsFromNegativeStops;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
const MsdFile::value_t &sParams = msd.GetValue(i);
|
||||
@@ -117,11 +120,83 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
{
|
||||
out.m_fBeat0OffsetInSeconds = StringToFloat( sParams[1] );
|
||||
}
|
||||
else if( sValueName=="BPMS" )
|
||||
{
|
||||
vector<RString> arrayBPMChangeExpressions;
|
||||
split( sParams[1], ",", arrayBPMChangeExpressions );
|
||||
|
||||
// prepare storage variables for negative BPMs -> Warps.
|
||||
float negBeat = -1;
|
||||
float negBPM = 1;
|
||||
float highspeedBeat = -1;
|
||||
|
||||
for( unsigned b=0; b<arrayBPMChangeExpressions.size(); b++ )
|
||||
{
|
||||
vector<RString> 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 #%s value \"%s\" (must have exactly one '='), ignored.",
|
||||
sValueName.c_str(), arrayBPMChangeExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( arrayBPMChangeValues[0] );
|
||||
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);
|
||||
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);
|
||||
out.AddWarpSegment( new_seg );
|
||||
highspeedBeat = -1;
|
||||
}
|
||||
{
|
||||
BPMSegment new_seg;
|
||||
new_seg.m_iStartRow = BeatToNoteRow(fBeat);
|
||||
new_seg.SetBPM( fNewBPM );
|
||||
out.AddBPMSegment( new_seg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="STOPS" || sValueName=="FREEZES" )
|
||||
{
|
||||
vector<RString> arrayFreezeExpressions;
|
||||
split( sParams[1], ",", arrayFreezeExpressions );
|
||||
|
||||
// Prepare variables for negative stop conversion.
|
||||
float negBeat = -1;
|
||||
float negPause = 0;
|
||||
|
||||
for( unsigned f=0; f<arrayFreezeExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayFreezeValues;
|
||||
@@ -136,29 +211,49 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
|
||||
const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] );
|
||||
const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] );
|
||||
StopSegment new_seg( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
|
||||
// XXX: Remove Negatives Bug?
|
||||
new_seg.m_iStartRow = BeatToNoteRow(fFreezeBeat);
|
||||
new_seg.m_fStopSeconds = fFreezeSeconds;
|
||||
|
||||
if(fFreezeSeconds > 0.0f)
|
||||
|
||||
// Process the prior stop.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
// LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
|
||||
out.AddStopSegment( new_seg );
|
||||
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, negBeat + fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
|
||||
negBeat = -1;
|
||||
negPause = 0;
|
||||
}
|
||||
else
|
||||
|
||||
if( fFreezeSeconds < 0.0f )
|
||||
{
|
||||
// negative stops (hi JS!) -aj
|
||||
if( PREFSMAN->m_bQuirksMode )
|
||||
{
|
||||
// LOG->Trace( "Adding a negative freeze segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
|
||||
out.AddStopSegment( new_seg );
|
||||
}
|
||||
else
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid stop at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
|
||||
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, negBeat + fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="DELAYS" )
|
||||
{
|
||||
vector<RString> arrayDelayExpressions;
|
||||
@@ -183,7 +278,7 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
// XXX: Remove Negatives Bug?
|
||||
new_seg.m_iStartRow = BeatToNoteRow(fFreezeBeat);
|
||||
new_seg.m_fStopSeconds = fFreezeSeconds;
|
||||
|
||||
|
||||
// LOG->Trace( "Adding a delay segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
|
||||
|
||||
if(fFreezeSeconds > 0.0f)
|
||||
@@ -193,95 +288,6 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="BPMS" )
|
||||
{
|
||||
vector<RString> arrayBPMChangeExpressions;
|
||||
split( sParams[1], ",", arrayBPMChangeExpressions );
|
||||
|
||||
for( unsigned b=0; b<arrayBPMChangeExpressions.size(); b++ )
|
||||
{
|
||||
vector<RString> 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 #%s value \"%s\" (must have exactly one '='), ignored.",
|
||||
sValueName.c_str(), arrayBPMChangeExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( arrayBPMChangeValues[0] );
|
||||
const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] );
|
||||
// XXX: Remove Negatives Bug?
|
||||
BPMSegment new_seg;
|
||||
new_seg.m_iStartRow = BeatToNoteRow(fBeat);
|
||||
new_seg.SetBPM( fNewBPM );
|
||||
|
||||
// convert negative BPMs into Warp segments
|
||||
if( fNewBPM < 0.0f )
|
||||
{
|
||||
vector<RString> arrayNextBPMChangeValues;
|
||||
// get next bpm in sequence
|
||||
if((b+1) < arrayBPMChangeExpressions.size())
|
||||
{
|
||||
split( arrayBPMChangeExpressions[b+1], "=", arrayNextBPMChangeValues );
|
||||
const float fNextPositiveBeat = StringToFloat( arrayNextBPMChangeValues[0] );
|
||||
const float fNextPositiveBPM = StringToFloat( arrayNextBPMChangeValues[1] );
|
||||
|
||||
// tJumpPos = (tPosBPS-abs(negBPS)) + (gPosBPMPosition - fNegPosition)
|
||||
float fDeltaBeat = ((fNextPositiveBPM/60.0f)-abs(fNewBPM/60.0f)) + (fNextPositiveBeat-fBeat);
|
||||
//float fWarpLengthBeats = fNextPositiveBeat + fDeltaBeat;
|
||||
WarpSegment wsTemp(BeatToNoteRow(fBeat),fDeltaBeat);
|
||||
arrayWarpsFromNegativeBPMs.push_back(wsTemp);
|
||||
|
||||
/*
|
||||
LOG->Trace( ssprintf("==NotesLoSM negbpm==\nfnextposbeat = %f, fnextposbpm = %f,\nfdelta = %f, fwarpto = %f",
|
||||
fNextPositiveBeat,
|
||||
fNextPositiveBPM,
|
||||
fDeltaBeat,
|
||||
fWarpToBeat
|
||||
) );
|
||||
*/
|
||||
/*
|
||||
LOG->Trace( ssprintf("==Negative/Subtractive BPM in NotesLoader==\nNegBPM has noterow = %i, BPM = %f\nNextBPM @ noterow %i\nDelta value = %i noterows\nThis warp will have us end up at noterow %i",
|
||||
BeatToNoteRow(fBeat), fNewBPM,
|
||||
BeatToNoteRow(fNextPositiveBeat),
|
||||
BeatToNoteRow(fDeltaBeat),
|
||||
BeatToNoteRow(fWarpToBeat))
|
||||
);
|
||||
*/
|
||||
//float fDeltaBeat = ((fNextPositiveBPM/60.0f)-abs(fNewBPM/60.0f)) + (fNextPositiveBeat-fBeat);
|
||||
/*
|
||||
LOG->Trace( ssprintf("==NotesLoader Delta as NoteRows==\nfDeltaBeat = %f (beat)\nfDeltaBeat = (NextBPMSeg %f - abs(fBPS %f)) + (nextStartRow %i - thisRow %i)",
|
||||
fDeltaBeat,(fNextPositiveBPM/60.0f),abs(fNewBPM/60.0f),BeatToNoteRow(fNextPositiveBeat),BeatToNoteRow(fBeat))
|
||||
);
|
||||
*/
|
||||
|
||||
out.AddBPMSegment( new_seg );
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// last BPM is a negative one? ugh. -aj (MAX_NOTE_ROW exists btw)
|
||||
out.AddBPMSegment( new_seg );
|
||||
}
|
||||
}
|
||||
|
||||
if(fNewBPM > 0.0f)
|
||||
out.AddBPMSegment( new_seg );
|
||||
else
|
||||
{
|
||||
out.m_bHasNegativeBpms = true;
|
||||
// only add Negative BPMs in quirks mode -aj
|
||||
if( PREFSMAN->m_bQuirksMode )
|
||||
out.AddBPMSegment( new_seg );
|
||||
else
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid BPM change at beat %f, BPM %f.", fBeat, fNewBPM );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="TIMESIGNATURES" )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
@@ -326,12 +332,12 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
out.AddTimeSignatureSegment( seg );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
else if( sValueName=="TICKCOUNTS" )
|
||||
{
|
||||
vector<RString> arrayTickcountExpressions;
|
||||
split( sParams[1], ",", arrayTickcountExpressions );
|
||||
|
||||
|
||||
for( unsigned f=0; f<arrayTickcountExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayTickcountValues;
|
||||
@@ -343,7 +349,7 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
sValueName.c_str(), arrayTickcountExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const float fTickcountBeat = StringToFloat( arrayTickcountValues[0] );
|
||||
int iTicks = atoi( arrayTickcountValues[1] );
|
||||
// you're lazy, let SM do the work for you... -DaisuMaster
|
||||
@@ -366,61 +372,8 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// warps (replacement for Negative BPM and Negative Stops)
|
||||
/*
|
||||
else if( sValueName=="WARPS" )
|
||||
{
|
||||
vector<RString> arrayWarpExpressions;
|
||||
split( sParams[1], ",", arrayWarpExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayWarpExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayWarpValues;
|
||||
split( arrayWarpExpressions[f], "=", arrayWarpValues );
|
||||
if( arrayWarpValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
|
||||
sValueName.c_str(), arrayWarpExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fWarpStart = StringToFloat( arrayWarpValues[0] );
|
||||
const float fWarpBeats = StringToFloat( arrayWarpValues[1] );
|
||||
|
||||
if( fWarpStart > 0.0f && fWarpBeats > 0.0f )
|
||||
{
|
||||
WarpSegment new_seg( BeatToNoteRow(fWarpStart), fWarpBeats );
|
||||
out.AddWarpSegment( new_seg );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Currently disallow negative warps, to prevent the same
|
||||
// kind of problem that happened when Negative/Subtractive
|
||||
// BPMs arrived on the StepMania scene. -aj
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid warp at beat %f lasting %f beats.", fWarpStart, fWarpBeats );
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Note: Even though it is possible to have Negative BPMs and Stops in
|
||||
// a song along with Warps, we should not support files that contain
|
||||
// both styles of warp tricks (Negatives vs. #WARPS).
|
||||
// If Warps have been populated from Negative BPMs, then go through that
|
||||
// instead of using the data in the Warps tag. This should be above,
|
||||
// but it breaks compiling so...
|
||||
if(arrayWarpsFromNegativeBPMs.size() > 0)
|
||||
{
|
||||
// zomg we already have some warps...
|
||||
for( unsigned j=0; j<arrayWarpsFromNegativeBPMs.size(); j++ )
|
||||
{
|
||||
out.AddWarpSegment( arrayWarpsFromNegativeBPMs[j] );
|
||||
}
|
||||
}
|
||||
// warp sorting will need to take place.
|
||||
//sort(out.m_WarpSegments.begin(), out.m_WarpSegments.end());
|
||||
// Ensure all of the warps are handled right.
|
||||
sort(out.m_WarpSegments.begin(), out.m_WarpSegments.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -485,6 +485,35 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="WARPS" )
|
||||
{
|
||||
vector<RString> arrayWarpExpressions;
|
||||
split( sParams[1], ",", arrayWarpExpressions );
|
||||
|
||||
for( unsigned b=0; b<arrayWarpExpressions.size(); b++ )
|
||||
{
|
||||
vector<RString> arrayWarpValues;
|
||||
split( arrayWarpExpressions[b], "=", arrayWarpValues );
|
||||
// XXX: Hard to tell which file caused this.
|
||||
if( arrayWarpValues.size() != 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
|
||||
sValueName.c_str(), arrayWarpExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( arrayWarpValues[0] );
|
||||
const float fNewBeat = StringToFloat( arrayWarpValues[1] );
|
||||
|
||||
if(fNewBeat > fBeat)
|
||||
out.m_Timing.AddWarpSegment( WarpSegment(fBeat, fNewBeat) );
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Warp at beat %f, BPM %f.", fBeat, fNewBeat );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="TIMESIGNATURES" )
|
||||
{
|
||||
|
||||
@@ -21,7 +21,10 @@ enum SSCLoadingStates
|
||||
NUM_SSCLoadingStates /**< The number of states used. */
|
||||
};
|
||||
|
||||
/** @brief The version where fakes started to be used as a radar category. */
|
||||
const float VERSION_RADAR_FAKE = 0.53f;
|
||||
/** @brief The version where WarpSegments started to be utilized. */
|
||||
const float VERSION_WARP_SEGMENT = 0.56f;
|
||||
|
||||
/**
|
||||
* @brief The SSCLoader handles all of the parsing needed for .ssc files.
|
||||
|
||||
+17
-4
@@ -144,18 +144,18 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
/*
|
||||
|
||||
f.Write( "#WARPS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_WarpSegments.size(); i++ )
|
||||
{
|
||||
const WarpSegment &ws = out.m_Timing.m_WarpSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(ws.m_iStartRow), ws.m_fWarpBeats ) );
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(ws.m_iStartRow), ws.m_fEndBeat ) );
|
||||
if( i != out.m_Timing.m_WarpSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
*/
|
||||
|
||||
|
||||
ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() );
|
||||
f.Write( "#TIMESIGNATURES:" );
|
||||
@@ -299,6 +299,7 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
lines.push_back( "#BPMS:;" );
|
||||
lines.push_back( "#STOPS:;" );
|
||||
lines.push_back( "#DELAYS:;" );
|
||||
lines.push_back( "#WARPS:;" );
|
||||
lines.push_back( "#TIMESIGNATURES:;" );
|
||||
lines.push_back( "#TICKCOUNTS:;" );
|
||||
lines.push_back( "#ATTACKS:;" );
|
||||
@@ -336,7 +337,19 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
}
|
||||
}
|
||||
lines.push_back( ssprintf( "#DELAYS:%s;", join("\n,", asDelayValues).c_str() ) );
|
||||
|
||||
|
||||
vector<RString> asWarpValues;
|
||||
for( unsigned i=0; i<in.m_Timing.m_WarpSegments.size(); i++ )
|
||||
{
|
||||
const WarpSegment &ws = in.m_Timing.m_WarpSegments[i];
|
||||
|
||||
if( ws.m_bDelay )
|
||||
{
|
||||
asWarpValues.push_back( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fWarpBeats ) );
|
||||
}
|
||||
}
|
||||
lines.push_back( ssprintf( "#WARPS:%s;", join("\n,", asWarpValues).c_str() ) );
|
||||
|
||||
ASSERT( !in.m_Timing.m_vTimeSignatureSegments.empty() );
|
||||
vector<RString> asTimeSigValues;
|
||||
FOREACH_CONST( TimeSignatureSegment, in.m_Timing.m_vTimeSignatureSegments, iter )
|
||||
|
||||
+18
-17
@@ -1520,6 +1520,8 @@ int Player::GetClosestNoteDirectional( int col, int iStartRow, int iEndRow, bool
|
||||
// Is this the row we want?
|
||||
do {
|
||||
const TapNote &tn = begin->second;
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( begin->first ) )
|
||||
break;
|
||||
if( tn.type == TapNote::empty )
|
||||
break;
|
||||
if( !bAllowGraded && tn.result.tns != TNS_None )
|
||||
@@ -1569,6 +1571,11 @@ int Player::GetClosestNonEmptyRowDirectional( int iStartRow, int iEndRow, bool b
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
|
||||
{
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
return iter.Row();
|
||||
}
|
||||
}
|
||||
@@ -1966,7 +1973,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
* Either option would fundamentally change the grading of two quick notes
|
||||
* "jack hammers." Hmm.
|
||||
*/
|
||||
const int iStepSearchRows = BeatToNoteRow( StepSearchDistance * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate );
|
||||
const int iStepSearchRows = BeatToNoteRow( GAMESTATE->m_pCurSong->m_Timing.GetBeatFromElapsedTime( GAMESTATE->m_fMusicSeconds + StepSearchDistance ) ) - iSongRow;
|
||||
int iRowOfOverlappingNoteOrRow = row;
|
||||
if( row == -1 )
|
||||
{
|
||||
@@ -2040,7 +2047,9 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
{
|
||||
case TapNote::mine:
|
||||
// Stepped too close to mine?
|
||||
if( !bRelease && ( REQUIRE_STEP_ON_MINES == !bHeld ) && fSecondsFromExact <= GetWindowSeconds(TW_Mine) )
|
||||
if( !bRelease && ( REQUIRE_STEP_ON_MINES == !bHeld ) &&
|
||||
fSecondsFromExact <= GetWindowSeconds(TW_Mine) &&
|
||||
!GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow(iSongRow) )
|
||||
score = TNS_HitMine;
|
||||
break;
|
||||
|
||||
@@ -2522,8 +2531,8 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
{
|
||||
//LOG->Trace( "Steps::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat );
|
||||
int iMissIfOlderThanThisRow;
|
||||
const float fEarliestTime = GAMESTATE->m_fMusicSeconds - fMissIfOlderThanSeconds;
|
||||
{
|
||||
const float fEarliestTime = GAMESTATE->m_fMusicSeconds - fMissIfOlderThanSeconds;
|
||||
bool bFreeze, bDelay;
|
||||
float fMissIfOlderThanThisBeat;
|
||||
float fThrowAway;
|
||||
@@ -2550,9 +2559,9 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
|
||||
if( !NeedsTapJudging(tn) )
|
||||
continue;
|
||||
|
||||
// warp hackery
|
||||
if( iter.Row() >= GAMESTATE->m_iWarpBeginRow && iter.Row() <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
|
||||
|
||||
// Ignore all notes that are skipped via WARPS.
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
|
||||
continue;
|
||||
|
||||
if( tn.type == TapNote::mine )
|
||||
@@ -2568,11 +2577,6 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
}
|
||||
else
|
||||
{
|
||||
// warp hackery: don't score notes within the warp region.
|
||||
// (Only useful when QuirksMode is enabled.) -aj
|
||||
if( iter.Row() >= GAMESTATE->m_iWarpBeginRow && iter.Row() <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
|
||||
continue;
|
||||
|
||||
tn.result.tns = TNS_Miss;
|
||||
}
|
||||
}
|
||||
@@ -2592,8 +2596,7 @@ void Player::UpdateJudgedRows()
|
||||
int iRow = iter.Row();
|
||||
|
||||
// if row is within a warp section, ignore it. -aj
|
||||
if( iRow >= GAMESTATE->m_iWarpBeginRow &&
|
||||
iRow <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow(iRow) )
|
||||
continue;
|
||||
|
||||
if( iLastSeenRow != iRow )
|
||||
@@ -2928,8 +2931,7 @@ void Player::HandleTapRowScore( unsigned row )
|
||||
#endif
|
||||
|
||||
// more warp hackery. -aj
|
||||
if( row >= (unsigned)GAMESTATE->m_iWarpBeginRow &&
|
||||
row <= (unsigned)(GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( row ) )
|
||||
return;
|
||||
|
||||
if( GAMESTATE->m_bDemonstrationOrJukebox )
|
||||
@@ -3033,8 +3035,7 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH
|
||||
#endif
|
||||
|
||||
// more warp hackery. -aj
|
||||
if( iRow >= GAMESTATE->m_iWarpBeginRow &&
|
||||
iRow <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iRow ) )
|
||||
return;
|
||||
|
||||
// don't accumulate combo if AutoPlay is on.
|
||||
|
||||
+22
-2
@@ -80,6 +80,7 @@ AutoScreenMessage( SM_BackFromTimeSignatureNumeratorChange );
|
||||
AutoScreenMessage( SM_BackFromTimeSignatureDenominatorChange );
|
||||
AutoScreenMessage( SM_BackFromTickcountChange );
|
||||
AutoScreenMessage( SM_BackFromComboChange );
|
||||
AutoScreenMessage( SM_BackFromWarpChange );
|
||||
AutoScreenMessage( SM_DoSaveAndExit );
|
||||
AutoScreenMessage( SM_DoExit );
|
||||
AutoScreenMessage( SM_SaveSuccessful );
|
||||
@@ -549,7 +550,8 @@ static MenuDef g_TimingDataInformation(
|
||||
MenuRowDef( ScreenEdit::time_signature_numerator, "Edit time signature (top)", true, EditMode_Full, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::time_signature_denominator, "Edit time signature (bottom)", true, EditMode_Full, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::tickcount, "Edit tickcount", true, EditMode_Full, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::combo, "Edit combo", true, EditMode_Full, true, true, 0, NULL )
|
||||
MenuRowDef( ScreenEdit::combo, "Edit combo", true, EditMode_Full, true, true, 0, NULL ),
|
||||
MenuRowDef( ScreenEdit::warp, "Edit warp", true, EditMode_Full, true, true, 0, NULL )
|
||||
);
|
||||
|
||||
enum { song_bganimation, song_movie, song_bitmap, global_bganimation, global_movie, global_movie_song_group, global_movie_song_group_and_genre, dynamic_random, baked_random, none };
|
||||
@@ -2639,6 +2641,13 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM )
|
||||
}
|
||||
SetDirty( true );
|
||||
}
|
||||
else if ( SM == SM_BackFromWarpChange )
|
||||
{
|
||||
float fWarp = StringToFloat( ScreenTextEntry::s_sLastAnswer );
|
||||
if( fWarp > 0 )
|
||||
m_pSong->m_Timing.SetWarpAtBeat( GAMESTATE->m_fSongBeat, fWarp );
|
||||
SetDirty( true );
|
||||
}
|
||||
else if( SM == SM_BackFromBGChange )
|
||||
{
|
||||
HandleBGChangeChoice( (BGChangeChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers );
|
||||
@@ -3176,6 +3185,7 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector<int> &iAns
|
||||
g_TimingDataInformation.rows[time_signature_denominator].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTimeSignatureDenominatorAtBeat( fBeat ) ) );
|
||||
g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtBeat( fBeat ) ) );
|
||||
g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d", pTime.GetComboAtBeat( fBeat ) ) );
|
||||
g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetWarpAtBeat( fBeat ) ) );
|
||||
|
||||
EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation );
|
||||
}
|
||||
@@ -3493,7 +3503,7 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAns
|
||||
|
||||
// don't move the step from where it is, just move everything later
|
||||
NoteDataUtil::InsertRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopBeats) );
|
||||
m_pSong->m_Timing.InsertRows( BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopSeconds) );
|
||||
m_pSong->m_Timing.InsertRows( BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopBeats) );
|
||||
}
|
||||
break;
|
||||
case undo:
|
||||
@@ -3609,6 +3619,7 @@ static LocalizedString ENTER_TIME_SIGNATURE_NUMERATOR_VALUE ( "ScreenEdit", "Ent
|
||||
static LocalizedString ENTER_TIME_SIGNATURE_DENOMINATOR_VALUE ( "ScreenEdit", "Enter a new Time Signature denominator value." );
|
||||
static LocalizedString ENTER_TICKCOUNT_VALUE ( "ScreenEdit", "Enter a new Tickcount value." );
|
||||
static LocalizedString ENTER_COMBO_VALUE ( "ScreenEdit", "Enter a new Combo value." );
|
||||
static LocalizedString ENTER_WARP_VALUE ( "ScreenEdit", "Enter a new Warp value." );
|
||||
void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice c, const vector<int> &iAnswers )
|
||||
{
|
||||
switch( c )
|
||||
@@ -3673,6 +3684,15 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice
|
||||
4
|
||||
);
|
||||
break;
|
||||
case warp:
|
||||
// todo: Call a screen with class ScreenTextEntry instead. -aj
|
||||
ScreenTextEntry::TextEntry(
|
||||
SM_BackFromWarpChange,
|
||||
ENTER_WARP_VALUE,
|
||||
ssprintf( "%.4f", m_pSong->m_Timing.GetWarpAtRow( BeatToNoteRow(GAMESTATE->m_fSongBeat) ) ),
|
||||
10
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -456,6 +456,7 @@ public:
|
||||
time_signature_denominator,
|
||||
tickcount,
|
||||
combo,
|
||||
warp,
|
||||
NUM_TIMING_DATA_INFORMATION_CHOICES
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -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 = 166;
|
||||
const int FILE_CACHE_VERSION = 167;
|
||||
|
||||
/** @brief How long does a song sample last by default? */
|
||||
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ struct lua_State;
|
||||
struct BackgroundChange;
|
||||
|
||||
/** @brief The version of the .ssc file format. */
|
||||
const static float STEPFILE_VERSION_NUMBER = 0.55f;
|
||||
const static float STEPFILE_VERSION_NUMBER = 0.56f;
|
||||
|
||||
/** @brief How many edits for this song can each profile have? */
|
||||
const int MAX_EDITS_PER_SONG_PER_PROFILE = 5;
|
||||
@@ -307,7 +307,7 @@ public:
|
||||
|
||||
void AddBPMSegment( const BPMSegment &seg ) { m_Timing.AddBPMSegment( seg ); }
|
||||
void AddStopSegment( const StopSegment &seg ) { m_Timing.AddStopSegment( seg ); }
|
||||
//void AddWarpSegment( const WarpSegment &seg ) { m_Timing.AddWarpSegment( seg ); }
|
||||
void AddWarpSegment( const WarpSegment &seg ) { m_Timing.AddWarpSegment( seg ); }
|
||||
void AddBackgroundChange( BackgroundLayer blLayer, BackgroundChange seg );
|
||||
void AddForegroundChange( BackgroundChange seg );
|
||||
void AddLyricSegment( LyricSegment seg );
|
||||
|
||||
+322
-208
@@ -150,12 +150,30 @@ void TimingData::SetTimeSignatureDenominatorAtRow( int iRow, int iDenominator )
|
||||
SetTimeSignatureAtRow( iRow, GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iRow ) ).m_iNumerator, iDenominator );
|
||||
}
|
||||
|
||||
/*
|
||||
void TimingData::SetWarpAtRow( int iRowAt, float fLengthBeats )
|
||||
void TimingData::SetWarpAtRow( int iRow, float fNew )
|
||||
{
|
||||
// todo: code this -aj
|
||||
unsigned i;
|
||||
for( i=0; i<m_WarpSegments.size(); i++ )
|
||||
if( m_WarpSegments[i].m_iStartRow == iRow )
|
||||
break;
|
||||
bool valid = iRow > 0 && NoteRowToBeat(iRow) < fNew;
|
||||
if( i == m_WarpSegments.size() )
|
||||
{
|
||||
if( valid )
|
||||
{
|
||||
AddWarpSegment( WarpSegment(iRow, fNew) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( valid )
|
||||
{
|
||||
m_WarpSegments[i].m_fEndBeat = fNew;
|
||||
}
|
||||
else
|
||||
m_WarpSegments.erase( m_WarpSegments.begin()+i, m_WarpSegments.begin()+i+1 );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* Change an existing Tickcount segment, merge identical segments together or insert a new one. */
|
||||
void TimingData::SetTickcountAtRow( int iRow, int iTicks )
|
||||
@@ -229,13 +247,13 @@ int TimingData::GetComboAtRow( int iNoteRow ) const
|
||||
return m_ComboSegments[GetComboSegmentIndexAtRow( iNoteRow )].m_iCombo;
|
||||
}
|
||||
|
||||
int TimingData::GetWarpToRow( int iWarpBeginRow ) const
|
||||
float TimingData::GetWarpAtRow( int iWarpRow ) const
|
||||
{
|
||||
for( unsigned i=0; i<m_WarpSegments.size(); i++ )
|
||||
{
|
||||
if( m_WarpSegments[i].m_iStartRow == iWarpBeginRow )
|
||||
if( m_WarpSegments[i].m_iStartRow == iWarpRow )
|
||||
{
|
||||
return iWarpBeginRow + BeatToNoteRow(m_WarpSegments[i].m_fWarpBeats);
|
||||
return m_WarpSegments[i].m_fEndBeat;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -296,7 +314,7 @@ int TimingData::GetBPMSegmentIndexAtRow( int iNoteRow ) const
|
||||
for( i=0; i<m_BPMSegments.size()-1; i++ )
|
||||
if( m_BPMSegments[i+1].m_iStartRow > iNoteRow )
|
||||
break;
|
||||
return (int)i;
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
int TimingData::GetStopSegmentIndexAtRow( int iNoteRow, bool bDelay ) const
|
||||
@@ -308,16 +326,72 @@ int TimingData::GetStopSegmentIndexAtRow( int iNoteRow, bool bDelay ) const
|
||||
if( s.m_iStartRow > iNoteRow && s.m_bDelay == bDelay )
|
||||
break;
|
||||
}
|
||||
return (int)i;
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
int TimingData::GetWarpSegmentIndexAtRow( int iNoteRow ) const
|
||||
{
|
||||
unsigned i;
|
||||
for( i=0; i<m_WarpSegments.size()-1; i++ )
|
||||
{
|
||||
const WarpSegment& s = m_WarpSegments[i+1];
|
||||
if( s.m_iStartRow > iNoteRow )
|
||||
break;
|
||||
}
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
bool TimingData::IsWarpAtRow( int iNoteRow ) const
|
||||
{
|
||||
if( m_WarpSegments.empty() )
|
||||
return false;
|
||||
|
||||
int i = GetWarpSegmentIndexAtRow( iNoteRow );
|
||||
const WarpSegment& s = m_WarpSegments[i];
|
||||
if( s.m_iStartRow <= iNoteRow && iNoteRow < BeatToNoteRow(s.m_fEndBeat) )
|
||||
{
|
||||
if( m_StopSegments.empty() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if( GetStopAtRow(iNoteRow) != 0.0f )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TimingData::GetTimeSignatureSegmentIndexAtRow( int iRow ) const
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < (int)(m_vTimeSignatureSegments.size()) - 1; i++ )
|
||||
unsigned i;
|
||||
for (i=0; i < m_vTimeSignatureSegments.size() - 1; i++ )
|
||||
if( m_vTimeSignatureSegments[i+1].m_iStartRow > iRow )
|
||||
break;
|
||||
return i;
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
int TimingData::GetComboSegmentIndexAtRow( int iRow ) const
|
||||
{
|
||||
unsigned i;
|
||||
for( i=0; i<m_ComboSegments.size()-1; i++ )
|
||||
{
|
||||
const ComboSegment& s = m_ComboSegments[i+1];
|
||||
if( s.m_iStartRow > iRow )
|
||||
break;
|
||||
}
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow )
|
||||
{
|
||||
static BPMSegment empty;
|
||||
if( m_BPMSegments.empty() )
|
||||
return empty;
|
||||
|
||||
int i = GetBPMSegmentIndexAtRow( iNoteRow );
|
||||
return m_BPMSegments[i];
|
||||
}
|
||||
|
||||
TimeSignatureSegment& TimingData::GetTimeSignatureSegmentAtRow( int iRow )
|
||||
@@ -339,28 +413,6 @@ int TimingData::GetTimeSignatureDenominatorAtRow( int iRow )
|
||||
return GetTimeSignatureSegmentAtRow( iRow ).m_iDenominator;
|
||||
}
|
||||
|
||||
int TimingData::GetComboSegmentIndexAtRow( int iRow ) const
|
||||
{
|
||||
unsigned i;
|
||||
for( i=0; i<m_ComboSegments.size()-1; i++ )
|
||||
{
|
||||
const ComboSegment& s = m_ComboSegments[i+1];
|
||||
if( s.m_iStartRow > iRow )
|
||||
break;
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow )
|
||||
{
|
||||
static BPMSegment empty;
|
||||
if( m_BPMSegments.empty() )
|
||||
return empty;
|
||||
|
||||
int i = GetBPMSegmentIndexAtRow( iNoteRow );
|
||||
return m_BPMSegments[i];
|
||||
}
|
||||
|
||||
ComboSegment& TimingData::GetComboSegmentAtRow( int iRow )
|
||||
{
|
||||
unsigned i;
|
||||
@@ -380,6 +432,16 @@ StopSegment& TimingData::GetStopSegmentAtRow( int iNoteRow, bool bDelay )
|
||||
return m_StopSegments[i];
|
||||
}
|
||||
|
||||
WarpSegment& TimingData::GetWarpSegmentAtRow( int iRow )
|
||||
{
|
||||
static WarpSegment empty;
|
||||
if( m_WarpSegments.empty() )
|
||||
return empty;
|
||||
|
||||
int i = GetWarpSegmentIndexAtRow( iRow );
|
||||
return m_WarpSegments[i];
|
||||
}
|
||||
|
||||
int TimingData::GetTickcountSegmentIndexAtRow( int iRow ) const
|
||||
{
|
||||
int i;
|
||||
@@ -411,150 +473,118 @@ void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatO
|
||||
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeatOut, fBPSOut, bFreezeOut, bDelayOut, iWarpBeginOut, fWarpLengthOut );
|
||||
}
|
||||
|
||||
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpLengthOut ) const
|
||||
enum
|
||||
{
|
||||
// LOG->Trace( "GetBeatAndBPSFromElapsedTime( fElapsedTime = %f )", fElapsedTime );
|
||||
const float fTime = fElapsedTime;
|
||||
fElapsedTime += m_fBeat0OffsetInSeconds;
|
||||
FOUND_WARP,
|
||||
FOUND_WARP_DESTINATION,
|
||||
FOUND_BPM_CHANGE,
|
||||
FOUND_STOP,
|
||||
FOUND_MARKER,
|
||||
NOT_FOUND
|
||||
};
|
||||
|
||||
for( unsigned i=0; i<m_BPMSegments.size(); i++ ) // foreach BPMSegment
|
||||
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpDestinationOut ) const
|
||||
{
|
||||
|
||||
vector<BPMSegment>::const_iterator itBPMS = m_BPMSegments.begin();
|
||||
vector<WarpSegment>::const_iterator itWS = m_WarpSegments.begin();
|
||||
vector<StopSegment>::const_iterator itSS = m_StopSegments.begin();
|
||||
|
||||
bFreezeOut = false;
|
||||
bDelayOut = false;
|
||||
|
||||
iWarpBeginOut = -1;
|
||||
|
||||
int iLastRow = 0;
|
||||
float fLastTime = -m_fBeat0OffsetInSeconds;
|
||||
float fBPS = GetBPMAtRow(0) / 60.0;
|
||||
|
||||
float bIsWarping = false;
|
||||
float fWarpDestination = 0.0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
const int iStartRowThisSegment = m_BPMSegments[i].m_iStartRow;
|
||||
const float fStartBeatThisSegment = NoteRowToBeat( iStartRowThisSegment );
|
||||
const bool bIsFirstBPMSegment = i==0;
|
||||
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1;
|
||||
const int iStartRowNextSegment = bIsLastBPMSegment ? MAX_NOTE_ROW : m_BPMSegments[i+1].m_iStartRow;
|
||||
const float fStartBeatNextSegment = NoteRowToBeat( iStartRowNextSegment );
|
||||
const float fBPS = m_BPMSegments[i].m_fBPS;
|
||||
|
||||
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze
|
||||
int iEventRow = INT_MAX;
|
||||
int iEventType = NOT_FOUND;
|
||||
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
|
||||
{
|
||||
const bool bIsDelay = m_StopSegments[j].m_bDelay;
|
||||
if( !bIsFirstBPMSegment && iStartRowThisSegment >= m_StopSegments[j].m_iStartRow )
|
||||
continue;
|
||||
if( !bIsLastBPMSegment && m_StopSegments[j].m_iStartRow > iStartRowNextSegment )
|
||||
continue;
|
||||
|
||||
// this freeze lies within this BPMSegment
|
||||
const int iRowsBeatsSinceStartOfSegment = m_StopSegments[j].m_iStartRow - iStartRowThisSegment;
|
||||
const float fBeatsSinceStartOfSegment = NoteRowToBeat(iRowsBeatsSinceStartOfSegment);
|
||||
const float fFreezeStartSecond = fBeatsSinceStartOfSegment / fBPS;
|
||||
|
||||
// modified for delays
|
||||
if( !bIsDelay && fFreezeStartSecond >= fElapsedTime )
|
||||
break;
|
||||
if( bIsDelay && fFreezeStartSecond > fElapsedTime )
|
||||
break;
|
||||
|
||||
// the freeze segment is <= current time
|
||||
fElapsedTime -= m_StopSegments[j].m_fStopSeconds;
|
||||
|
||||
if( (fFreezeStartSecond >= fElapsedTime && !bIsDelay) ||
|
||||
(fFreezeStartSecond > fElapsedTime && bIsDelay) )
|
||||
iEventRow = BeatToNoteRow(fWarpDestination);
|
||||
iEventType = FOUND_WARP_DESTINATION;
|
||||
}
|
||||
if( itBPMS != m_BPMSegments.end() && itBPMS->m_iStartRow < iEventRow )
|
||||
{
|
||||
iEventRow = itBPMS->m_iStartRow;
|
||||
iEventType = FOUND_BPM_CHANGE;
|
||||
}
|
||||
if( itSS != m_StopSegments.end() && itSS->m_iStartRow < iEventRow )
|
||||
{
|
||||
iEventRow = itSS->m_iStartRow;
|
||||
iEventType = FOUND_STOP;
|
||||
}
|
||||
if( itWS != m_WarpSegments.end() && itWS->m_iStartRow < iEventRow )
|
||||
{
|
||||
iEventRow = itWS->m_iStartRow;
|
||||
iEventType = FOUND_WARP;
|
||||
}
|
||||
if( iEventType == NOT_FOUND )
|
||||
{
|
||||
break;
|
||||
}
|
||||
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
|
||||
float fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
if ( fElapsedTime < fNextEventTime )
|
||||
{
|
||||
break;
|
||||
}
|
||||
fLastTime = fNextEventTime;
|
||||
switch( iEventType )
|
||||
{
|
||||
case FOUND_WARP_DESTINATION:
|
||||
bIsWarping = false;
|
||||
break;
|
||||
case FOUND_BPM_CHANGE:
|
||||
fBPS = itBPMS->m_fBPS;
|
||||
itBPMS ++;
|
||||
break;
|
||||
case FOUND_STOP: // TODO: update for Delays.
|
||||
{
|
||||
// The time lies within the stop.
|
||||
fBeatOut = NoteRowToBeat(m_StopSegments[j].m_iStartRow);
|
||||
fBPSOut = fBPS;
|
||||
bFreezeOut = !bIsDelay;
|
||||
bDelayOut = bIsDelay;
|
||||
//iWarpBeginOut = -1;
|
||||
//fWarpLengthOut = -1;
|
||||
return;
|
||||
fTimeToNextEvent = itSS->m_fStopSeconds;
|
||||
fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
const bool bIsDelay = itSS->m_bDelay;
|
||||
if ( fElapsedTime < fNextEventTime )
|
||||
{
|
||||
bFreezeOut = !bIsDelay;
|
||||
bDelayOut = bIsDelay;
|
||||
fBeatOut = NoteRowToBeat( itSS->m_iStartRow );
|
||||
fBPSOut = fBPS;
|
||||
return;
|
||||
}
|
||||
fLastTime = fNextEventTime;
|
||||
itSS ++;
|
||||
}
|
||||
}
|
||||
|
||||
// by this point we should have the warps in their own place.
|
||||
for( unsigned j=0; j<m_WarpSegments.size(); j++ ) // foreach warp
|
||||
{
|
||||
if( !bIsFirstBPMSegment && iStartRowThisSegment >= m_WarpSegments[j].m_iStartRow )
|
||||
continue;
|
||||
if( !bIsLastBPMSegment && m_WarpSegments[j].m_iStartRow > iStartRowNextSegment )
|
||||
continue;
|
||||
|
||||
/*
|
||||
const int iRowsBeatsSinceStartOfSegment = m_WarpSegments[j].m_iStartRow - iStartRowThisSegment;
|
||||
const float fBeatsSinceStartOfSegment = NoteRowToBeat(iRowsBeatsSinceStartOfSegment);
|
||||
const float fWarpStartSecond = fBeatsSinceStartOfSegment / fBPS;
|
||||
*/
|
||||
|
||||
// the freeze segment is <= current time
|
||||
//fElapsedTime -= m_WarpSegments[j].m_fWarpBeats;
|
||||
|
||||
// this warp lies within this BPMSegment.
|
||||
/*
|
||||
if( fWarpStartSecond >= fElapsedTime )
|
||||
break;
|
||||
case FOUND_WARP:
|
||||
bIsWarping = true;
|
||||
if( itWS->m_fEndBeat > fWarpDestination )
|
||||
{
|
||||
// this WarpSegment IS the current segment.
|
||||
// don't know how to properly handle beatout -aj
|
||||
//fBeatOut = NoteRowToBeat(m_WarpSegments[j].m_iStartRow);
|
||||
fBeatOut = fStartBeatThisSegment + fElapsedTime*fBPS;
|
||||
fBPSOut = m_BPMSegments[i+1].m_fBPS;
|
||||
bFreezeOut = false;
|
||||
bDelayOut = false;
|
||||
iWarpBeginOut = m_WarpSegments[j].m_iStartRow;
|
||||
fWarpLengthOut = m_WarpSegments[j].m_fWarpBeats;
|
||||
return;
|
||||
fWarpDestination = itWS->m_fEndBeat;
|
||||
}
|
||||
*/
|
||||
iWarpBeginOut = iEventRow;
|
||||
fWarpDestinationOut = fWarpDestination;
|
||||
itWS ++;
|
||||
break;
|
||||
}
|
||||
|
||||
const float fBeatsInThisSegment = fStartBeatNextSegment - fStartBeatThisSegment;
|
||||
const float fSecondsInThisSegment = fBeatsInThisSegment / fBPS;
|
||||
//if(fBPS < 0.0f)
|
||||
/*
|
||||
if(fStartBeatThisSegment == 445.500f || fStartBeatThisSegment == 449.500)
|
||||
{
|
||||
LOG->Trace( ssprintf("segment (beat %f) beats: %f / seconds: %f / BPS: %f",fStartBeatThisSegment,fBeatsInThisSegment,fSecondsInThisSegment,fBPS) );
|
||||
}
|
||||
*/
|
||||
if( bIsLastBPMSegment || fElapsedTime <= fSecondsInThisSegment )
|
||||
{
|
||||
// this BPMSegment IS the current segment.
|
||||
fBeatOut = fStartBeatThisSegment + fElapsedTime*fBPS;
|
||||
fBPSOut = fBPS;
|
||||
bFreezeOut = false;
|
||||
bDelayOut = false;
|
||||
//iWarpBeginOut;
|
||||
//fWarpLengthOut;
|
||||
return;
|
||||
}
|
||||
|
||||
// this BPMSegment is NOT the current segment.
|
||||
fElapsedTime -= fSecondsInThisSegment;
|
||||
// xxx: negative testing [aj]
|
||||
/*
|
||||
//if(fBPS < 0.0f)
|
||||
if( (fStartBeatNextSegment >= 445.490f && fStartBeatNextSegment <= 453.72f) || fBPS < 0.0f )
|
||||
{
|
||||
//LOG->Trace( ssprintf("beat %f is %f BPS (%f BPM)",fBeatOut,fBPSOut,fBPSOut*60.0f) );
|
||||
//LOG->Trace( ssprintf("start beat %f + elapsed time %f",fStartBeatThisSegment,fElapsedTime) );
|
||||
//LOG->Trace( ssprintf("elapsed time is now %f",fElapsedTime) );
|
||||
}
|
||||
*/
|
||||
iLastRow = iEventRow;
|
||||
}
|
||||
// If we get here, something has gone wrong. Is everything sorted?
|
||||
vector<BPMSegment> vBPMS = m_BPMSegments;
|
||||
vector<StopSegment> vSS = m_StopSegments;
|
||||
vector<WarpSegment> vWS = m_WarpSegments;
|
||||
vector<TimeSignatureSegment> vTSS = m_vTimeSignatureSegments;
|
||||
vector<TickcountSegment> vTS = m_TickcountSegments;
|
||||
vector<ComboSegment> vCS = m_ComboSegments;
|
||||
sort( vBPMS.begin(), vBPMS.end() );
|
||||
sort( vSS.begin(), vSS.end() );
|
||||
sort( vWS.begin(), vWS.end() );
|
||||
sort( vTSS.begin(), vTSS.end() );
|
||||
sort( vTS.begin(), vTS.end() );
|
||||
sort( vCS.begin(), vCS.end() );
|
||||
ASSERT_M( vBPMS == m_BPMSegments, "The BPM segments were not sorted!" );
|
||||
ASSERT_M( vSS == m_StopSegments, "The Stop segments were not sorted!" );
|
||||
ASSERT_M( vWS == m_WarpSegments, "The Warp segments were not sorted!" );
|
||||
ASSERT_M( vTSS == m_vTimeSignatureSegments, "The Time Signature segments were not sorted!" );
|
||||
ASSERT_M( vTS == m_TickcountSegments, "The Tickcount segments were not sorted!" );
|
||||
ASSERT_M( vCS == m_ComboSegments, "The Combo segments were not sorted!" );
|
||||
FAIL_M( ssprintf("Failed to find the appropriate segment for elapsed time %f.", fTime) );
|
||||
|
||||
fBeatOut = NoteRowToBeat( iLastRow ) + (fElapsedTime - fLastTime) * fBPS;
|
||||
fBPSOut = fBPS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
|
||||
{
|
||||
return TimingData::GetElapsedTimeFromBeatNoOffset( fBeat ) - PREFSMAN->m_fGlobalOffsetSeconds;
|
||||
@@ -562,53 +592,86 @@ float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
|
||||
|
||||
float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const
|
||||
{
|
||||
float fElapsedTime = 0;
|
||||
fElapsedTime -= m_fBeat0OffsetInSeconds;
|
||||
|
||||
int iRow = BeatToNoteRow(fBeat);
|
||||
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze
|
||||
vector<BPMSegment>::const_iterator itBPMS = m_BPMSegments.begin();
|
||||
vector<WarpSegment>::const_iterator itWS = m_WarpSegments.begin();
|
||||
vector<StopSegment>::const_iterator itSS = m_StopSegments.begin();
|
||||
|
||||
int iLastRow = 0;
|
||||
float fLastTime = -m_fBeat0OffsetInSeconds;
|
||||
float fBPS = GetBPMAtRow(0) / 60.0;
|
||||
|
||||
float bIsWarping = false;
|
||||
float fWarpDestination = 0.0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
/* A traditional stop has the beat happening before the stop. (>=)
|
||||
* A Pump delay acts differently: the pause is before the beat. (>)
|
||||
*/
|
||||
if( ( m_StopSegments[j].m_iStartRow >= iRow && !m_StopSegments[j].m_bDelay ) ||
|
||||
( m_StopSegments[j].m_iStartRow > iRow && m_StopSegments[j].m_bDelay ) )
|
||||
int iEventRow = INT_MAX;
|
||||
int iEventType = NOT_FOUND;
|
||||
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
|
||||
{
|
||||
iEventRow = BeatToNoteRow(fWarpDestination);
|
||||
iEventType = FOUND_WARP_DESTINATION;
|
||||
}
|
||||
if( itBPMS != m_BPMSegments.end() && itBPMS->m_iStartRow < iEventRow )
|
||||
{
|
||||
iEventRow = itBPMS->m_iStartRow;
|
||||
iEventType = FOUND_BPM_CHANGE;
|
||||
}
|
||||
if( itSS != m_StopSegments.end() && itSS->m_bDelay && itSS->m_iStartRow < iEventRow ) // delays (come before marker)
|
||||
{
|
||||
iEventRow = itSS->m_iStartRow;
|
||||
iEventType = FOUND_STOP;
|
||||
}
|
||||
if( BeatToNoteRow(fBeat) < iEventRow )
|
||||
{
|
||||
iEventRow = BeatToNoteRow(fBeat);
|
||||
iEventType = FOUND_MARKER;
|
||||
}
|
||||
if( itSS != m_StopSegments.end() && !itSS->m_bDelay && itSS->m_iStartRow < iEventRow ) // stops (come after marker)
|
||||
{
|
||||
iEventRow = itSS->m_iStartRow;
|
||||
iEventType = FOUND_STOP;
|
||||
}
|
||||
if( itWS != m_WarpSegments.end() && itWS->m_iStartRow < iEventRow )
|
||||
{
|
||||
iEventRow = itWS->m_iStartRow;
|
||||
iEventType = FOUND_WARP;
|
||||
}
|
||||
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
|
||||
float fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
fLastTime = fNextEventTime;
|
||||
switch( iEventType )
|
||||
{
|
||||
case FOUND_WARP_DESTINATION:
|
||||
bIsWarping = false;
|
||||
break;
|
||||
case FOUND_BPM_CHANGE:
|
||||
fBPS = itBPMS->m_fBPS;
|
||||
itBPMS ++;
|
||||
break;
|
||||
case FOUND_STOP:
|
||||
fTimeToNextEvent = itSS->m_fStopSeconds;
|
||||
fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
fLastTime = fNextEventTime;
|
||||
itSS ++;
|
||||
break;
|
||||
case FOUND_MARKER:
|
||||
return fLastTime;
|
||||
case FOUND_WARP:
|
||||
bIsWarping = true;
|
||||
if( itWS->m_fEndBeat > fWarpDestination )
|
||||
{
|
||||
fWarpDestination = itWS->m_fEndBeat;
|
||||
}
|
||||
itWS ++;
|
||||
break;
|
||||
fElapsedTime += m_StopSegments[j].m_fStopSeconds;
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<m_BPMSegments.size(); i++ ) // foreach BPMSegment
|
||||
{
|
||||
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1;
|
||||
const float fBPS = m_BPMSegments[i].m_fBPS;
|
||||
|
||||
if( bIsLastBPMSegment )
|
||||
{
|
||||
fElapsedTime += NoteRowToBeat( iRow ) / fBPS;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int iStartIndexThisSegment = m_BPMSegments[i].m_iStartRow;
|
||||
const int iStartIndexNextSegment = m_BPMSegments[i+1].m_iStartRow;
|
||||
const int iRowsInThisSegment = min( iStartIndexNextSegment - iStartIndexThisSegment, iRow );
|
||||
fElapsedTime += NoteRowToBeat( iRowsInThisSegment ) / fBPS;
|
||||
iRow -= iRowsInThisSegment;
|
||||
}
|
||||
|
||||
if( iRow <= 0 )
|
||||
return fElapsedTime;
|
||||
iLastRow = iEventRow;
|
||||
}
|
||||
|
||||
/*
|
||||
for( unsigned i=0; i<m_WarpSegments.size(); i++ ) // foreach WarpSegment
|
||||
{
|
||||
// todo: is this correct? -aj
|
||||
const int iWarpToRow = m_WarpSegments[i].m_iEndRow;
|
||||
fElapsedTime += NoteRowToBeat( iWarpToRow );
|
||||
}
|
||||
*/
|
||||
|
||||
return fElapsedTime;
|
||||
|
||||
// won't reach here, unless BeatToNoteRow(fBeat == INT_MAX) (impossible)
|
||||
|
||||
}
|
||||
|
||||
void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool bAdjustBPM )
|
||||
@@ -627,7 +690,7 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
|
||||
else
|
||||
m_BPMSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex;
|
||||
}
|
||||
|
||||
|
||||
for( unsigned i = 0; i < m_StopSegments.size(); i++ )
|
||||
{
|
||||
const int iSegStartRow = m_StopSegments[i].m_iStartRow;
|
||||
@@ -639,6 +702,25 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
|
||||
m_StopSegments[i].m_iStartRow = lrintf((iSegStartRow - iStartIndex) * fScale) + iStartIndex;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_WarpSegments.size(); i++ )
|
||||
{
|
||||
const int iSegStartRow = m_WarpSegments[i].m_iStartRow;
|
||||
const int iSegEndRow = BeatToNoteRow( m_WarpSegments[i].m_fEndBeat );
|
||||
if( iSegEndRow >= iStartIndex )
|
||||
{
|
||||
if( iSegEndRow > iEndIndex )
|
||||
m_WarpSegments[i].m_fEndBeat += NoteRowToBeat(lrintf((iEndIndex - iStartIndex) * (fScale - 1)));
|
||||
else
|
||||
m_WarpSegments[i].m_fEndBeat = NoteRowToBeat(lrintf((iSegEndRow - iStartIndex) * fScale) + iStartIndex);
|
||||
}
|
||||
if( iSegStartRow < iStartIndex )
|
||||
continue;
|
||||
else if( iSegStartRow > iEndIndex )
|
||||
m_WarpSegments[i].m_iStartRow += lrintf((iEndIndex - iStartIndex) * (fScale - 1));
|
||||
else
|
||||
m_WarpSegments[i].m_iStartRow = lrintf((iSegStartRow - iStartIndex) * fScale) + iStartIndex;
|
||||
}
|
||||
|
||||
// adjust BPM changes to preserve timing
|
||||
if( bAdjustBPM )
|
||||
{
|
||||
@@ -683,6 +765,16 @@ void TimingData::InsertRows( int iStartRow, int iRowsToAdd )
|
||||
stop.m_iStartRow += iRowsToAdd;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_WarpSegments.size(); i++ )
|
||||
{
|
||||
WarpSegment &warp = m_WarpSegments[i];
|
||||
if( BeatToNoteRow(warp.m_fEndBeat) >= iStartRow )
|
||||
warp.m_fEndBeat += NoteRowToBeat(iRowsToAdd);
|
||||
if( warp.m_iStartRow < iStartRow )
|
||||
continue;
|
||||
warp.m_iStartRow += iRowsToAdd;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_vTimeSignatureSegments.size(); i++ )
|
||||
{
|
||||
TimeSignatureSegment &time = m_vTimeSignatureSegments[i];
|
||||
@@ -764,6 +856,26 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete )
|
||||
stop.m_iStartRow -= iRowsToDelete;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_WarpSegments.size(); i++ )
|
||||
{
|
||||
WarpSegment &warp = m_WarpSegments[i];
|
||||
|
||||
if( BeatToNoteRow(warp.m_fEndBeat) >= iStartRow )
|
||||
warp.m_fEndBeat = max( NoteRowToBeat(iStartRow), warp.m_fEndBeat - NoteRowToBeat(iRowsToDelete) );
|
||||
|
||||
if( warp.m_iStartRow < iStartRow )
|
||||
continue;
|
||||
|
||||
if( warp.m_iStartRow < iStartRow+iRowsToDelete )
|
||||
{
|
||||
m_WarpSegments.erase( m_WarpSegments.begin()+i, m_WarpSegments.begin()+i+1 );
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
warp.m_iStartRow -= iRowsToDelete;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_vTimeSignatureSegments.size(); i++ )
|
||||
{
|
||||
TimeSignatureSegment &time = m_vTimeSignatureSegments[i];
|
||||
@@ -891,6 +1003,7 @@ class LunaTimingData: public Luna<TimingData>
|
||||
public:
|
||||
static int HasStops( T* p, lua_State *L ) { lua_pushboolean(L, p->HasStops()); return 1; }
|
||||
static int HasBPMChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasBpmChanges()); return 1; }
|
||||
static int HasWarps( T* p, lua_State *L ) { lua_pushboolean(L, p->HasWarps()); return 1; }
|
||||
static int GetStops( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vStops;
|
||||
@@ -965,6 +1078,7 @@ public:
|
||||
{
|
||||
ADD_METHOD( HasStops );
|
||||
ADD_METHOD( HasBPMChanges );
|
||||
ADD_METHOD( HasWarps );
|
||||
ADD_METHOD( GetStops );
|
||||
ADD_METHOD( GetDelays );
|
||||
ADD_METHOD( GetBPMs );
|
||||
|
||||
+84
-16
@@ -318,25 +318,41 @@ struct TimeSignatureSegment
|
||||
struct WarpSegment
|
||||
{
|
||||
/**
|
||||
* @brief Creates a simple Warp Segment with default values.
|
||||
* @brief Create a simple Warp Segment with default values.
|
||||
*
|
||||
* It is best to override the values as soon as possible.
|
||||
*/
|
||||
WarpSegment() : m_iStartRow(-1), m_fWarpBeats(-1) { }
|
||||
WarpSegment() : m_iStartRow(-1), m_fEndBeat(-1) { }
|
||||
/**
|
||||
* @brief Creates a Warp Segment with the specified starting row and row to warp to.
|
||||
* @brief Create a Warp Segment with the specified starting row and row to warp to.
|
||||
* @param s the starting row of this segment.
|
||||
* @param r the row to warp to.
|
||||
*/
|
||||
WarpSegment( int s, int r ): m_iStartRow(max(0, s)),
|
||||
m_fWarpBeats(max(0, NoteRowToBeat(r))) {}
|
||||
WarpSegment( int s, int r ): m_iStartRow(max(0, (s < r ? s : r))),
|
||||
m_fEndBeat(max(0, NoteRowToBeat((r > s ? r : s)))) {}
|
||||
/**
|
||||
* @brief Creates a Warp Segment with the specified starting row and beat to warp to.
|
||||
* @param s the starting row of this segment.
|
||||
* @param b the beat to warp to.
|
||||
*/
|
||||
WarpSegment( int s, float b ): m_iStartRow(max(0, s)),
|
||||
m_fWarpBeats(max(0, b)) {}
|
||||
m_fEndBeat(max(0, b)) {}
|
||||
/**
|
||||
* @brief Create a Warp Segment with the specified starting beat and row to warp to.
|
||||
* @param s the starting beat in this segment.
|
||||
* @param r the row to warp to.
|
||||
*/
|
||||
WarpSegment( float s, int r ):
|
||||
m_iStartRow(max(0, BeatToNoteRow(s))),
|
||||
m_fEndBeat(max(0, NoteRowToBeat(r))) {}
|
||||
/**
|
||||
* @brief Creates a Warp Segment with the specified starting beat and beat to warp to.
|
||||
* @param s the starting beat of this segment.
|
||||
* @param b the beat to warp to.
|
||||
*/
|
||||
WarpSegment( float s, float b ):
|
||||
m_iStartRow(max(0, BeatToNoteRow((s < b ? s : b)))),
|
||||
m_fEndBeat(max(0, (b > s ? b : s))) {}
|
||||
/**
|
||||
* @brief The row in which the WarpSegment activates.
|
||||
*/
|
||||
@@ -344,7 +360,7 @@ struct WarpSegment
|
||||
/**
|
||||
* @brief The beat to warp to.
|
||||
*/
|
||||
float m_fWarpBeats;
|
||||
float m_fEndBeat;
|
||||
/**
|
||||
* @brief Compares two WarpSegments to see if they are equal to each other.
|
||||
* @param other the other WarpSegment to compare to.
|
||||
@@ -353,7 +369,7 @@ struct WarpSegment
|
||||
bool operator==( const WarpSegment &other ) const
|
||||
{
|
||||
COMPARE( m_iStartRow );
|
||||
COMPARE( m_fWarpBeats );
|
||||
COMPARE( m_fEndBeat );
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
@@ -370,7 +386,7 @@ struct WarpSegment
|
||||
bool operator<( const WarpSegment &other ) const
|
||||
{
|
||||
return m_iStartRow < other.m_iStartRow ||
|
||||
( m_iStartRow == other.m_iStartRow && m_fWarpBeats < other.m_fWarpBeats );
|
||||
( m_iStartRow == other.m_iStartRow && m_fEndBeat < other.m_fEndBeat );
|
||||
}
|
||||
/**
|
||||
* @brief Compares two WarpSegments to see if one is less than or equal to the other.
|
||||
@@ -875,19 +891,71 @@ public:
|
||||
* @param seg the new TimeSignatureSegment.
|
||||
*/
|
||||
void AddTimeSignatureSegment( const TimeSignatureSegment &seg );
|
||||
|
||||
/**
|
||||
* @brief Determine the row to warp to.
|
||||
* @param iWarpBeginRow The row you start on.
|
||||
* @return the row you warp to.
|
||||
* @brief Determine the beat to warp to.
|
||||
* @param iRow The row you start on.
|
||||
* @return the beat you warp to.
|
||||
*/
|
||||
int GetWarpToRow( int iWarpBeginRow ) const;
|
||||
float GetWarpAtRow( int iRow ) const;
|
||||
/**
|
||||
* @brief Determine the beat to warp to.
|
||||
* @param fBeat The beat you start on.
|
||||
* @return the beat you warp to.
|
||||
*/
|
||||
float GetWarpAtBeat( float fBeat ) const { return GetWarpAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Set the beat to warp to given a starting row.
|
||||
* @param iRow The row to start on.
|
||||
* @param fNew The destination beat.
|
||||
*/
|
||||
void SetWarpAtRow( int iRow, float fNew );
|
||||
/**
|
||||
* @brief Set the beat to warp to given a starting beat.
|
||||
* @param fBeat The beat to start on.
|
||||
* @param fNew The destination beat.
|
||||
*/
|
||||
void SetWarpAtBeat( float fBeat, float fNew ) { SetWarpAtRow( BeatToNoteRow( fBeat ), fNew ); }
|
||||
/**
|
||||
* @brief Retrieve the WarpSegment at the specified row.
|
||||
* @param iRow the row to focus on.
|
||||
* @return the WarpSegment in question.
|
||||
*/
|
||||
WarpSegment& GetWarpSegmentAtRow( int iRow );
|
||||
/**
|
||||
* @brief Retrieve the WarpSegment at the specified beat.
|
||||
* @param fBeat the beat to focus on.
|
||||
* @return the WarpSegment in question.
|
||||
*/
|
||||
WarpSegment& GetWarpSegmentAtBeat( float fBeat ) { return GetWarpSegmentAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Retrieve the index of the WarpSegment at the specified row.
|
||||
* @param iRow the row to focus on.
|
||||
* @return the index in question.
|
||||
*/
|
||||
int GetWarpSegmentIndexAtRow( int iRow ) const;
|
||||
/**
|
||||
* @brief Retrieve the index of the WarpSegment at the specified beat.
|
||||
* @param fBeat the beat to focus on.
|
||||
* @return the index in question.
|
||||
*/
|
||||
int GetWarpSegmentIndexAtBeat( float fBeat ) const { return GetWarpSegmentIndexAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Checks if the row is inside a warp.
|
||||
* @param iRow the row to focus on.
|
||||
* @return true if the row is inside a warp, false otherwise.
|
||||
*/
|
||||
bool IsWarpAtRow( int iRow ) const;
|
||||
/**
|
||||
* @brief Checks if the beat is inside a warp.
|
||||
* @param fBeat the beat to focus on.
|
||||
* @return true if the row is inside a warp, false otherwise.
|
||||
*/
|
||||
bool IsWarpAtBeat( float fBeat ) const { return IsWarpAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Add the WarpSegment to the TimingData.
|
||||
* @param seg the new WarpSegment.
|
||||
*/
|
||||
void AddWarpSegment( const WarpSegment &seg );
|
||||
|
||||
/**
|
||||
* @brief Retrieve the Tickcount at the given row.
|
||||
* @param iNoteRow the row in question.
|
||||
@@ -1011,7 +1079,7 @@ public:
|
||||
}
|
||||
float GetElapsedTimeFromBeat( float fBeat ) const;
|
||||
|
||||
void GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &iWarpLengthOut ) const;
|
||||
void GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpDestinationOut ) const;
|
||||
float GetBeatFromElapsedTimeNoOffset( float fElapsedTime ) const // shortcut for places that care only about the beat
|
||||
{
|
||||
float fBeat, fThrowAway, fThrowAway2;
|
||||
|
||||
Reference in New Issue
Block a user