diff --git a/Docs/Changelog_SSCformat.txt b/Docs/Changelog_SSCformat.txt index 12cb56d7b8..20088bb92b 100644 --- a/Docs/Changelog_SSCformat.txt +++ b/Docs/Changelog_SSCformat.txt @@ -9,6 +9,11 @@ change to JSON, but it is unsure if this will be done. Implement .ssc at your own risk. ________________________________________________________________________________ +[v0.73] - Wolfman2000 +* Use #FIRSTSECOND, #LASTSECOND, and #LASTSECONDHINT instead of the BEAT + equivalents. +* Move #LASTSECONDHINT to after the Timing tags. + [v0.72] - Wolfman2000 * Have #OFFSET come before #ATTACKS in the Step data. diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 3becdfa38c..d2e2b127ef 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -5,9 +5,18 @@ from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. ________________________________________________________________________________ ================================================================================ -StepMania 5.0 Preview 2 | 201106?? +StepMania 5.0 Preview 2 | 201107?? -------------------------------------------------------------------------------- +2011/06/30 +---------- +* [NotesAll] Use #FIRSTSECOND, #LASTSECOND, and #LASTSECONDHINT instead of the + former BEAT equivalents. Split Timing has made this a necessity thanks to + certain files that abuse Stops/Delays more than usual. [Wolfman2000] +* [ScreenEdit] Add a function in the Area Menu to designate the current beat + as the last second of the song. Please try to be smart and not use this too + early. [Wolfman2000] + 2011/06/28 ---------- * [ScreenEdit] Add a function to copy/paste the TimingData of a player's diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 1785b00b94..ed192118a5 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1244,9 +1244,11 @@ + + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 2098898a41..40d229921d 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -3084,6 +3084,9 @@ Returns the first beat of the song. + + Returns the first second of the song. + Returns the genre of the song. @@ -3093,6 +3096,9 @@ Returns the last beat of the song. + + Returns the last second of the song. + Gets the path to the lyrics. diff --git a/Docs/SimfileFormats/ssc_msd5.txt b/Docs/SimfileFormats/ssc_msd5.txt index 119f61b297..9c00273a64 100644 --- a/Docs/SimfileFormats/ssc_msd5.txt +++ b/Docs/SimfileFormats/ssc_msd5.txt @@ -21,11 +21,11 @@ #DELAYS:; #LABELS:; #TIMESIGNATURES:; -#LASTBEATHINT:; #SAMPLESTART:; #SAMPLELENGTH:; #DISPLAYBPM:[xxx][xxx:xxx]|[*]; #SELECTABLE:; +#LASTBEATHINT:; #BGCHANGES:; #FGCHANGES:; #KEYSOUNDS:; diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 1c796abc6b..adbb6deadc 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -838,6 +838,7 @@ Delete=Delete Delete beat and shift up=Delete beat and shift up Description=Description Designate as Music Preview=Designate as Music Preview +Designate last second at current beat=Designate last second at current beat Difficulty=Difficulty Display BPM=Display BPM DisplayAspectRatio=Aspect Ratio @@ -916,7 +917,7 @@ Insert Entry=Insert Entry Jumps=Jumps Key Joy Mappings=Config Key/Joy Mappings Language=Language -Last beat hint=Last beat hint +Last second hint=Last second hint Layer=Layer LifeDifficulty=Life Difficulty LifeType=Life Type @@ -1256,7 +1257,7 @@ Enter a new chart style.=Enter a new chart style (e.g. "Pad", "Keyboard"). Enter a new meter.=Enter the difficulty rating for this chart. Enter the author who made this step pattern.=Enter the author who made this step pattern. Enter the offset for the song.=Enter the beat 0 offset for the song. -Enter a new last beat hint.=Enter a new last beat hint. +Enter a new last second hint.=Enter a new last second hint. Enter a new main title transliteration.=Enter a new main title transliteration. Enter a new main title.=Enter a new main title. Enter a new sub title transliteration.=Enter a new sub title transliteration. diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 127d5862d7..6054b15099 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -3650,12 +3650,12 @@ PlayRecordHelpOnCommand=halign,0;valign,0;shadowlength,1 LoopOnChartEnd=true -CurrentBeatFormat="%s:\n %.5f\n" -CurrentSecondFormat="%s:\n %.5f\n" +CurrentBeatFormat="%s:\n %.6f\n" +CurrentSecondFormat="%s:\n %.6f\n" SnapToFormat="%s: %s\n" -SelectionBeatBeginFormat="%s:\n %.5f" +SelectionBeatBeginFormat="%s:\n %.6f" SelectionBeatUnfinishedFormat=" ...\n" -SelectionBeatEndFormat="-%.5f\n" +SelectionBeatEndFormat="-%.6f\n" DifficultyFormat="%s:\n %s\n" RoutinePlayerFormat="%s:\n %d\n" DescriptionFormat="%s:\n %s\n" @@ -3674,9 +3674,9 @@ NumRollsFormat="%s: %d\n" NumLiftsFormat="%s: %d\n" NumFakesFormat="%s: %d\n" TimingModeFormat="%s:\n %s\n" -Beat0OffsetFormat="%s:\n %.5f secs\n" -PreviewStartFormat="%s:\n %.5f secs\n" -PreviewLengthFormat="%s:\n %.5f secs\n" +Beat0OffsetFormat="%s:\n %.6f secs\n" +PreviewStartFormat="%s:\n %.6f secs\n" +PreviewLengthFormat="%s:\n %.6f secs\n" [ScreenPracticeMenu] Fallback="ScreenEditMenu" diff --git a/Themes/default/BGAnimations/ScreenGameplay decorations/default.lua b/Themes/default/BGAnimations/ScreenGameplay decorations/default.lua index 6fa5c4a506..815b30b141 100644 --- a/Themes/default/BGAnimations/ScreenGameplay decorations/default.lua +++ b/Themes/default/BGAnimations/ScreenGameplay decorations/default.lua @@ -1,8 +1,13 @@ -local function CreateStops(Player) +local function CreateSegments(Player) local t = Def.ActorFrame { }; local bars = Def.ActorFrame{ }; local bpmFrame = Def.ActorFrame{ Name="BPMFrame"; }; local stopFrame = Def.ActorFrame{ Name="StopFrame"; }; + local delayFrame = Def.ActorFrame{ Name="DelayFrame"; }; + local warpFrame = Def.ActorFrame{ Name="WarpFrame"; }; + local fakeFrame = Def.ActorFrame{ Name="FakeFrame"; }; + local scrollFrame = Def.ActorFrame{ Name="ScrollFrame"; }; + local speedFrame = Def.ActorFrame{ Name="SpeedFrame"; }; local fFrameWidth = 380; local fFrameHeight = 8; @@ -19,32 +24,32 @@ local function CreateStops(Player) if song then local songLen = song:MusicLengthSeconds(); - local firstBeatSecs = timingData:GetElapsedTimeFromBeat(song:GetFirstBeat()); - local lastBeatSecs = timingData:GetElapsedTimeFromBeat(song:GetLastBeat()); - - local bpms = timingData:GetBPMs(); + local firstBeatSecs = song:GetFirstSecond(); + local lastBeatSecs = song:GetLastSecond(); + local bpms = timingData:GetBPMsAndTimes(); local stops = timingData:GetStops(); - for i=1,#stops do - local data = split("=",stops[i]); - local beat = data[1]; - local secs = data[2]; + local delays = timingData:GetDelays(); + local warps = timingData:GetWarps(); + local fakes = timingData:GetFakes(); + local scrolls = timingData:GetScrolls(); + local speeds = timingData:GetSpeeds(); + + local function CreateLine(beat, secs, firstShadow, firstDiffuse, secondShadow, firstEffect, secondEffect) local beatTime = timingData:GetElapsedTimeFromBeat(beat); if beatTime < 0 then beatTime = 0; end; - - stopFrame[#stopFrame+1] = Def.ActorFrame { + return Def.ActorFrame { Def.Quad { InitCommand=function(self) - --self:diffuse(HSVA(192,1,0.8,0.8)); self:shadowlength(0); - self:shadowcolor( color("#FFFFFF77") ); + self:shadowcolor(color(firstShadow)); -- set width - self:zoomto( math.max((secs/songLen)*fFrameWidth, 1), fFrameHeight ); + self:zoomto(math.max((secs/songLen)*fFrameWidth, 1), fFrameHeight); -- find location - self:x( ( scale(beatTime, firstBeatSecs,lastBeatSecs, -fFrameWidth/2,fFrameWidth/2) ) ); + self:x((scale(beatTime,firstBeatSecs,lastBeatSecs,-fFrameWidth/2,fFrameWidth/2))); end; OnCommand=function(self) - self:diffuse(Color("White")); + self:diffuse(color(firstDiffuse)); self:sleep(beatTime+1); self:linear(2); self:diffusealpha(0); @@ -54,34 +59,80 @@ local function CreateStops(Player) InitCommand=function(self) --self:diffuse(HSVA(192,1,0.8,0.8)); self:shadowlength(0); - self:shadowcolor( color("#FFFFFF77") ); + self:shadowcolor(color(secondShadow)); -- set width - self:zoomto( math.max((secs/songLen)*fFrameWidth, 1), fFrameHeight ); + self:zoomto(math.max((secs/songLen)*fFrameWidth, 1),fFrameHeight); -- find location - self:x( ( scale(beatTime, firstBeatSecs,lastBeatSecs, -fFrameWidth/2,fFrameWidth/2) ) ); + self:x((scale(beatTime,firstBeatSecs,lastBeatSecs,-fFrameWidth/2,fFrameWidth/2))); end; OnCommand=function(self) self:diffusealpha(1); self:diffuseshift(); - self:effectcolor1(Color("Orange")); - self:effectcolor2(Color("Red")); + self:effectcolor1(color(firstEffect)); + self:effectcolor2(color(secondEffect)); self:effectclock('beat'); self:effectperiod(1/8); -- self:diffusealpha(0); self:sleep(beatTime+1); - -- self:linear(1); self:diffusealpha(1); self:linear(4); self:diffusealpha(0); end; }; - --OnCommand=cmd(diffuse,Color("White");sleep,math.min(0.00001+(secs-5),0.00001);linear,1;diffuse,Color("Orange");sleep,4;linear,2;diffusealpha,0); - --OnCommand=cmd(diffuse,Color("White");linear,1;diffuse,Color("Orange");); }; end; + + for i=1,#bpms do + local data = split("=",bpms[i]); + bpmFrame[#bpmFrame+1] = CreateLine(data[1], 0, + "#00808077", "#00808077", "#00808077", "#FF634777", "#FF000077"); + end; + + for i=1,#delays do + local data = split("=",delays[i]); + delayFrame[#delayFrame+1] = CreateLine(data[1], data[2], + "#FFFF0077", "#FFFF0077", "#FFFF0077", "#00FF0077", "#FF000077"); + end; + + for i=1,#stops do + local data = split("=",stops[i]); + stopFrame[#stopFrame+1] = CreateLine(data[1], data[2], + "#FFFFFF77", "#FFFFFF77", "#FFFFFF77", "#FFA50077", "#FF000077"); + end; + + for i=1,#scrolls do + local data = split("=",scrolls[i]); + scrollFrame[#scrollFrame+1] = CreateLine(data[1], 0, + "#4169E177", "#4169E177", "#4169E177", "#0000FF77", "#FF000077"); + end; + + for i=1,#speeds do + local data = split("=",speeds[i]); + -- TODO: Turn beats into seconds for this calculation? + speedFrame[#speedFrame+1] = CreateLine(data[1], 0, + "#ADFF2F77", "#ADFF2F77", "#ADFF2F77", "#7CFC0077", "#FF000077"); + end; + + for i=1,#warps do + local data = split("=",warps[i]); + warpFrame[#warpFrame+1] = CreateLine(data[1], 0, + "#CC00CC77", "#CC00CC77", "#CC00CC77", "#FF33CC77", "#FF000077"); + end; + + for i=1,#fakes do + local data = split("=",fakes[i]); + fakeFrame[#fakeFrame+1] = CreateLine(data[1], 0, + "#BC8F8F77", "#BC8F8F77", "#BC8F8F77", "#F4A46077", "#FF000077"); + end; end; + bars[#bars+1] = bpmFrame; + bars[#bars+1] = scrollFrame; + bars[#bars+1] = speedFrame; bars[#bars+1] = stopFrame; + bars[#bars+1] = delayFrame; + bars[#bars+1] = warpFrame; + bars[#bars+1] = fakeFrame; t[#t+1] = bars; end return t @@ -121,7 +172,7 @@ for pn in ivalues(PlayerNumber) do }; Tip=LoadActor( THEME:GetPathG( 'SongMeterDisplay', 'tip ' .. PlayerNumberToString(pn) ) ) .. { InitCommand=cmd(visible,false); }; }; - CreateStops(pn) .. { + CreateSegments(pn) .. { -- InitCommand=cmd(draworder,10); }; }; @@ -142,38 +193,6 @@ t[#t+1] = StandardDecorationFromFileOptional("BPMDisplay","BPMDisplay"); t[#t+1] = StandardDecorationFromFileOptional("StageDisplay","StageDisplay"); t[#t+1] = StandardDecorationFromFileOptional("SongTitle","SongTitle"); ---[[ t[#t+1] = Def.ActorFrame { - InitCommand=function(self) - self:name("SongMeterDisplay"); - ActorUtil.LoadAllCommandsAndSetXY(self,Var "LoadingScreen"); - end; - LoadActor( THEME:GetPathG( 'SongMeterDisplay', 'frame ' .. PlayerNumberToString(PLAYER_1) ) ) .. { - InitCommand=function(self) - self:name('Frame'); - ActorUtil.LoadAllCommandsAndSetXY(self,"SongMeterDisplay"); - end; - }; - Def.Quad { - InitCommand=cmd(zoomto,2,8); - OnCommand=cmd(x,scale(0.25,0,1,-380/2,380/2);diffuse,Color("Orange");diffusealpha,0.5); - }; - Def.Quad { - InitCommand=cmd(zoomto,2,8); - OnCommand=cmd(x,scale(0.5,0,1,-380/2,380/2);diffuse,Color("Orange");diffusealpha,0.5); - }; - Def.Quad { - InitCommand=cmd(zoomto,2,8); - OnCommand=cmd(x,scale(0.75,0,1,-380/2,380/2);diffuse,Color("Orange");diffusealpha,0.5); - }; - Def.SongMeterDisplay { - InitCommand=cmd(SetStreamWidth,THEME:GetMetric( "SongMeterDisplay", 'StreamWidth' )); - Stream=LoadActor( THEME:GetPathG( 'SongMeterDisplay', 'stream ' .. PlayerNumberToString(PLAYER_1) ) )..{ - InitCommand=cmd(diffuse,Color("Orange");diffusealpha,0.5;blend,Blend.Add;); - }; - Tip=LoadActor( THEME:GetPathG( 'SongMeterDisplay', 'tip ' .. PlayerNumberToString(PLAYER_1) ) ) .. { InitCommand=cmd(visible,false); }; - }; - CreateStops(); -}; --]] if( not GAMESTATE:IsCourseMode() ) then t[#t+1] = Def.Actor{ JudgmentMessageCommand = function(self, params) diff --git a/src/Attack.cpp b/src/Attack.cpp index 568b83ec4c..25a19415c9 100644 --- a/src/Attack.cpp +++ b/src/Attack.cpp @@ -104,6 +104,14 @@ vector AttackArray::ToVectorString() const return ret; } +void AttackArray::UpdateStartTimes(float delta) +{ + FOREACH(Attack, *this, a) + { + a->fStartSecond += delta; + } +} + /* * (c) 2003-2004 Chris Danford * All rights reserved. diff --git a/src/Attack.h b/src/Attack.h index 9e3ae45310..fe61a48723 100644 --- a/src/Attack.h +++ b/src/Attack.h @@ -76,6 +76,11 @@ struct AttackArray : public vector * @brief Return a string representation used for simfiles. * @return the string representation. */ vector ToVectorString() const; + + /** + * @brief Adjust the starting time of all attacks. + * @param delta the amount to change. */ + void UpdateStartTimes(float delta); }; #endif diff --git a/src/Background.cpp b/src/Background.cpp index 19e21abf7a..d5b14b308b 100644 --- a/src/Background.cpp +++ b/src/Background.cpp @@ -577,13 +577,15 @@ void BackgroundImpl::LoadFromSong( const Song* pSong ) else // pSong doesn't have an animation plan { Layer &layer = m_Layer[0]; + float firstBeat = pSong->GetFirstBeat(); + float lastBeat = pSong->GetLastBeat(); - LoadFromRandom( pSong->m_fFirstBeat, pSong->m_fLastBeat, BackgroundChange() ); + LoadFromRandom( firstBeat, lastBeat, BackgroundChange() ); // end showing the static song background BackgroundChange change; change.m_def = m_StaticBackgroundDef; - change.m_fStartBeat = pSong->m_fLastBeat; + change.m_fStartBeat = lastBeat; layer.m_aBGChanges.push_back( change ); } @@ -642,7 +644,7 @@ void BackgroundImpl::LoadFromSong( const Song* pSong ) continue; float fStartBeat = change.m_fStartBeat; - float fEndBeat = pSong->m_fLastBeat; + float fEndBeat = pSong->GetLastBeat(); if( i+1 < mainlayer.m_aBGChanges.size() ) fEndBeat = mainlayer.m_aBGChanges[i+1].m_fStartBeat; diff --git a/src/DancingCharacters.cpp b/src/DancingCharacters.cpp index 08088290eb..403277580c 100644 --- a/src/DancingCharacters.cpp +++ b/src/DancingCharacters.cpp @@ -166,7 +166,7 @@ void DancingCharacters::LoadNextSong() m_fThisCameraEndBeat = 0; ASSERT( GAMESTATE->m_pCurSong ); - m_fThisCameraEndBeat = GAMESTATE->m_pCurSong->m_fFirstBeat; + m_fThisCameraEndBeat = GAMESTATE->m_pCurSong->GetFirstBeat(); FOREACH_PlayerNumber( p ) if( GAMESTATE->IsPlayerEnabled(p) ) @@ -212,9 +212,9 @@ void DancingCharacters::Update( float fDelta ) bWasGameplayStarting = bGameplayStarting; static float fLastBeat = GAMESTATE->m_Position.m_fSongBeat; + float firstBeat = GAMESTATE->m_pCurSong->GetFirstBeat(); float fThisBeat = GAMESTATE->m_Position.m_fSongBeat; - if( fLastBeat < GAMESTATE->m_pCurSong->m_fFirstBeat && - fThisBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat ) + if( fLastBeat < firstBeat && fThisBeat >= firstBeat ) { FOREACH_PlayerNumber( p ) m_pCharacter[p]->PlayAnimation( "dance" ); diff --git a/src/GameState.cpp b/src/GameState.cpp index fe0d7099de..bf9a841bd8 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -991,7 +991,8 @@ update player position code goes here float GameState::GetSongPercent( float beat ) const { // 0 = first step; 1 = last step - return (beat - m_pCurSong->m_fFirstBeat) / m_pCurSong->m_fLastBeat; + float curTime = this->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat(beat); + return (curTime - m_pCurSong->GetFirstSecond()) / m_pCurSong->GetLastSecond(); } int GameState::GetNumStagesLeft( PlayerNumber pn ) const diff --git a/src/Inventory.cpp b/src/Inventory.cpp index d3f563c30a..b45b779449 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -119,10 +119,10 @@ void Inventory::Update( float fDelta ) } } - + Song &song = *GAMESTATE->m_pCurSong; // use items if this player is CPU-controlled if( m_pPlayerState->m_PlayerController != PC_HUMAN && - GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat ) + GAMESTATE->m_Position.m_fSongBeat < song.GetLastBeat() ) { // every 1 seconds, try to use an item int iLastSecond = (int)(RageTimer::GetTimeSinceStartFast() - fDelta); diff --git a/src/LyricDisplay.cpp b/src/LyricDisplay.cpp index 7284d2dc17..b896814e1d 100644 --- a/src/LyricDisplay.cpp +++ b/src/LyricDisplay.cpp @@ -59,8 +59,8 @@ void LyricDisplay::Update( float fDeltaTime ) if( m_iCurLyricNumber+1 < GAMESTATE->m_pCurSong->m_LyricSegments.size() ) fEndTime = pSong->m_LyricSegments[m_iCurLyricNumber+1].m_fStartTime; else - fEndTime = pSong->m_SongTiming.GetElapsedTimeFromBeat( pSong->m_fLastBeat ); - + fEndTime = pSong->GetLastSecond(); + const float fDistance = fEndTime - pSong->m_LyricSegments[m_iCurLyricNumber].m_fStartTime; const float fTweenBufferTime = IN_LENGTH.GetValue() + OUT_LENGTH.GetValue(); diff --git a/src/MeterDisplay.cpp b/src/MeterDisplay.cpp index 641b50d73c..cd8c2faebb 100644 --- a/src/MeterDisplay.cpp +++ b/src/MeterDisplay.cpp @@ -73,8 +73,8 @@ void SongMeterDisplay::Update( float fDeltaTime ) { if( GAMESTATE->m_pCurSong ) { - float fSongStartSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fFirstBeat ); - float fSongEndSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat ); + float fSongStartSeconds = GAMESTATE->m_pCurSong->GetFirstSecond(); + float fSongEndSeconds = GAMESTATE->m_pCurSong->GetLastSecond(); float fPercentPositionSong = SCALE( GAMESTATE->m_Position.m_fMusicSeconds, fSongStartSeconds, fSongEndSeconds, 0.0f, 1.0f ); CLAMP( fPercentPositionSong, 0, 1 ); diff --git a/src/NotesLoaderJson.cpp b/src/NotesLoaderJson.cpp index 9d422d5177..dab70f29ee 100644 --- a/src/NotesLoaderJson.cpp +++ b/src/NotesLoaderJson.cpp @@ -150,8 +150,6 @@ static void Deserialize( Song &out, const Json::Value &root ) else if( sSelectable.EqualsNoCase("NO") ) out.m_SelectionDisplay = out.SHOW_NEVER; - out.m_fFirstBeat = (float)root["FirstBeat"].asDouble(); - out.m_fLastBeat = (float)root["LastBeat"].asDouble(); out.m_sSongFileName = root["SongFileName"].asString(); out.m_bHasMusic = root["HasMusic"].asBool(); out.m_bHasBanner = root["HasBanner"].asBool(); diff --git a/src/NotesLoaderSM.cpp b/src/NotesLoaderSM.cpp index fc5a59273e..ffe564269c 100644 --- a/src/NotesLoaderSM.cpp +++ b/src/NotesLoaderSM.cpp @@ -812,37 +812,19 @@ bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache } else if( sValueName=="LASTBEATHINT" ) - out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] ); + { + // unable to identify at this point: ignore + } else if( sValueName=="MUSICBYTES" ) ; /* ignore */ - /* We calculate these. Some SMs in circulation have bogus values for - * these, so make sure we always calculate it ourself. */ - else if( sValueName=="FIRSTBEAT" ) + // cache tags from older SM files: ignore. + else if(sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" || + sValueName=="SONGFILENAME" || sValueName=="HASMUSIC" || + sValueName=="HASBANNER") { - if( bFromCache ) - out.m_fFirstBeat = StringToFloat( sParams[1] ); - } - else if( sValueName=="LASTBEAT" ) - { - if( bFromCache ) - out.m_fLastBeat = StringToFloat( sParams[1] ); - } - else if( sValueName=="SONGFILENAME" ) - { - if( bFromCache ) - out.m_sSongFileName = sParams[1]; - } - else if( sValueName=="HASMUSIC" ) - { - if( bFromCache ) - out.m_bHasMusic = StringToInt( sParams[1] ) != 0; - } - else if( sValueName=="HASBANNER" ) - { - if( bFromCache ) - out.m_bHasBanner = StringToInt( sParams[1] ) != 0; + ; } else if( sValueName=="SAMPLESTART" ) @@ -1108,9 +1090,10 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache ) if( bFromCache ) break; + float lastBeat = song.GetLastBeat(); /* If BGChanges already exist after the last beat, don't add the * background in the middle. */ - if( !bg.empty() && bg.back().m_fStartBeat-0.0001f >= song.m_fLastBeat ) + if( !bg.empty() && bg.back().m_fStartBeat-0.0001f >= lastBeat ) break; // If the last BGA is already the song BGA, don't add a duplicate. @@ -1120,7 +1103,8 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache ) if( !IsAFile( song.GetBackgroundPath() ) ) break; - bg.push_back( BackgroundChange(song.m_fLastBeat,song.m_sBackgroundFile) ); + + bg.push_back( BackgroundChange(lastBeat,song.m_sBackgroundFile) ); } while(0); } song.TidyUpData( bFromCache ); diff --git a/src/NotesLoaderSMA.cpp b/src/NotesLoaderSMA.cpp index 648d54dcca..05db8d42b0 100644 --- a/src/NotesLoaderSMA.cpp +++ b/src/NotesLoaderSMA.cpp @@ -225,30 +225,16 @@ bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach } else if( sValueName=="LASTBEATHINT" ) - out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] ); - + { + // can't identify at this position: ignore. + } else if( sValueName=="MUSICBYTES" ) ; /* ignore */ - /* We calculate these. Some SMs in circulation have bogus values for - * these, so make sure we always calculate it ourself. */ - else if( sValueName=="FIRSTBEAT" ) - { - ; - } - else if( sValueName=="LASTBEAT" ) - { - ; - } - else if( sValueName=="SONGFILENAME" ) - { - ; - } - else if( sValueName=="HASMUSIC" ) - { - ; - } - else if( sValueName=="HASBANNER" ) + // Cache tags: ignore. + else if (sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" || + sValueName=="SONGFILENAME" || sValueName=="HASMUSIC" || + sValueName=="HASBANNER" ) { ; } diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index fe02b9181b..258d1ae5d7 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -265,7 +265,12 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach else if( sValueName=="LASTBEATHINT" ) { - out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] ); + // unable to parse due to tag position. Ignore. + } + + else if (sValueName == "LASTSECONDHINT") + { + out.SetSpecifiedLastSecond(StringToFloat(sParams[1])); } else if( sValueName=="MUSICBYTES" ) @@ -395,16 +400,20 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach /* The following are cache tags. Never fill their values * directly: only from the cached version. */ - else if( sValueName=="FIRSTBEAT" ) + else if( sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" ) + { + // no longer used. + } + else if (sValueName=="FIRSTSECOND") { if( bFromCache ) - out.m_fFirstBeat = StringToFloat( sParams[1] ); + out.SetFirstSecond(StringToFloat(sParams[1])); } - else if( sValueName=="LASTBEAT" ) + else if( sValueName=="LASTSECOND" ) { if( bFromCache ) - out.m_fLastBeat = StringToFloat( sParams[1] ); + out.SetLastSecond(StringToFloat(sParams[1])); } else if( sValueName=="SONGFILENAME" ) diff --git a/src/NotesWriterJson.cpp b/src/NotesWriterJson.cpp index e5ad6338d9..8320c3d4b9 100644 --- a/src/NotesWriterJson.cpp +++ b/src/NotesWriterJson.cpp @@ -147,8 +147,8 @@ bool NotesWriterJson::WriteSong( const RString &sFile, const Song &out, bool bWr else root["Selectable"] = "YES"; - root["FirstBeat"] = out.m_fFirstBeat; - root["LastBeat"] = out.m_fLastBeat; + root["FirstBeat"] = out.GetFirstBeat(); + root["LastBeat"] = out.GetLastBeat(); root["SongFileName"] = out.m_sSongFileName; root["HasMusic"] = out.m_bHasMusic; root["HasBanner"] = out.m_bHasBanner; diff --git a/src/NotesWriterSM.cpp b/src/NotesWriterSM.cpp index 6b9f89c1fe..48db17d8ff 100644 --- a/src/NotesWriterSM.cpp +++ b/src/NotesWriterSM.cpp @@ -66,8 +66,9 @@ static void WriteGlobalTags( RageFile &f, Song &out ) f.PutLine( ssprintf( "#OFFSET:%.3f;", out.m_SongTiming.m_fBeat0OffsetInSeconds ) ); f.PutLine( ssprintf( "#SAMPLESTART:%.3f;", out.m_fMusicSampleStartSeconds ) ); f.PutLine( ssprintf( "#SAMPLELENGTH:%.3f;", out.m_fMusicSampleLengthSeconds ) ); - if( out.m_fSpecifiedLastBeat > 0 ) - f.PutLine( ssprintf("#LASTBEATHINT:%.3f;", out.m_fSpecifiedLastBeat) ); + float specBeat = out.GetSpecifiedLastBeat(); + if( specBeat > 0 ) + f.PutLine( ssprintf("#LASTBEATHINT:%.3f;", specBeat) ); f.Write( "#SELECTABLE:" ); switch(out.m_SelectionDisplay) diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp index dc9c63a5a2..bad8a1690e 100644 --- a/src/NotesWriterSSC.cpp +++ b/src/NotesWriterSSC.cpp @@ -203,8 +203,6 @@ static void WriteGlobalTags( RageFile &f, const Song &out ) f.PutLine( ssprintf( "#OFFSET:%.6f;", out.m_SongTiming.m_fBeat0OffsetInSeconds ) ); f.PutLine( ssprintf( "#SAMPLESTART:%.6f;", out.m_fMusicSampleStartSeconds ) ); f.PutLine( ssprintf( "#SAMPLELENGTH:%.6f;", out.m_fMusicSampleLengthSeconds ) ); - if( out.m_fSpecifiedLastBeat > 0 ) - f.PutLine( ssprintf("#LASTBEATHINT:%.6f;", out.m_fSpecifiedLastBeat) ); f.Write( "#SELECTABLE:" ); switch(out.m_SelectionDisplay) @@ -234,6 +232,9 @@ static void WriteGlobalTags( RageFile &f, const Song &out ) WriteTimingTags( f, out.m_SongTiming, true ); + if( out.GetSpecifiedLastSecond() > 0 ) + f.PutLine( ssprintf("#LASTSECONDHINT:%.6f;", out.GetSpecifiedLastSecond()) ); + FOREACH_BackgroundLayer( b ) { if( b==0 ) @@ -367,12 +368,12 @@ bool NotesWriterSSC::Write( RString sPath, const Song &out, const vector if( bSavingCache ) { f.PutLine( ssprintf( "// cache tags:" ) ); - f.PutLine( ssprintf( "#FIRSTBEAT:%.3f;", out.m_fFirstBeat ) ); - f.PutLine( ssprintf( "#LASTBEAT:%.3f;", out.m_fLastBeat ) ); + f.PutLine( ssprintf( "#FIRSTSECOND:%.6f;", out.GetFirstSecond() ) ); + f.PutLine( ssprintf( "#LASTSECOND:%.6f;", out.GetLastSecond() ) ); f.PutLine( ssprintf( "#SONGFILENAME:%s;", out.m_sSongFileName.c_str() ) ); f.PutLine( ssprintf( "#HASMUSIC:%i;", out.m_bHasMusic ) ); f.PutLine( ssprintf( "#HASBANNER:%i;", out.m_bHasBanner ) ); - f.PutLine( ssprintf( "#MUSICLENGTH:%.3f;", out.m_fMusicLengthSeconds ) ); + f.PutLine( ssprintf( "#MUSICLENGTH:%.6f;", out.m_fMusicLengthSeconds ) ); f.PutLine( ssprintf( "// end cache tags" ) ); } diff --git a/src/Player.cpp b/src/Player.cpp index b9f3c82d11..98c19b8d30 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -115,7 +115,7 @@ void TimingWindowSecondsInit( size_t /*TimingWindow*/ i, RString &sNameOut, floa case TW_W5: defaultValueOut = 0.180f; break; case TW_Mine: defaultValueOut = 0.090f; break; // same as great case TW_Hold: defaultValueOut = 0.500f; break; // allow enough time to take foot off and put back on - case TW_Roll: defaultValueOut = 0.350f; break; + case TW_Roll: defaultValueOut = 0.500f; break; case TW_Attack: defaultValueOut = 0.135f; break; } } diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 55660d2c05..ed8bebe2d4 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -581,8 +581,9 @@ static MenuDef g_AreaMenu( EditMode_Full, true, true, 0, NULL ), MenuRowDef(ScreenEdit::convert_delay_to_beat, "Convert delay to beats", true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::last_second_at_beat, "Designate last second at current beat", 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, + MenuRowDef(ScreenEdit::clear_clipboard, "Clear clipboard", true, EditMode_Practice, true, true, 0, NULL ) ); @@ -618,7 +619,7 @@ static MenuDef g_SongInformation( MenuRowDef( ScreenEdit::main_title_transliteration, "Main title transliteration", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::sub_title_transliteration, "Sub title transliteration", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::artist_transliteration, "Artist transliteration", true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::last_beat_hint, "Last beat hint", true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::last_second_hint, "Last second hint", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::preview_start, "Preview Start", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::preview_length, "Preview Length", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::display_bpm, "Display BPM", true, EditMode_Full, true, true, 0, "Actual", "Specified", "Random" ), @@ -1935,6 +1936,14 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) } GetAppropriateTiming().m_fBeat0OffsetInSeconds += fDelta; (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); + if (GAMESTATE->m_bIsUsingStepTiming) + { + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.UpdateStartTimes(fDelta); + } + else + { + GAMESTATE->m_pCurSong->m_Attacks.UpdateStartTimes(fDelta); + } SetDirty( true ); } break; @@ -2127,7 +2136,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) if (index >= 0) { - const RString sDuration = ssprintf( "%.5f", attacks[index].fSecsRemaining ); + const RString sDuration = ssprintf( "%.6f", attacks[index].fSecsRemaining ); g_InsertCourseAttack.rows[remove].bEnabled = true; if( g_InsertCourseAttack.rows[duration].choices.size() == 9 ) @@ -2158,7 +2167,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) if( iAttack >= 0 ) { - const RString sDuration = ssprintf( "%.5f", ce.attacks[iAttack].fSecsRemaining ); + const RString sDuration = ssprintf( "%.6f", ce.attacks[iAttack].fSecsRemaining ); g_InsertCourseAttack.rows[remove].bEnabled = true; if( g_InsertCourseAttack.rows[duration].choices.size() == 9 ) @@ -3426,13 +3435,26 @@ static void ChangeArtistTranslit( const RString &sNew ) static void ChangeBeat0Offset( const RString &sNew ) { - TimingData &timing = GAMESTATE->m_bIsUsingStepTiming ? GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing : GAMESTATE->m_pCurSong->m_SongTiming; + TimingData &timing = (GAMESTATE->m_bIsUsingStepTiming ? + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing : + GAMESTATE->m_pCurSong->m_SongTiming); + float old = timing.m_fBeat0OffsetInSeconds; timing.m_fBeat0OffsetInSeconds = StringToFloat( sNew ); + float delta = timing.m_fBeat0OffsetInSeconds - old; + if (GAMESTATE->m_bIsUsingStepTiming) + { + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.UpdateStartTimes(delta); + } + else + { + GAMESTATE->m_pCurSong->m_Attacks.UpdateStartTimes(delta); + } } -static void ChangeLastBeatHint( const RString &sNew ) +static void ChangeLastSecondHint( const RString &sNew ) { - GAMESTATE->m_pCurSong->m_fSpecifiedLastBeat = StringToFloat( sNew ); + Song &s = *GAMESTATE->m_pCurSong; + s.SetSpecifiedLastSecond(StringToFloat(sNew)); } static void ChangePreviewStart( const RString &sNew ) @@ -3507,23 +3529,23 @@ void ScreenEdit::DisplayTimingMenu() TimingData &pTime = GetAppropriateTiming(); bool bHasSpeedOnThisRow = pTime.GetSpeedSegmentAtBeat( fBeat ).GetBeat() == fBeat; - g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( ssprintf("%.5f", pTime.m_fBeat0OffsetInSeconds) ); - g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetBPMAtBeat( fBeat ) ) ); - g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetStopAtBeat( fBeat ) ) ) ; - g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetDelayAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( ssprintf("%.6f", pTime.m_fBeat0OffsetInSeconds) ); + g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetBPMAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetStopAtBeat( fBeat ) ) ) ; + g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetDelayAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[time_signature].SetOneUnthemedChoice( ssprintf("%d / %d", pTime.GetTimeSignatureNumeratorAtBeat( fBeat ), pTime.GetTimeSignatureDenominatorAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[label].SetOneUnthemedChoice( pTime.GetLabelAtBeat( fBeat ).c_str() ); 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 ) ) ); - g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.5f", pTime.GetSpeedPercentAtBeat( fBeat ) ) : "---" ); - g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.5f", pTime.GetSpeedWaitAtBeat( fBeat ) ) : "---" ); + g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetWarpAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.6f", pTime.GetSpeedPercentAtBeat( fBeat ) ) : "---" ); + g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.6f", pTime.GetSpeedWaitAtBeat( fBeat ) ) : "---" ); RString starting = ( pTime.GetSpeedModeAtBeat( fBeat ) == 1 ? "Seconds" : "Beats" ); g_TimingDataInformation.rows[speed_mode].SetOneUnthemedChoice( starting.c_str() ); - g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetScrollAtBeat( fBeat ) ) ); - g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetFakeAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetScrollAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetFakeAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[tickcount].bEnabled = GAMESTATE->m_bIsUsingStepTiming; g_TimingDataInformation.rows[combo].bEnabled = GAMESTATE->m_bIsUsingStepTiming; @@ -3733,12 +3755,12 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAns g_SongInformation.rows[main_title_transliteration].SetOneUnthemedChoice( pSong->m_sMainTitleTranslit ); g_SongInformation.rows[sub_title_transliteration].SetOneUnthemedChoice( pSong->m_sSubTitleTranslit ); g_SongInformation.rows[artist_transliteration].SetOneUnthemedChoice( pSong->m_sArtistTranslit ); - g_SongInformation.rows[last_beat_hint].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fSpecifiedLastBeat) ); - g_SongInformation.rows[preview_start].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fMusicSampleStartSeconds) ); - g_SongInformation.rows[preview_length].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fMusicSampleLengthSeconds) ); + g_SongInformation.rows[last_second_hint].SetOneUnthemedChoice( ssprintf("%.6f", pSong->GetSpecifiedLastSecond()) ); + g_SongInformation.rows[preview_start].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fMusicSampleStartSeconds) ); + g_SongInformation.rows[preview_length].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fMusicSampleLengthSeconds) ); g_SongInformation.rows[display_bpm].iDefaultChoice = pSong->m_DisplayBPMType; - g_SongInformation.rows[min_bpm].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fSpecifiedBPMMin) ); - g_SongInformation.rows[max_bpm].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fSpecifiedBPMMax) ); + g_SongInformation.rows[min_bpm].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fSpecifiedBPMMin) ); + g_SongInformation.rows[max_bpm].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fSpecifiedBPMMax) ); EditMiniMenu( &g_SongInformation, SM_BackFromSongInformation ); } @@ -4126,6 +4148,13 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns timing.InsertRows(GetRow(), BeatToNoteRow(pauseBeats)); break; } + case last_second_at_beat: + { + TimingData &timing = GetAppropriateTiming(); + Song &s = *GAMESTATE->m_pCurSong; + s.SetSpecifiedLastSecond(timing.GetElapsedTimeFromBeat(GetBeat())); + break; + } case undo: Undo(); break; @@ -4206,7 +4235,7 @@ static LocalizedString ENTER_CREDIT ("ScreenEdit","Enter a new credit."); static LocalizedString ENTER_MAIN_TITLE_TRANSLIT ("ScreenEdit","Enter a new main title transliteration."); static LocalizedString ENTER_SUB_TITLE_TRANSLIT ("ScreenEdit","Enter a new sub title transliteration."); static LocalizedString ENTER_ARTIST_TRANSLIT ("ScreenEdit","Enter a new artist transliteration."); -static LocalizedString ENTER_LAST_BEAT_HINT ("ScreenEdit","Enter a new last beat hint."); +static LocalizedString ENTER_LAST_SECOND_HINT ("ScreenEdit","Enter a new last second hint."); static LocalizedString ENTER_PREVIEW_START ("ScreenEdit","Enter a new preview start."); static LocalizedString ENTER_PREVIEW_LENGTH ("ScreenEdit","Enter a new preview length."); static LocalizedString ENTER_MIN_BPM ("ScreenEdit","Enter a new min BPM."); @@ -4242,29 +4271,29 @@ void ScreenEdit::HandleSongInformationChoice( SongInformationChoice c, const vec case artist_transliteration: ScreenTextEntry::TextEntry( SM_None, ENTER_ARTIST_TRANSLIT, pSong->m_sArtistTranslit, 100, NULL, ChangeArtistTranslit, NULL ); break; - case last_beat_hint: - ScreenTextEntry::TextEntry( SM_None, ENTER_LAST_BEAT_HINT, - ssprintf("%.5f", pSong->m_fSpecifiedLastBeat), 20, - ScreenTextEntry::FloatValidate, ChangeLastBeatHint, NULL ); + case last_second_hint: + ScreenTextEntry::TextEntry( SM_None, ENTER_LAST_SECOND_HINT, + ssprintf("%.6f", pSong->GetSpecifiedLastSecond()), 20, + ScreenTextEntry::FloatValidate, ChangeLastSecondHint, NULL ); break; case preview_start: ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_START, - ssprintf("%.5f", pSong->m_fMusicSampleStartSeconds), 20, + ssprintf("%.6f", pSong->m_fMusicSampleStartSeconds), 20, ScreenTextEntry::FloatValidate, ChangePreviewStart, NULL ); break; case preview_length: ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_LENGTH, - ssprintf("%.5f", pSong->m_fMusicSampleLengthSeconds), 20, + ssprintf("%.6f", pSong->m_fMusicSampleLengthSeconds), 20, ScreenTextEntry::FloatValidate, ChangePreviewLength, NULL ); break; case min_bpm: ScreenTextEntry::TextEntry( SM_None, ENTER_MIN_BPM, - ssprintf("%.5f", pSong->m_fSpecifiedBPMMin), 20, + ssprintf("%.6f", pSong->m_fSpecifiedBPMMin), 20, ScreenTextEntry::FloatValidate, ChangeMinBPM, NULL ); break; case max_bpm: ScreenTextEntry::TextEntry( SM_None, ENTER_MAX_BPM, - ssprintf("%.5f", pSong->m_fSpecifiedBPMMax), 20, + ssprintf("%.6f", pSong->m_fSpecifiedBPMMax), 20, ScreenTextEntry::FloatValidate, ChangeMaxBPM, NULL ); break; }; @@ -4293,7 +4322,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice DEFAULT_FAIL( c ); case beat_0_offset: ScreenTextEntry::TextEntry( SM_None, ENTER_BEAT_0_OFFSET, - ssprintf("%.5f", GetAppropriateTiming().m_fBeat0OffsetInSeconds), 20, + ssprintf("%.6f", GetAppropriateTiming().m_fBeat0OffsetInSeconds), 20, ScreenTextEntry::FloatValidate, ChangeBeat0Offset, NULL ); break; case bpm: @@ -4359,7 +4388,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromWarpChange, ENTER_WARP_VALUE, - ssprintf( "%.4f", GetAppropriateTiming().GetWarpAtBeat( GetBeat() ) ), + ssprintf( "%.6f", GetAppropriateTiming().GetWarpAtBeat( GetBeat() ) ), 10 ); break; @@ -4367,7 +4396,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromSpeedPercentChange, ENTER_SPEED_PERCENT_VALUE, - ssprintf( "%.5f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetRatio() ), + ssprintf( "%.6f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetRatio() ), 10 ); break; @@ -4375,7 +4404,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromScrollChange, ENTER_SCROLL_VALUE, - ssprintf( "%.5f", GetAppropriateTiming().GetScrollSegmentAtBeat( GetBeat() ).GetRatio() ), + ssprintf( "%.6f", GetAppropriateTiming().GetScrollSegmentAtBeat( GetBeat() ).GetRatio() ), 10 ); break; @@ -4383,7 +4412,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromSpeedWaitChange, ENTER_SPEED_WAIT_VALUE, - ssprintf( "%.5f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetLength() ), + ssprintf( "%.6f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetLength() ), 10 ); break; @@ -4403,7 +4432,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromFakeChange, ENTER_FAKE_VALUE, - ssprintf("%.5f", GetAppropriateTiming().GetFakeAtBeat( GetBeat() ) ), + ssprintf("%.6f", GetAppropriateTiming().GetFakeAtBeat( GetBeat() ) ), 10 ); break; @@ -4701,14 +4730,15 @@ float ScreenEdit::GetMaximumBeatForNewNote() const case EditMode_CourseMods: case EditMode_Home: { - float fEndBeat = GAMESTATE->m_pCurSong->m_fLastBeat; + Song &s = *GAMESTATE->m_pCurSong; + float fEndBeat = s.GetLastBeat(); /* Round up to the next measure end. Some songs end on weird beats * mid-measure, and it's odd to have movement capped to these weird * beats. */ - TimingData &songTiming = GAMESTATE->m_pCurSong->m_SongTiming; + TimingData &timing = s.m_SongTiming; float playerBeat = GetAppropriatePosition().m_fSongBeat; - int beatsPerMeasure = songTiming.GetTimeSignatureSegmentAtBeat( playerBeat ).GetNum(); + int beatsPerMeasure = timing.GetTimeSignatureSegmentAtBeat( playerBeat ).GetNum(); fEndBeat += beatsPerMeasure; fEndBeat = ftruncf( fEndBeat, (float)beatsPerMeasure ); @@ -4723,7 +4753,7 @@ float ScreenEdit::GetMaximumBeatForMoving() const { float fEndBeat = GetMaximumBeatForNewNote(); - /* Jump to GetLastBeat even if it's past m_pCurSong->m_fLastBeat + /* Jump to GetLastBeat even if it's past the song's last beat * so that users can delete garbage steps past then end that they have * have inserted in a text editor. Once they delete all steps on * GetLastBeat() and move off of that beat, they won't be able to return. */ diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 255cf2b425..a2392a83e6 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -421,6 +421,7 @@ public: shift_pauses_backward, convert_pause_to_beat, convert_delay_to_beat, + last_second_at_beat, undo, clear_clipboard, NUM_AREA_MENU_CHOICES @@ -529,7 +530,7 @@ public: main_title_transliteration, sub_title_transliteration, artist_transliteration, - last_beat_hint, + last_second_hint, preview_start, preview_length, display_bpm, diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index 9936268534..7cb93d1113 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -1329,8 +1329,7 @@ void ScreenGameplay::StartPlayingSong( float fMinTimeToNotes, float fMinTimeToMu p.StopMode = RageSoundParams::M_CONTINUE; { - const float fFirstBeat = GAMESTATE->m_pCurSong->m_fFirstBeat; - const float fFirstSecond = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( fFirstBeat ); + const float fFirstSecond = GAMESTATE->m_pCurSong->GetFirstSecond(); float fStartDelay = fMinTimeToNotes - fFirstSecond; fStartDelay = max( fStartDelay, fMinTimeToMusic ); p.m_StartSecond = -fStartDelay; @@ -1412,8 +1411,8 @@ void ScreenGameplay::PlayAnnouncer( RString type, float fSeconds ) /* Don't play before the first beat, or after we're finished. */ if( m_DancingState != STATE_DANCING ) return; - if( GAMESTATE->m_pCurSong == NULL || // this will be true on ScreenDemonstration sometimes - GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat ) + if(GAMESTATE->m_pCurSong == NULL || // this will be true on ScreenDemonstration sometimes + GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->GetFirstBeat()) return; if( m_fTimeSinceLastDancingComment < fSeconds ) @@ -1488,7 +1487,7 @@ bool ScreenGameplay::AllAreFailing() void ScreenGameplay::GetMusicEndTiming( float &fSecondsToStartFadingOutMusic, float &fSecondsToStartTransitioningOut ) { - float fLastStepSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat ); + float fLastStepSeconds = GAMESTATE->m_pCurSong->GetLastSecond(); fLastStepSeconds += Player::GetMaxStepDistanceSeconds(); float fTransitionLength; @@ -1696,7 +1695,10 @@ void ScreenGameplay::Update( float fDeltaTime ) // update fGameplaySeconds STATSMAN->m_CurStageStats.m_fGameplaySeconds += fUnscaledDeltaTime; - if( GAMESTATE->m_Position.m_fSongBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat && GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat ) + float curBeat = GAMESTATE->m_Position.m_fSongBeat; + Song &s = *GAMESTATE->m_pCurSong; + + if( curBeat >= s.GetFirstBeat() && curBeat < s.GetLastBeat() ) { STATSMAN->m_CurStageStats.m_fStepsSeconds += fUnscaledDeltaTime; @@ -1977,7 +1979,8 @@ void ScreenGameplay::UpdateLights() } // Before the first beat of the song, all cabinet lights solid on (except for menu buttons). - bool bOverrideCabinetBlink = (GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat); + Song &s = *GAMESTATE->m_pCurSong; + bool bOverrideCabinetBlink = (GAMESTATE->m_Position.m_fSongBeat < s.GetFirstBeat()); FOREACH_CabinetLight( cl ) bBlinkCabinetLight[cl] |= bOverrideCabinetBlink; diff --git a/src/Song.cpp b/src/Song.cpp index caf5f08315..87eaf91058 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 = 185; +const int FILE_CACHE_VERSION = 187; /** @brief How long does a song sample last by default? */ const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; @@ -69,9 +69,9 @@ Song::Song() m_fMusicSampleStartSeconds = -1; m_fMusicSampleLengthSeconds = DEFAULT_MUSIC_SAMPLE_LENGTH; m_fMusicLengthSeconds = 0; - m_fFirstBeat = -1; - m_fLastBeat = -1; - m_fSpecifiedLastBeat = -1; + firstSecond = -1; + lastSecond = -1; + specifiedLastSecond = -1; m_SelectionDisplay = SHOW_ALWAYS; m_bEnabled = true; m_DisplayBPMType = DISPLAY_BPM_ACTUAL; @@ -100,6 +100,51 @@ void Song::DetachSteps() m_vpStepsByType[st].clear(); } +float Song::GetFirstSecond() const +{ + return this->firstSecond; +} + +float Song::GetFirstBeat() const +{ + return this->m_SongTiming.GetBeatFromElapsedTime(this->firstSecond); +} + +float Song::GetLastSecond() const +{ + return this->lastSecond; +} + +float Song::GetLastBeat() const +{ + return this->m_SongTiming.GetBeatFromElapsedTime(this->lastSecond); +} + +float Song::GetSpecifiedLastSecond() const +{ + return this->specifiedLastSecond; +} + +float Song::GetSpecifiedLastBeat() const +{ + return this->m_SongTiming.GetBeatFromElapsedTime(this->specifiedLastSecond); +} + +void Song::SetFirstSecond(const float f) +{ + this->firstSecond = f; +} + +void Song::SetLastSecond(const float f) +{ + this->lastSecond = f; +} + +void Song::SetSpecifiedLastSecond(const float f) +{ + this->specifiedLastSecond = f; +} + // Reset to an empty song. void Song::Reset() { @@ -508,7 +553,8 @@ void Song::TidyUpData( bool bFromCache ) if( m_fMusicSampleStartSeconds+m_fMusicSampleLengthSeconds > this->m_fMusicLengthSeconds ) { - int iBeat = lrintf( m_fLastBeat/2 ); + // Attempt to get a reasonable default. + int iBeat = lrintf(this->m_SongTiming.GetBeatFromElapsedTime(this->GetLastSecond())/2); iBeat -= iBeat%4; m_fMusicSampleStartSeconds = timing.GetElapsedTimeFromBeat( (float)iBeat ); } @@ -797,7 +843,7 @@ void Song::TranslateTitles() void Song::ReCalculateRadarValuesAndLastBeat( bool bFromCache ) { - if( bFromCache && m_fFirstBeat >= 0 && m_fLastBeat > 0 ) + if( bFromCache && this->GetFirstSecond() >= 0 && this->GetLastSecond() > 0 ) { // this is loaded from cache, then we just have to calculate the radar values. for( unsigned i=0; ispecifiedLastSecond; // Make sure we're at least as long as the specified amount. for( unsigned i=0; iCalculateRadarValues( m_fMusicLengthSeconds ); - // calculate lastBeat + // calculate lastSecond // If it's autogen, then first/last beat will come from the parent. if( pSteps->IsAutogen() ) @@ -839,12 +885,14 @@ void Song::ReCalculateRadarValuesAndLastBeat( bool bFromCache ) if( tempNoteData.GetLastRow() == 0 ) continue; - fFirstBeat = min( fFirstBeat, m_SongTiming.GetBeatFromElapsedTime(pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat())) ); - fLastBeat = max( fLastBeat, m_SongTiming.GetBeatFromElapsedTime(pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat())) ); + localFirst = min(localFirst, + pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat())); + localLast = max(localLast, + pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat())); } - m_fFirstBeat = fFirstBeat; - m_fLastBeat = fLastBeat; + this->firstSecond = localFirst; + this->lastSecond = localLast; } // Return whether the song is playable in the given style. @@ -1458,8 +1506,7 @@ bool Song::HasSignificantBpmChangesOrStops() const float Song::GetStepsSeconds() const { - const TimingData &timing = this->m_SongTiming; - return timing.GetElapsedTimeFromBeat( m_fLastBeat ) - timing.GetElapsedTimeFromBeat( m_fFirstBeat ); + return this->GetLastSecond() - this->GetFirstSecond(); } bool Song::IsLong() const @@ -1576,8 +1623,26 @@ public: } static int GetStepsSeconds( T* p, lua_State *L ) { lua_pushnumber(L, p->GetStepsSeconds()); return 1; } static int NormallyDisplayed( T* p, lua_State *L ){ lua_pushboolean(L, p->NormallyDisplayed()); return 1; } - static int GetFirstBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->m_fFirstBeat); return 1; } - static int GetLastBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->m_fLastBeat); return 1; } + static int GetFirstSecond(T* p, lua_State *L) + { + lua_pushnumber(L, p->GetFirstSecond()); + return 1; + } + static int GetLastSecond(T* p, lua_State *L) + { + lua_pushnumber(L, p->GetLastSecond()); + return 1; + } + static int GetFirstBeat( T* p, lua_State *L ) + { + lua_pushnumber(L, p->GetFirstBeat()); + return 1; + } + static int GetLastBeat( T* p, lua_State *L ) + { + lua_pushnumber(L, p->GetLastBeat()); + return 1; + } static int HasAttacks( T* p, lua_State *L ) { lua_pushboolean(L, p->HasAttacks()); return 1; } static int GetDisplayBpms( T* p, lua_State *L ) { @@ -1654,7 +1719,9 @@ public: ADD_METHOD( GetStepsSeconds ); ADD_METHOD( NormallyDisplayed ); ADD_METHOD( GetFirstBeat ); + ADD_METHOD( GetFirstSecond ); ADD_METHOD( GetLastBeat ); + ADD_METHOD( GetLastSecond ); ADD_METHOD( HasAttacks ); ADD_METHOD( GetDisplayBpms ); ADD_METHOD( IsDisplayBpmSecret ); diff --git a/src/Song.h b/src/Song.h index 990e553009..279cb8e5b9 100644 --- a/src/Song.h +++ b/src/Song.h @@ -20,7 +20,7 @@ void FixupPath( RString &path, const RString &sSongPath ); RString GetSongAssetPath( RString sPath, const RString &sSongPath ); /** @brief The version of the .ssc file format. */ -const static float STEPFILE_VERSION_NUMBER = 0.72f; +const static float STEPFILE_VERSION_NUMBER = 0.73f; /** @brief How many edits for this song can each profile have? */ const int MAX_EDITS_PER_SONG_PER_PROFILE = 15; @@ -231,10 +231,8 @@ public: RString m_sMusicFile; RString m_sInstrumentTrackFile[NUM_InstrumentTrack]; + /** @brief The length of the music file. */ float m_fMusicLengthSeconds; - float m_fFirstBeat; // beat of first note - float m_fLastBeat; // beat of last note - float m_fSpecifiedLastBeat; // specified last beat of the song float m_fMusicSampleStartSeconds; float m_fMusicSampleLengthSeconds; DisplayBPM m_DisplayBPMType; @@ -289,9 +287,26 @@ public: /** @brief The Song's TimingData. */ TimingData m_SongTiming; + + float GetFirstBeat() const; + float GetFirstSecond() const; + float GetLastBeat() const; + float GetLastSecond() const; + float GetSpecifiedLastBeat() const; + float GetSpecifiedLastSecond() const; + + void SetFirstSecond(const float f); + void SetLastSecond(const float f); + void SetSpecifiedLastSecond(const float f); typedef vector VBackgroundChange; private: + /** @brief The first second that a note is hit. */ + float firstSecond; + /** @brief The last second that a note is hit. */ + float lastSecond; + /** @brief The last second of the song for playing purposes. */ + float specifiedLastSecond; /** * @brief The background changes (sorted by layer) that are for this Song. *