[splittiming -> default] Bring it in.
Any bugs left in here will be fixed as a team. Now let's kick some StepMania 5 booty!
This commit is contained in:
@@ -64,6 +64,7 @@ lib*_link.a
|
||||
Data/UserPrefs
|
||||
Data/uPrefs
|
||||
Data/GamePrefs/*.cfg
|
||||
Data/Static.ini
|
||||
PBProject/Hardware.plist
|
||||
Themes/pump
|
||||
Themes/optical-release
|
||||
|
||||
@@ -9,6 +9,12 @@ change to JSON, but it is unsure if this will be done.
|
||||
Implement .ssc at your own risk.
|
||||
________________________________________________________________________________
|
||||
|
||||
[v0.7] - theDtTvB, Wolfman2000
|
||||
* Split Timing officially implemented.
|
||||
* #SPEEDS tag for modified scroll speeds.
|
||||
* #FAKES tag for ignoring judgments within a range.
|
||||
* #WARPS modified so that second parameter is now relative to the start.
|
||||
|
||||
[v0.59] - Wolfman2000
|
||||
* Typo fix: #RADARVALUES needed a semicolon at the end, not a colon.
|
||||
|
||||
|
||||
@@ -8,16 +8,30 @@ ________________________________________________________________________________
|
||||
StepMania 5.0 $NEXT | 20110xyy
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
2011/05/17
|
||||
----------
|
||||
* [NotesWriterSM] Write out OpenITG compatible SM files from here on out.
|
||||
This includes turning Warps into Negative Stops. For those that wanted
|
||||
negative BPMs specifically, my apologies, but stops are easier on math.
|
||||
[Wolfman2000]
|
||||
|
||||
2011/05/16
|
||||
----------
|
||||
* [ProfileManager] Added ProfileWasLoadedFromMemoryCard(PlayerNumber)
|
||||
Lua binding. [AJ]
|
||||
* Added #FAKES to the SSC format. Similar to the fake arrows, these blocks
|
||||
prevent all judgments within the range. [Wolfman2000]
|
||||
* Changed the syntax of #WARPS slightly. Now the second value is the relative
|
||||
distance that is skipped, not the absolute value when you start hitting
|
||||
arrows again. [Wolfman2000]
|
||||
|
||||
2011/05/15
|
||||
----------
|
||||
* [ScreenGameplay] Removed hardcoded commands for Debug (give up text) and
|
||||
replace them with DebugStartOnCommand, DebugBackOnCommand, and
|
||||
DebugTweenOffCommand. [AJ]
|
||||
* Added #SPEEDS to the SSC format. This multiplies your scrolling speed during
|
||||
gameplay, not replaces it (see mod attacks for replacing). [Wolfman2000]
|
||||
|
||||
2011/05/14
|
||||
----------
|
||||
|
||||
@@ -1349,6 +1349,7 @@
|
||||
<Function name='GetMeter'/>
|
||||
<Function name='GetRadarValues'/>
|
||||
<Function name='GetStepsType'/>
|
||||
<Function name='HasSignificantTimingChanges'/>
|
||||
<Function name='IsAPlayerEdit'/>
|
||||
<Function name='IsAnEdit'/>
|
||||
<Function name='IsAutogen'/>
|
||||
@@ -1405,6 +1406,8 @@
|
||||
<Function name='HasNegativeBPMs'/>
|
||||
<Function name='HasStops'/>
|
||||
<Function name='HasWarps'/>
|
||||
<Function name='HasFakes'/>
|
||||
<Function name='HasSpeedChanges'/>
|
||||
</Class>
|
||||
<Class name='Trail'>
|
||||
<Function name='GetArtists'/>
|
||||
|
||||
@@ -3367,6 +3367,9 @@
|
||||
<Function name='GetMeter' return='int' arguments=''>
|
||||
Returns the numerical difficulty of the Steps.
|
||||
</Function>
|
||||
<Function name='HasSignificantTimingChanges' sm-ssc='true' return='bool' arguments=''>
|
||||
Returns <code>true</code> if the song has significant timing changes.
|
||||
</Function>
|
||||
<Function name='GetRadarValues' return='RadarValues' arguments='PlayerNumber pn'>
|
||||
Returns the complete list of RadarValues for player <code>pn</code>. Use <Link class='RadarValues' function='GetValue' /> to grab a specific value.
|
||||
</Function>
|
||||
@@ -3515,6 +3518,12 @@
|
||||
<Function name='HasWarps' sm-ssc='true' return='bool' arguments=''>
|
||||
Returns <code>true</code> if the TimingData contains warps.
|
||||
</Function>
|
||||
<Function name='HasFakes' sm-ssc='true' return='bool' arguments=''>
|
||||
Returns <code>true</code> if the TimingData contains fake segments.
|
||||
</Function>
|
||||
<Function name='HasSpeedChanges' sm-ssc='true' return='bool' arguments=''>
|
||||
Returns <code>true</code> if the TimingData contains speed scrolling changes.
|
||||
</Function>
|
||||
</Class>
|
||||
<Class name='Trail'>
|
||||
<Function name='GetArtists' return='{string}' arguments=''>
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
#INSTRUMENTTRACK:;
|
||||
#MUSICLENGTH:;
|
||||
#OFFSET:;
|
||||
#BPMS:;
|
||||
#STOPS:;
|
||||
#DELAYS:;
|
||||
#BPMS:;
|
||||
#LABELS:;
|
||||
#TIMESIGNATURES:;
|
||||
#LASTBEATHINT:;
|
||||
#SAMPLESTART:;
|
||||
@@ -43,17 +44,24 @@
|
||||
// information from #NOTES moved here
|
||||
#STEPSTYPE:;
|
||||
#DESCRIPTION:;
|
||||
#CHARTSTYLE:;
|
||||
#DIFFICULTY:;
|
||||
#METER:;
|
||||
#RADARVALUES:;
|
||||
#CREDIT:;
|
||||
|
||||
// steps-based timingdata
|
||||
#BPMS:;
|
||||
#STOPS:;
|
||||
#DELAYS:;
|
||||
#BPMS:;
|
||||
#TIMESIGNATURES:;
|
||||
#LASTBEATHINT:;
|
||||
#TICKCOUNTS:;
|
||||
#COMBOS:;
|
||||
#SPEEDS:;
|
||||
#FAKES:;
|
||||
#LABELS:;
|
||||
#ATTACKS:; // Not yet in use.
|
||||
#OFFSET:;
|
||||
|
||||
// actual step data
|
||||
#NOTES:;
|
||||
#NOTES:;
|
||||
|
||||
@@ -6,7 +6,7 @@ local function Beat(self)
|
||||
local part = beat%1
|
||||
part = clamp(part,0,0.5)
|
||||
local eff = scale(part,0,0.5,1,0)
|
||||
if (GAMESTATE:GetSongDelay() or false) and part == 0 then eff = 0 end
|
||||
if (GAMESTATE:GetSongPosition:GetDelay() or false) and part == 0 then eff = 0 end
|
||||
if beat < 0 then
|
||||
eff = 0
|
||||
end
|
||||
|
||||
@@ -558,6 +558,7 @@ Baked Random=Baked Random
|
||||
Bar=Bar
|
||||
Battery=Battery
|
||||
Beat=Beat
|
||||
Beats=Beats
|
||||
Big=Big
|
||||
Blind=Blind
|
||||
Blink=Blink
|
||||
@@ -694,6 +695,7 @@ Right=Right
|
||||
Roll=Roll
|
||||
Romanization=Transliterate
|
||||
SHOW=Show
|
||||
Seconds=Seconds
|
||||
Select=Select
|
||||
Servers=Servers
|
||||
Shift Left=Shift Left
|
||||
@@ -845,15 +847,18 @@ Exit Edit Mode=Exit Edit Mode
|
||||
Edit stop=Edit stop
|
||||
Edit delay=Edit delay
|
||||
Edit Timing Data=Edit Timing Data
|
||||
Edit time signature=Edit time signature (not working yet)
|
||||
Edit time signature (top)=Edit time signature (beats per measure)
|
||||
Edit time signature (bottom)=Edit time signature (single beat note value)
|
||||
Edit time signature=Edit time signature
|
||||
Edit tickcount=Edit tickcount
|
||||
Edit combo=Edit combo
|
||||
Edit label=Edit label
|
||||
Edit warp=Edit warp
|
||||
Edit speed (percent)=Edit speed (percent)
|
||||
Edit speed (wait)=Edit speed (length)
|
||||
Edit speed (mode)=Edit speed (mode)
|
||||
Edit fake=Edit fake segment
|
||||
Editor options=Options
|
||||
EditorShowBGChangesPlay=Show Backgrounds
|
||||
Erase step timing=Erase step timing
|
||||
Effect=Effect
|
||||
EnableAttackSounds=Attack Sounds
|
||||
EnableMineHitSound=Mine Sounds
|
||||
@@ -1217,15 +1222,19 @@ Can't undo - no undo data.=Can't undo - no undo data.
|
||||
Do you want to revert from disk?=Do you want to revert from disk?
|
||||
Do you want to revert to your last save?=Do you want to revert to your last save?
|
||||
Do you want to save changes before exiting?=Do you want to save changes before exiting?
|
||||
Enter a new BPM value.=Enter a new BPM value.
|
||||
Enter a new Stop value.=Enter a new Stop value.
|
||||
Enter a new Delay value.=Enter a new Delay value.
|
||||
Enter a new Time Signature numerator value.=Enter the number of beats per measure.
|
||||
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 Label value.=Enter a name for this section of the chart.
|
||||
Enter a new Warp value.=Enter the beat you will warp to when you reach this point.
|
||||
Enter a new BPM value.=Enter a new BPM value.\n\nEnter the previous value to remove.
|
||||
Enter a new Stop value.=Enter a new Stop value.\n\nType "0" to remove.
|
||||
Enter a new Delay value.=Enter a new Delay value.\n\nType "0" to remove.
|
||||
Enter a new Time Signature.=Enter a new time signature.\nUse the format "x/y".\nEnter the previous value to remove.
|
||||
Enter a new Tickcount value.=Enter a new Tickcount value.\n\nEnter the previous value to remove.
|
||||
Enter a new Combo value.=Enter a new Combo value.\n\nEnter the previous value to remove.
|
||||
Enter a new Label value.=Enter a name for this section of the chart.\n\nEnter a previously used label to remove.
|
||||
Enter a new Warp value.=Enter the number of beats you will warp over.\n\nType "0" to remove this segment.
|
||||
Enter a new Speed percent value.=Enter the ratio for speed scrolling.\nEnter the previous value to remove.\n1 is the default scroll.
|
||||
Enter a new Speed wait value.=Enter how long in seconds/beats it takes to change.\n\n0 is instant.
|
||||
Enter a new Speed mode value.=Enter how this segment is handled.\n\nType "0" for beats, "1" for seconds.
|
||||
Enter a new Fake value.=Enter the number of beats that won't be judged.\n\nType "0" to remove this.
|
||||
Are you sure you want to erase this chart's timing data?=Are you sure you want to erase this chart's timing data?
|
||||
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.
|
||||
@@ -1246,7 +1255,7 @@ Enter a new min BPM.=Enter the minimum displayed BPM.
|
||||
Enter a new max BPM.=Enter the maximum displayed BPM.
|
||||
More than %d notes per measure is not allowed. This change has been reverted.=More than %d notes per measure is not allowed. This change has been reverted.
|
||||
No backgrounds available=No backgrounds available
|
||||
EditHelpText=Up/Down:\n change beat\nLeft/Right:\n change snap\nNumber keys:\n add/remove\n tap note\nCreate hold note:\n Hold a number\n while moving\n Up or Down\nCreate roll note:\n Hold Shift,\n then create a\n hold note.\nSpace bar:\n Set area\n marker\nEnter:\n Area Menu\nEscape:\n Main Menu\nF1:\n Show help\n
|
||||
EditHelpText=Up/Down:\n change beat\nLeft/Right:\n change snap\nNumber keys:\n add/remove\n tap note\nN and M keys:\n swap tap notes\nCreate hold note:\n Hold a number\n while moving\n Up or Down\nCreate roll note:\n Hold Shift,\n then create a\n hold note.\nSpace bar:\n Set area\n marker\nEnter:\n Area Menu\nEscape:\n Main Menu\nF4:\n Timing Menu\nF1:\n Show help\n
|
||||
PlayRecordHelpText=Press START to end
|
||||
Save successful.=Save successful.
|
||||
Saved as SM and DWI.=Saved as SM and DWI.
|
||||
@@ -1257,6 +1266,7 @@ This change creates more than %d notes in a measure.=This change creates more th
|
||||
This change creates notes past the end of the music and is not allowed.=This change creates notes past the end of the music and is not allowed.
|
||||
This will destroy all unsaved changes.=This will destroy all unsaved changes.
|
||||
Undo=Undo
|
||||
You must be in Song Timing Mode to edit BG Changes.=You must be in Song Timing Mode to edit BG Changes.
|
||||
|
||||
Current beat=Current beat
|
||||
Current second=Current second
|
||||
@@ -1276,6 +1286,9 @@ Mines=Mines
|
||||
Rolls=Rolls
|
||||
Lifts=Lifts
|
||||
Fakes=Fakes
|
||||
Timing Mode=Timing Mode
|
||||
Step Timing=Step Timing
|
||||
Song Timing=Song Timing
|
||||
Beat 0 offset=Beat 0 offset
|
||||
Preview Start=Preview Start
|
||||
Preview Length=Preview Length
|
||||
@@ -1944,7 +1957,7 @@ Decrease/increase music offset=Decrease/increase music offset
|
||||
Decrease/increase sample music start=Decrease/increase sample music start
|
||||
Decrease/increase sample music length=Decrease/increase sample music length
|
||||
Play sample music=Play sample music
|
||||
Add/Edit Background Change=Add/Edit Background Change
|
||||
Add/Edit Background Change=Add/Edit Background Change (Song Timing Only)
|
||||
Insert beat and shift down=Insert beat and shift down
|
||||
Shift BPM changes and stops down one beat=Shift BPM changes and stops down one beat
|
||||
Delete beat and shift up=Delete beat and shift up
|
||||
@@ -1953,6 +1966,7 @@ Cycle between tap notes=Cycle between tap notes
|
||||
Lay mine=Lay mine
|
||||
Lay lift=Lay lift
|
||||
Add to/remove from right half=Add to/remove from right half
|
||||
Switch Timing=Switch Timing
|
||||
Switch player (Routine only)=Switch player (Routine only)
|
||||
|
||||
[Grade]
|
||||
|
||||
@@ -20,4 +20,4 @@ local _screen = {
|
||||
h = SCREEN_HEIGHT,
|
||||
cx = SCREEN_CENTER_X,
|
||||
cy = SCREEN_CENTER_Y
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +63,14 @@ end
|
||||
|
||||
if ScreenMetric then
|
||||
ScreenMetric = Screen.Metric
|
||||
end
|
||||
end
|
||||
|
||||
--[[ GameState ]]
|
||||
--Aliases for old GAMESTATE timing functions.
|
||||
--These have been converted to SongPosition, but most themes still use these old functions.
|
||||
|
||||
function GameState:GetSongBeat() return self:GetSongPosition():GetSongBeat() end
|
||||
function GameState:GetSongBeatNoOffset() return self:GetSongPosition():GetSongBeatNoOffset() end
|
||||
function GameState:GetSongBPS() return self:GetSongPosition():GetCurBPS() end
|
||||
function GameState:GetSongDelay() return self:GetSongPosition:GetDelay() end
|
||||
function GameState:GetSongFreeze() return self:GetSongPosition:GetFreeze() end
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
MusicWheel sort
|
||||
@@ -0,0 +1 @@
|
||||
MusicWheel sort
|
||||
@@ -989,6 +989,8 @@ TimeSignatureColor=color("1,0.55,0,1")
|
||||
TickcountColor=color("0,1,0,1")
|
||||
ComboColor=color("0.55,1,0,1")
|
||||
LabelColor=color("1,0,0,1")
|
||||
SpeedColor=color("0.5,1,1,1")
|
||||
FakeColor=color("1,1,0.5,1")
|
||||
#
|
||||
BPMIsLeftSide=true
|
||||
StopIsLeftSide=true
|
||||
@@ -998,15 +1000,19 @@ TimeSignatureIsLeftSide=true
|
||||
TickcountIsLeftSide=false
|
||||
ComboIsLeftSide=false
|
||||
LabelIsLeftSide=false
|
||||
SpeedIsLeftSide=false
|
||||
FakeIsLeftSide=true
|
||||
#
|
||||
BPMOffsetX=60
|
||||
StopOffsetX=50
|
||||
DelayOffsetX=120
|
||||
WarpOffsetX=60
|
||||
WarpOffsetX=90
|
||||
TimeSignatureOffsetX=30
|
||||
TickcountOffsetX=10
|
||||
ComboOffsetX=30
|
||||
LabelOffsetX=80
|
||||
TickcountOffsetX=70
|
||||
ComboOffsetX=50
|
||||
LabelOffsetX=130
|
||||
SpeedOffsetX=30
|
||||
FakeOffsetX=90
|
||||
|
||||
[PlayerStageStats]
|
||||
# Original CVS Grading
|
||||
@@ -3607,7 +3613,7 @@ TimerSeconds=-1
|
||||
EditModifiers="no reverse"
|
||||
EditHelpX=SCREEN_LEFT+4
|
||||
EditHelpY=SCREEN_TOP+16
|
||||
EditHelpOnCommand=halign,0;valign,0;zoom,0.675;shadowlength,1
|
||||
EditHelpOnCommand=halign,0;valign,0;zoom,0.5;shadowlength,1
|
||||
InfoX=SCREEN_RIGHT-128
|
||||
InfoY=SCREEN_TOP+16
|
||||
InfoOnCommand=halign,0;valign,0;zoom,0.5;shadowlength,1
|
||||
@@ -3636,6 +3642,7 @@ NumHandsFormat="%s: %d\n"
|
||||
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"
|
||||
|
||||
@@ -1657,6 +1657,8 @@
|
||||
AAFF41A40F5BD4AD00FFDB67 /* ScreenOptionsExportPackage.h in Headers */ = {isa = PBXBuildFile; fileRef = AAFF41A20F5BD4AD00FFDB67 /* ScreenOptionsExportPackage.h */; };
|
||||
AAFF41A50F5BD4AD00FFDB67 /* ScreenOptionsExportPackage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAFF41A10F5BD4AD00FFDB67 /* ScreenOptionsExportPackage.cpp */; };
|
||||
AAFF41A60F5BD4AD00FFDB67 /* ScreenOptionsExportPackage.h in Headers */ = {isa = PBXBuildFile; fileRef = AAFF41A20F5BD4AD00FFDB67 /* ScreenOptionsExportPackage.h */; };
|
||||
B75CE3B5137963350076A82D /* SongPosition.h in Headers */ = {isa = PBXBuildFile; fileRef = B75CE3B3137963350076A82D /* SongPosition.h */; };
|
||||
B75CE3B6137963350076A82D /* SongPosition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B75CE3B4137963350076A82D /* SongPosition.cpp */; };
|
||||
EE2153A910A75BEE0074B01B /* NotesLoaderPMS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EE2153A710A75BEE0074B01B /* NotesLoaderPMS.cpp */; };
|
||||
EE2153AA10A75BEE0074B01B /* NotesLoaderPMS.h in Headers */ = {isa = PBXBuildFile; fileRef = EE2153A810A75BEE0074B01B /* NotesLoaderPMS.h */; };
|
||||
EE879B60132DEE720078FDDC /* RageDisplay_Legacy_Helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EE879B5C132DEE720078FDDC /* RageDisplay_Legacy_Helpers.cpp */; };
|
||||
@@ -3025,6 +3027,8 @@
|
||||
AAFF55220549F43A00D02EE9 /* ComboGraph.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; name = ComboGraph.h; path = ../src/ComboGraph.h; sourceTree = "<group>"; };
|
||||
AAFF5541054E736D00D02EE9 /* Attack.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; name = Attack.cpp; path = ../src/Attack.cpp; sourceTree = "<group>"; };
|
||||
AAFF5542054E736D00D02EE9 /* Attack.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; name = Attack.h; path = ../src/Attack.h; sourceTree = "<group>"; };
|
||||
B75CE3B3137963350076A82D /* SongPosition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SongPosition.h; path = ../src/SongPosition.h; sourceTree = SOURCE_ROOT; };
|
||||
B75CE3B4137963350076A82D /* SongPosition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SongPosition.cpp; path = ../src/SongPosition.cpp; sourceTree = SOURCE_ROOT; };
|
||||
EE2153A710A75BEE0074B01B /* NotesLoaderPMS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesLoaderPMS.cpp; path = ../src/NotesLoaderPMS.cpp; sourceTree = SOURCE_ROOT; };
|
||||
EE2153A810A75BEE0074B01B /* NotesLoaderPMS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotesLoaderPMS.h; path = ../src/NotesLoaderPMS.h; sourceTree = SOURCE_ROOT; };
|
||||
EE879B5C132DEE720078FDDC /* RageDisplay_Legacy_Helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RageDisplay_Legacy_Helpers.cpp; path = ../src/RageDisplay_Legacy_Helpers.cpp; sourceTree = SOURCE_ROOT; };
|
||||
@@ -3938,6 +3942,8 @@
|
||||
AA7CDD930D8395F90001DAFC /* Song.h */,
|
||||
AA6EFE0704770C79005F316C /* SongOptions.cpp */,
|
||||
AA6EFE0804770C79005F316C /* SongOptions.h */,
|
||||
B75CE3B4137963350076A82D /* SongPosition.cpp */,
|
||||
B75CE3B3137963350076A82D /* SongPosition.h */,
|
||||
AA5C73F90637A5F000AFB891 /* SongUtil.cpp */,
|
||||
AA5C73FA0637A5F000AFB891 /* SongUtil.h */,
|
||||
AAEBF57C0B5C83F4005F3C7C /* SoundEffectControl.cpp */,
|
||||
@@ -5056,6 +5062,7 @@
|
||||
2BC6132D13773C8E00CC8BBD /* pngpriv.h in Headers */,
|
||||
2BC6133313773C8E00CC8BBD /* pngstruct.h in Headers */,
|
||||
2BC6133E13773D2C00CC8BBD /* pnglibconf.h in Headers */,
|
||||
B75CE3B5137963350076A82D /* SongPosition.h in Headers */,
|
||||
2B9C2E8E137A138B004C92F3 /* NotesLoaderJson.h in Headers */,
|
||||
2B9C2E90137A138B004C92F3 /* NotesWriterJson.h in Headers */,
|
||||
);
|
||||
@@ -6317,6 +6324,7 @@
|
||||
2BC6133813773C8E00CC8BBD /* pngwrite.c in Sources */,
|
||||
2BC6133913773C8E00CC8BBD /* pngwtran.c in Sources */,
|
||||
2BC6133A13773C8E00CC8BBD /* pngwutil.c in Sources */,
|
||||
B75CE3B6137963350076A82D /* SongPosition.cpp in Sources */,
|
||||
2B9C2E8D137A138B004C92F3 /* NotesLoaderJson.cpp in Sources */,
|
||||
2B9C2E8F137A138B004C92F3 /* NotesWriterJson.cpp in Sources */,
|
||||
);
|
||||
|
||||
+19
-2
@@ -34,6 +34,8 @@ REGISTER_ACTOR_CLASS_WITH_NAME( HiddenActor, Actor );
|
||||
|
||||
float Actor::g_fCurrentBGMTime = 0, Actor::g_fCurrentBGMBeat;
|
||||
float Actor::g_fCurrentBGMTimeNoOffset = 0, Actor::g_fCurrentBGMBeatNoOffset = 0;
|
||||
vector<float> Actor::g_vfCurrentBGMBeatPlayer(NUM_PlayerNumber, 0);
|
||||
vector<float> Actor::g_vfCurrentBGMBeatPlayerNoOffset(NUM_PlayerNumber, 0);
|
||||
|
||||
|
||||
Actor *Actor::Copy() const { return new Actor(*this); }
|
||||
@@ -68,6 +70,12 @@ void Actor::SetBGMTime( float fTime, float fBeat, float fTimeNoOffset, float fBe
|
||||
g_fCurrentBGMBeatNoOffset = fBeatNoOffset;
|
||||
}
|
||||
|
||||
void Actor::SetPlayerBGMBeat( PlayerNumber pn, float fBeat, float fBeatNoOffset )
|
||||
{
|
||||
g_vfCurrentBGMBeatPlayer[pn] = fBeat;
|
||||
g_vfCurrentBGMBeatPlayerNoOffset[pn] = fBeatNoOffset;
|
||||
}
|
||||
|
||||
void Actor::SetBGMLight( int iLightNumber, float fCabinetLights )
|
||||
{
|
||||
ASSERT( iLightNumber < NUM_CabinetLight );
|
||||
@@ -150,8 +158,7 @@ Actor::Actor()
|
||||
lua_setfield( L, -2, "ctx" );
|
||||
lua_pop( L, 1 );
|
||||
LUA->Release( L );
|
||||
|
||||
|
||||
|
||||
m_size = RageVector2( 1, 1 );
|
||||
InitState();
|
||||
m_pParent = NULL;
|
||||
@@ -676,6 +683,16 @@ void Actor::UpdateInternal( float fDeltaTime )
|
||||
m_fSecsIntoEffect = g_fCurrentBGMBeat;
|
||||
break;
|
||||
|
||||
case CLOCK_BGM_BEAT_PLAYER1:
|
||||
m_fEffectDelta = g_vfCurrentBGMBeatPlayer[PLAYER_1] - m_fSecsIntoEffect;
|
||||
m_fSecsIntoEffect = g_vfCurrentBGMBeatPlayerNoOffset[PLAYER_1];
|
||||
break;
|
||||
|
||||
case CLOCK_BGM_BEAT_PLAYER2:
|
||||
m_fEffectDelta = g_vfCurrentBGMBeatPlayer[PLAYER_2] - m_fSecsIntoEffect;
|
||||
m_fSecsIntoEffect = g_vfCurrentBGMBeatPlayerNoOffset[PLAYER_2];
|
||||
break;
|
||||
|
||||
case CLOCK_BGM_TIME:
|
||||
m_fEffectDelta = g_fCurrentBGMTime - m_fSecsIntoEffect;
|
||||
m_fSecsIntoEffect = g_fCurrentBGMTime;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ACTOR_H
|
||||
#define ACTOR_H
|
||||
|
||||
#include "PlayerNumber.h"
|
||||
#include "RageTypes.h"
|
||||
#include "RageUtil_AutoPtr.h"
|
||||
#include "LuaReference.h"
|
||||
@@ -109,6 +110,7 @@ public:
|
||||
virtual void LoadFromNode( const XNode* pNode );
|
||||
|
||||
static void SetBGMTime( float fTime, float fBeat, float fTimeNoOffset, float fBeatNoOffset );
|
||||
static void SetPlayerBGMBeat( PlayerNumber pn, float fBeat, float fBeatNoOffset );
|
||||
static void SetBGMLight( int iLightNumber, float fCabinetLights );
|
||||
|
||||
/**
|
||||
@@ -131,6 +133,8 @@ public:
|
||||
CLOCK_BGM_BEAT,
|
||||
CLOCK_BGM_TIME_NO_OFFSET,
|
||||
CLOCK_BGM_BEAT_NO_OFFSET,
|
||||
CLOCK_BGM_BEAT_PLAYER1,
|
||||
CLOCK_BGM_BEAT_PLAYER2,
|
||||
CLOCK_LIGHT_1 = 1000,
|
||||
CLOCK_LIGHT_LAST = 1100,
|
||||
NUM_CLOCKS
|
||||
@@ -713,6 +717,8 @@ protected:
|
||||
// global state
|
||||
static float g_fCurrentBGMTime, g_fCurrentBGMBeat;
|
||||
static float g_fCurrentBGMTimeNoOffset, g_fCurrentBGMBeatNoOffset;
|
||||
static vector<float> g_vfCurrentBGMBeatPlayer;
|
||||
static vector<float> g_vfCurrentBGMBeatPlayerNoOffset;
|
||||
|
||||
private:
|
||||
// commands
|
||||
|
||||
+12
-12
@@ -58,7 +58,7 @@ void AdjustSync::ResetOriginalSyncData()
|
||||
s_pTimingDataOriginal = new TimingData;
|
||||
|
||||
if( GAMESTATE->m_pCurSong )
|
||||
*s_pTimingDataOriginal = GAMESTATE->m_pCurSong->m_Timing;
|
||||
*s_pTimingDataOriginal = GAMESTATE->m_pCurSong->m_SongTiming;
|
||||
else
|
||||
*s_pTimingDataOriginal = TimingData();
|
||||
s_fGlobalOffsetSecondsOriginal = PREFSMAN->m_fGlobalOffsetSeconds;
|
||||
@@ -87,7 +87,7 @@ void AdjustSync::SaveSyncChanges()
|
||||
{
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
return;
|
||||
if( GAMESTATE->m_pCurSong && *s_pTimingDataOriginal != GAMESTATE->m_pCurSong->m_Timing )
|
||||
if( GAMESTATE->m_pCurSong && *s_pTimingDataOriginal != GAMESTATE->m_pCurSong->m_SongTiming )
|
||||
{
|
||||
if( GAMESTATE->IsEditing() )
|
||||
{
|
||||
@@ -110,7 +110,7 @@ void AdjustSync::RevertSyncChanges()
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
return;
|
||||
PREFSMAN->m_fGlobalOffsetSeconds.Set( s_fGlobalOffsetSecondsOriginal );
|
||||
GAMESTATE->m_pCurSong->m_Timing = *s_pTimingDataOriginal;
|
||||
GAMESTATE->m_pCurSong->m_SongTiming = *s_pTimingDataOriginal;
|
||||
ResetOriginalSyncData();
|
||||
s_fStandardDeviation = 0.0f;
|
||||
s_fAverageError = 0.0f;
|
||||
@@ -186,7 +186,7 @@ void AdjustSync::AutosyncOffset()
|
||||
switch( GAMESTATE->m_SongOptions.GetCurrent().m_AutosyncType )
|
||||
{
|
||||
case SongOptions::AUTOSYNC_SONG:
|
||||
GAMESTATE->m_pCurSong->m_Timing.m_fBeat0OffsetInSeconds += mean;
|
||||
GAMESTATE->m_pCurSong->m_SongTiming.m_fBeat0OffsetInSeconds += mean;
|
||||
break;
|
||||
case SongOptions::AUTOSYNC_MACHINE:
|
||||
PREFSMAN->m_fGlobalOffsetSeconds.Set( PREFSMAN->m_fGlobalOffsetSeconds + mean );
|
||||
@@ -232,15 +232,15 @@ void AdjustSync::AutosyncTempo()
|
||||
if( !CalcLeastSquares( s_vAutosyncTempoData, fSlope, fIntercept, fFilteredError ) )
|
||||
return;
|
||||
|
||||
GAMESTATE->m_pCurSong->m_Timing.m_fBeat0OffsetInSeconds += fIntercept;
|
||||
GAMESTATE->m_pCurSong->m_SongTiming.m_fBeat0OffsetInSeconds += fIntercept;
|
||||
const float fScaleBPM = 1.0f/(1.0f - fSlope);
|
||||
FOREACH( BPMSegment, GAMESTATE->m_pCurSong->m_Timing.m_BPMSegments, i )
|
||||
FOREACH( BPMSegment, GAMESTATE->m_pCurSong->m_SongTiming.m_BPMSegments, i )
|
||||
i->SetBPM( i->GetBPM() * fScaleBPM );
|
||||
|
||||
// We assume that the stops were measured as a number of beats.
|
||||
// Therefore, if we change the bpms, we need to make a similar
|
||||
// change to the stops.
|
||||
FOREACH( StopSegment, GAMESTATE->m_pCurSong->m_Timing.m_StopSegments, i )
|
||||
FOREACH( StopSegment, GAMESTATE->m_pCurSong->m_SongTiming.m_StopSegments, i )
|
||||
i->m_fStopSeconds *= 1.0f - fSlope;
|
||||
|
||||
SCREENMAN->SystemMessage( AUTOSYNC_CORRECTION_APPLIED.GetValue() );
|
||||
@@ -296,7 +296,7 @@ void AdjustSync::GetSyncChangeTextSong( vector<RString> &vsAddTo )
|
||||
|
||||
{
|
||||
float fOld = Quantize( AdjustSync::s_pTimingDataOriginal->m_fBeat0OffsetInSeconds, 0.001f );
|
||||
float fNew = Quantize( GAMESTATE->m_pCurSong->m_Timing.m_fBeat0OffsetInSeconds, 0.001f );
|
||||
float fNew = Quantize( GAMESTATE->m_pCurSong->m_SongTiming.m_fBeat0OffsetInSeconds, 0.001f );
|
||||
float fDelta = fNew - fOld;
|
||||
|
||||
if( fabsf(fDelta) > 0.0001f )
|
||||
@@ -309,10 +309,10 @@ void AdjustSync::GetSyncChangeTextSong( vector<RString> &vsAddTo )
|
||||
}
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<GAMESTATE->m_pCurSong->m_Timing.m_BPMSegments.size(); i++ )
|
||||
for( unsigned i=0; i<GAMESTATE->m_pCurSong->m_SongTiming.m_BPMSegments.size(); i++ )
|
||||
{
|
||||
float fOld = Quantize( AdjustSync::s_pTimingDataOriginal->m_BPMSegments[i].GetBPM(), 0.001f );
|
||||
float fNew = Quantize( GAMESTATE->m_pCurSong->m_Timing.m_BPMSegments[i].GetBPM(), 0.001f );
|
||||
float fNew = Quantize( GAMESTATE->m_pCurSong->m_SongTiming.m_BPMSegments[i].GetBPM(), 0.001f );
|
||||
float fDelta = fNew - fOld;
|
||||
|
||||
if( fabsf(fDelta) > 0.0001f )
|
||||
@@ -330,10 +330,10 @@ void AdjustSync::GetSyncChangeTextSong( vector<RString> &vsAddTo )
|
||||
}
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<GAMESTATE->m_pCurSong->m_Timing.m_StopSegments.size(); i++ )
|
||||
for( unsigned i=0; i<GAMESTATE->m_pCurSong->m_SongTiming.m_StopSegments.size(); i++ )
|
||||
{
|
||||
float fOld = Quantize( AdjustSync::s_pTimingDataOriginal->m_StopSegments[i].m_fStopSeconds, 0.001f );
|
||||
float fNew = Quantize( GAMESTATE->m_pCurSong->m_Timing.m_StopSegments[i].m_fStopSeconds, 0.001f );
|
||||
float fNew = Quantize( GAMESTATE->m_pCurSong->m_SongTiming.m_StopSegments[i].m_fStopSeconds, 0.001f );
|
||||
float fDelta = fNew - fOld;
|
||||
|
||||
if( fabsf(fDelta) > 0.0001f )
|
||||
|
||||
+79
-22
@@ -67,13 +67,13 @@ static float GetNoteFieldHeight( const PlayerState* pPlayerState )
|
||||
|
||||
namespace
|
||||
{
|
||||
float g_fExpandSeconds = 0;
|
||||
struct PerPlayerData
|
||||
{
|
||||
float m_fMinTornadoX[MAX_COLS_PER_PLAYER];
|
||||
float m_fMaxTornadoX[MAX_COLS_PER_PLAYER];
|
||||
float m_fInvertDistance[MAX_COLS_PER_PLAYER];
|
||||
float m_fBeatFactor;
|
||||
float m_fExpandSeconds;
|
||||
};
|
||||
PerPlayerData g_EffectData[NUM_PLAYERS];
|
||||
};
|
||||
@@ -82,22 +82,25 @@ void ArrowEffects::Update()
|
||||
{
|
||||
const Style* pStyle = GAMESTATE->GetCurrentStyle();
|
||||
|
||||
{
|
||||
static float fLastTime = 0;
|
||||
float fTime = RageTimer::GetTimeSinceStartFast();
|
||||
if( !GAMESTATE->m_bFreeze || !GAMESTATE->m_bDelay )
|
||||
{
|
||||
g_fExpandSeconds += fTime - fLastTime;
|
||||
g_fExpandSeconds = fmodf( g_fExpandSeconds, PI*2 );
|
||||
}
|
||||
fLastTime = fTime;
|
||||
}
|
||||
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pn];
|
||||
const SongPosition &position = GAMESTATE->m_bIsUsingStepTiming
|
||||
? GAMESTATE->m_pPlayerState[pn]->m_Position : GAMESTATE->m_Position;
|
||||
|
||||
PerPlayerData &data = g_EffectData[pn];
|
||||
|
||||
{
|
||||
static float fLastTime = 0;
|
||||
float fTime = RageTimer::GetTimeSinceStartFast();
|
||||
if( !position.m_bFreeze || !position.m_bDelay )
|
||||
{
|
||||
data.m_fExpandSeconds += fTime - fLastTime;
|
||||
data.m_fExpandSeconds = fmodf( data.m_fExpandSeconds, PI*2 );
|
||||
}
|
||||
fLastTime = fTime;
|
||||
}
|
||||
|
||||
// Update Tornado
|
||||
for( int iColNum = 0; iColNum < MAX_COLS_PER_PLAYER; ++iColNum )
|
||||
{
|
||||
@@ -176,7 +179,7 @@ void ArrowEffects::Update()
|
||||
// Update Beat
|
||||
do {
|
||||
float fAccelTime = 0.2f, fTotalTime = 0.5f;
|
||||
float fBeat = GAMESTATE->m_fSongBeatVisible + fAccelTime;
|
||||
float fBeat = position.m_fSongBeatVisible + fAccelTime;
|
||||
|
||||
const bool bEvenBeat = ( int(fBeat) % 2 ) != 0;
|
||||
|
||||
@@ -208,6 +211,50 @@ void ArrowEffects::Update()
|
||||
}
|
||||
}
|
||||
|
||||
float GetSpeedMultiplier( float fSongBeat, float fMusicSeconds, const TimingData &tim )
|
||||
{
|
||||
if( tim.m_SpeedSegments.size() == 0 )
|
||||
return 1.0;
|
||||
|
||||
const int index = tim.GetSpeedSegmentIndexAtBeat( fSongBeat );
|
||||
|
||||
const SpeedSegment &seg = tim.m_SpeedSegments[index];
|
||||
float fStartBeat = NoteRowToBeat(seg.m_iStartRow);
|
||||
float fStartTime = tim.GetElapsedTimeFromBeat( fStartBeat ) - tim.GetDelayAtBeat( fStartBeat );
|
||||
float fEndTime;
|
||||
float fCurTime = fMusicSeconds;
|
||||
|
||||
if( seg.m_usMode == 1 ) // seconds
|
||||
{
|
||||
fEndTime = fStartTime + seg.m_fWait;
|
||||
}
|
||||
else
|
||||
{
|
||||
fEndTime = tim.GetElapsedTimeFromBeat( fStartBeat + seg.m_fWait ) - tim.GetDelayAtBeat( fStartBeat + seg.m_fWait );
|
||||
}
|
||||
|
||||
if( ( index == 0 && tim.m_SpeedSegments[0].m_fWait > 0.0 ) && fCurTime < fStartTime )
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
else if( fEndTime >= fCurTime && ( index > 0 || tim.m_SpeedSegments[0].m_fWait > 0.0 ) )
|
||||
{
|
||||
const float fPriorSpeed = ( index == 0 ? 1 : tim.m_SpeedSegments[index - 1].m_fPercent );
|
||||
float fTimeUsed = fCurTime - fStartTime;
|
||||
float fDuration = fEndTime - fStartTime;
|
||||
float fRatioUsed = fDuration == 0.0 ? 1 : fTimeUsed / fDuration;
|
||||
|
||||
float fDistance = fPriorSpeed - seg.m_fPercent;
|
||||
float fRatioNeed = fRatioUsed * -fDistance;
|
||||
return (fPriorSpeed + fRatioNeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
return seg.m_fPercent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* For visibility testing: if bAbsolute is false, random modifiers must return
|
||||
* the minimum possible scroll speed. */
|
||||
float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakOut, bool bAbsolute )
|
||||
@@ -217,21 +264,27 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
||||
bIsPastPeakOut = true;
|
||||
|
||||
float fYOffset = 0;
|
||||
const SongPosition &position = GAMESTATE->m_bIsUsingStepTiming
|
||||
? pPlayerState->m_Position : GAMESTATE->m_Position;
|
||||
|
||||
float fSongBeat = position.m_fSongBeatVisible;
|
||||
|
||||
Steps *pCurSteps = GAMESTATE->m_pCurSteps[pPlayerState->m_PlayerNumber];
|
||||
|
||||
/* Usually, fTimeSpacing is 0 or 1, in which case we use entirely beat spacing or
|
||||
* entirely time spacing (respectively). Occasionally, we tween between them. */
|
||||
if( pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing != 1.0f )
|
||||
{
|
||||
float fSongBeat = GAMESTATE->m_fSongBeatVisible;
|
||||
float fBeatsUntilStep = fNoteBeat - fSongBeat;
|
||||
float fYOffsetBeatSpacing = fBeatsUntilStep;
|
||||
fYOffset += fYOffsetBeatSpacing * (1-pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing);
|
||||
float fSpeedMultiplier = ( GAMESTATE->m_bInStepEditor || !GAMESTATE->m_bIsUsingStepTiming ) ? 1.0 : GetSpeedMultiplier( position.m_fSongBeatVisible, position.m_fMusicSecondsVisible, pCurSteps->m_Timing );
|
||||
fYOffset += fSpeedMultiplier * fYOffsetBeatSpacing * (1-pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing);
|
||||
}
|
||||
|
||||
if( pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing != 0.0f )
|
||||
{
|
||||
float fSongSeconds = GAMESTATE->m_fMusicSecondsVisible;
|
||||
float fNoteSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat(fNoteBeat);
|
||||
float fSongSeconds = GAMESTATE->m_Position.m_fMusicSecondsVisible;
|
||||
float fNoteSeconds = pCurSteps->m_Timing.GetElapsedTimeFromBeat(fNoteBeat);
|
||||
float fSecondsUntilStep = fNoteSeconds - fSongSeconds;
|
||||
float fBPM = pPlayerState->m_PlayerOptions.GetCurrent().m_fScrollBPM;
|
||||
float fBPS = fBPM/60.f;
|
||||
@@ -242,7 +295,7 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
||||
// TODO: If we allow noteskins to have metricable row spacing
|
||||
// (per issue 24), edit this to reflect that. -aj
|
||||
fYOffset *= ARROW_SPACING;
|
||||
|
||||
|
||||
// don't mess with the arrows after they've crossed 0
|
||||
if( fYOffset < 0 )
|
||||
return fYOffset * pPlayerState->m_PlayerOptions.GetCurrent().m_fScrollSpeed;
|
||||
@@ -288,6 +341,7 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
||||
|
||||
// Factor in scroll speed
|
||||
float fScrollSpeed = pPlayerState->m_PlayerOptions.GetCurrent().m_fScrollSpeed;
|
||||
|
||||
if( pPlayerState->m_PlayerOptions.GetCurrent().m_fRandomSpeed > 0 && !bAbsolute )
|
||||
{
|
||||
// Generate a deterministically "random" speed for each arrow.
|
||||
@@ -307,7 +361,10 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
||||
|
||||
if( fAccels[PlayerOptions::ACCEL_EXPAND] != 0 )
|
||||
{
|
||||
float fExpandMultiplier = SCALE( RageFastCos(g_fExpandSeconds*EXPAND_MULTIPLIER_FREQUENCY),
|
||||
// TODO: Don't index by PlayerNumber.
|
||||
PerPlayerData &data = g_EffectData[pPlayerState->m_PlayerNumber];
|
||||
|
||||
float fExpandMultiplier = SCALE( RageFastCos(data.m_fExpandSeconds*EXPAND_MULTIPLIER_FREQUENCY),
|
||||
EXPAND_MULTIPLIER_SCALE_FROM_LOW, EXPAND_MULTIPLIER_SCALE_FROM_HIGH,
|
||||
EXPAND_MULTIPLIER_SCALE_TO_LOW, EXPAND_MULTIPLIER_SCALE_TO_HIGH );
|
||||
fScrollSpeed *= SCALE( fAccels[PlayerOptions::ACCEL_EXPAND],
|
||||
@@ -511,7 +568,7 @@ float ArrowEffects::GetRotationZ( const PlayerState* pPlayerState, float fNoteBe
|
||||
// As usual, enable dizzy hold heads at your own risk. -Wolfman2000
|
||||
if( fEffects[PlayerOptions::EFFECT_DIZZY] != 0 && ( DIZZY_HOLD_HEADS || !bIsHoldHead ) )
|
||||
{
|
||||
const float fSongBeat = GAMESTATE->m_fSongBeatVisible;
|
||||
const float fSongBeat = pPlayerState->m_Position.m_fSongBeatVisible;
|
||||
float fDizzyRotation = fNoteBeat - fSongBeat;
|
||||
fDizzyRotation *= fEffects[PlayerOptions::EFFECT_DIZZY];
|
||||
fDizzyRotation = fmodf( fDizzyRotation, 2*PI );
|
||||
@@ -528,7 +585,7 @@ float ArrowEffects::ReceptorGetRotationZ( const PlayerState* pPlayerState )
|
||||
|
||||
if( fEffects[PlayerOptions::EFFECT_CONFUSION] != 0 )
|
||||
{
|
||||
float fConfRotation = GAMESTATE->m_fSongBeatVisible;
|
||||
float fConfRotation = pPlayerState->m_Position.m_fSongBeatVisible;
|
||||
fConfRotation *= fEffects[PlayerOptions::EFFECT_CONFUSION];
|
||||
fConfRotation = fmodf( fConfRotation, 2*PI );
|
||||
fConfRotation *= -180/PI;
|
||||
@@ -680,7 +737,7 @@ float ArrowEffects::GetBrightness( const PlayerState* pPlayerState, float fNoteB
|
||||
if( GAMESTATE->IsEditing() )
|
||||
return 1;
|
||||
|
||||
float fSongBeat = GAMESTATE->m_fSongBeatVisible;
|
||||
float fSongBeat = pPlayerState->m_Position.m_fSongBeatVisible;
|
||||
float fBeatsUntilStep = fNoteBeat - fSongBeat;
|
||||
|
||||
float fBrightness = SCALE( fBeatsUntilStep, 0, -1, 1.f, 0.f );
|
||||
|
||||
+8
-6
@@ -11,9 +11,10 @@ void Attack::GetAttackBeats( const Song *pSong, float &fStartBeat, float &fEndBe
|
||||
{
|
||||
ASSERT( pSong );
|
||||
ASSERT_M( fStartSecond >= 0, ssprintf("StartSecond: %f",fStartSecond) );
|
||||
|
||||
fStartBeat = pSong->GetBeatFromElapsedTime( fStartSecond );
|
||||
fEndBeat = pSong->GetBeatFromElapsedTime( fStartSecond+fSecsRemaining );
|
||||
|
||||
const TimingData &timing = pSong->m_SongTiming;
|
||||
fStartBeat = timing.GetBeatFromElapsedTime( fStartSecond );
|
||||
fEndBeat = timing.GetBeatFromElapsedTime( fStartSecond+fSecsRemaining );
|
||||
}
|
||||
|
||||
/* Get the range for an attack that's being applied in realtime, eg. during battle
|
||||
@@ -31,12 +32,13 @@ void Attack::GetRealtimeAttackBeats( const Song *pSong, const PlayerState* pPlay
|
||||
ASSERT( pSong );
|
||||
|
||||
/* If reasonable, push the attack forward 8 beats so that notes on screen don't change suddenly. */
|
||||
fStartBeat = min( GAMESTATE->m_fSongBeat+8, pPlayerState->m_fLastDrawnBeat );
|
||||
fStartBeat = min( GAMESTATE->m_Position.m_fSongBeat+8, pPlayerState->m_fLastDrawnBeat );
|
||||
fStartBeat = truncf(fStartBeat)+1;
|
||||
|
||||
const float lStartSecond = pSong->GetElapsedTimeFromBeat( fStartBeat );
|
||||
const TimingData &timing = pSong->m_SongTiming;
|
||||
const float lStartSecond = timing.GetElapsedTimeFromBeat( fStartBeat );
|
||||
const float fEndSecond = lStartSecond + fSecsRemaining;
|
||||
fEndBeat = pSong->GetBeatFromElapsedTime( fEndSecond );
|
||||
fEndBeat = timing.GetBeatFromElapsedTime( fEndSecond );
|
||||
fEndBeat = truncf(fEndBeat)+1;
|
||||
|
||||
// loading the course should have caught this.
|
||||
|
||||
@@ -99,7 +99,7 @@ void AutoKeysounds::LoadAutoplaySoundsInto( RageSoundReader_Chain *pChain )
|
||||
if( tn[pn].iKeysoundIndex >= 0 )
|
||||
{
|
||||
RString sKeysoundFilePath = sSongDir + pSong->m_vsKeysoundFile[tn[pn].iKeysoundIndex];
|
||||
float fSeconds = pSong->m_Timing.GetElapsedTimeFromBeatNoOffset( NoteRowToBeat(iRow) ) + SOUNDMAN->GetPlayLatency();
|
||||
float fSeconds = GAMESTATE->m_pCurSteps[pn]->m_Timing.GetElapsedTimeFromBeatNoOffset( NoteRowToBeat(iRow) ) + SOUNDMAN->GetPlayLatency();
|
||||
|
||||
float fPan = 0;
|
||||
if( !bSoundIsGlobal )
|
||||
|
||||
+1
-1
@@ -277,7 +277,7 @@ SongBPMDisplay::SongBPMDisplay()
|
||||
|
||||
void SongBPMDisplay::Update( float fDeltaTime )
|
||||
{
|
||||
float fGameStateBPM = GAMESTATE->m_fCurBPS * 60.0f;
|
||||
float fGameStateBPM = GAMESTATE->m_Position.m_fCurBPS * 60.0f;
|
||||
if( m_fLastGameStateBPM != fGameStateBPM )
|
||||
{
|
||||
m_fLastGameStateBPM = fGameStateBPM;
|
||||
|
||||
+5
-5
@@ -419,7 +419,7 @@ void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const Bac
|
||||
int iStartRow = BeatToNoteRow(fFirstBeat);
|
||||
int iEndRow = BeatToNoteRow(fEndBeat);
|
||||
|
||||
const TimingData &timing = m_pSong->m_Timing;
|
||||
const TimingData &timing = m_pSong->m_SongTiming;
|
||||
|
||||
// change BG every time signature change or 4 measures
|
||||
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, iter )
|
||||
@@ -697,7 +697,7 @@ void BackgroundImpl::Layer::UpdateCurBGChange( const Song *pSong, float fLastMus
|
||||
float fBeat, fBPS, fThrowAway;
|
||||
bool bFreeze;
|
||||
int iThrowAway;
|
||||
pSong->m_Timing.GetBeatAndBPSFromElapsedTime( fCurrentTime, fBeat, fBPS, bFreeze, bFreeze, iThrowAway, fThrowAway );
|
||||
pSong->m_SongTiming.GetBeatAndBPSFromElapsedTime( fCurrentTime, fBeat, fBPS, bFreeze, bFreeze, iThrowAway, fThrowAway );
|
||||
|
||||
// Calls to Update() should *not* be scaled by music rate; fCurrentTime is. Undo it.
|
||||
const float fRate = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
|
||||
@@ -762,7 +762,7 @@ void BackgroundImpl::Layer::UpdateCurBGChange( const Song *pSong, float fLastMus
|
||||
m_pCurrentBGA->PlayCommand( "GainFocus" );
|
||||
|
||||
/* How much time of this BGA have we skipped? (This happens with SetSeconds.) */
|
||||
const float fStartSecond = pSong->m_Timing.GetElapsedTimeFromBeat( change.m_fStartBeat );
|
||||
const float fStartSecond = pSong->m_SongTiming.GetElapsedTimeFromBeat( change.m_fStartBeat );
|
||||
|
||||
/* This is affected by the music rate. */
|
||||
fDeltaTime = fCurrentTime - fStartSecond;
|
||||
@@ -800,9 +800,9 @@ void BackgroundImpl::Update( float fDeltaTime )
|
||||
FOREACH_BackgroundLayer( i )
|
||||
{
|
||||
Layer &layer = m_Layer[i];
|
||||
layer.UpdateCurBGChange( m_pSong, m_fLastMusicSeconds, GAMESTATE->m_fMusicSeconds, m_mapNameToTransition );
|
||||
layer.UpdateCurBGChange( m_pSong, m_fLastMusicSeconds, GAMESTATE->m_Position.m_fMusicSeconds, m_mapNameToTransition );
|
||||
}
|
||||
m_fLastMusicSeconds = GAMESTATE->m_fMusicSeconds;
|
||||
m_fLastMusicSeconds = GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
}
|
||||
|
||||
void BackgroundImpl::DrawPrimitives()
|
||||
|
||||
@@ -191,7 +191,7 @@ void BeginnerHelper::ShowStepCircle( PlayerNumber pn, int CSTEP )
|
||||
m_sStepCircle[pn][isc].StopEffect();
|
||||
m_sStepCircle[pn][isc].SetZoom( 2 );
|
||||
m_sStepCircle[pn][isc].StopTweening();
|
||||
m_sStepCircle[pn][isc].BeginTweening( GAMESTATE->m_fCurBPS/3, TWEEN_LINEAR );
|
||||
m_sStepCircle[pn][isc].BeginTweening( GAMESTATE->m_Position.m_fCurBPS/3, TWEEN_LINEAR );
|
||||
m_sStepCircle[pn][isc].SetZoom( 0 );
|
||||
}
|
||||
|
||||
@@ -318,19 +318,19 @@ void BeginnerHelper::Step( PlayerNumber pn, int CSTEP )
|
||||
ShowStepCircle( pn, ST_DOWN );
|
||||
m_pDancer[pn]->StopTweening();
|
||||
m_pDancer[pn]->PlayAnimation( "Step-JUMPLR", 1.5f );
|
||||
m_pDancer[pn]->BeginTweening( GAMESTATE->m_fCurBPS/8, TWEEN_LINEAR );
|
||||
m_pDancer[pn]->BeginTweening( GAMESTATE->m_Position.m_fCurBPS/8, TWEEN_LINEAR );
|
||||
m_pDancer[pn]->SetRotationY( 90 );
|
||||
m_pDancer[pn]->BeginTweening( 1/(GAMESTATE->m_fCurBPS * 2) ); //sleep between jump-frames
|
||||
m_pDancer[pn]->BeginTweening( GAMESTATE->m_fCurBPS /6, TWEEN_LINEAR );
|
||||
m_pDancer[pn]->BeginTweening( 1/(GAMESTATE->m_Position.m_fCurBPS * 2) ); //sleep between jump-frames
|
||||
m_pDancer[pn]->BeginTweening( GAMESTATE->m_Position.m_fCurBPS /6, TWEEN_LINEAR );
|
||||
m_pDancer[pn]->SetRotationY( 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
m_sFlash.StopEffect();
|
||||
m_sFlash.StopTweening();
|
||||
m_sFlash.Sleep( GAMESTATE->m_fCurBPS/16 );
|
||||
m_sFlash.Sleep( GAMESTATE->m_Position.m_fCurBPS/16 );
|
||||
m_sFlash.SetDiffuseAlpha( 1 );
|
||||
m_sFlash.BeginTweening( 1/GAMESTATE->m_fCurBPS * 0.5f );
|
||||
m_sFlash.BeginTweening( 1/GAMESTATE->m_Position.m_fCurBPS * 0.5f );
|
||||
m_sFlash.SetDiffuseAlpha( 0 );
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ void BeginnerHelper::Update( float fDeltaTime )
|
||||
return;
|
||||
|
||||
// the row we want to check on this update
|
||||
int iCurRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat + 0.4f );
|
||||
int iCurRow = BeatToNoteRowNotRounded( GAMESTATE->m_Position.m_fSongBeat + 0.4f );
|
||||
FOREACH_EnabledPlayer( pn )
|
||||
{
|
||||
for( int iRow=m_iLastRowChecked; iRow<iCurRow; iRow++ )
|
||||
@@ -369,7 +369,7 @@ void BeginnerHelper::Update( float fDeltaTime )
|
||||
m_pDancePad->Update( fDeltaTime );
|
||||
m_sFlash.Update( fDeltaTime );
|
||||
|
||||
float beat = fDeltaTime*GAMESTATE->m_fCurBPS;
|
||||
float beat = fDeltaTime*GAMESTATE->m_Position.m_fCurBPS;
|
||||
// If this is not a human player, the dancer is not shown
|
||||
FOREACH_HumanPlayer( pu )
|
||||
{
|
||||
|
||||
@@ -175,7 +175,7 @@ int Neg1OrPos1() { return RandomInt( 2 ) ? -1 : +1; }
|
||||
|
||||
void DancingCharacters::Update( float fDelta )
|
||||
{
|
||||
if( GAMESTATE->m_bFreeze || GAMESTATE->m_bDelay )
|
||||
if( GAMESTATE->m_Position.m_bFreeze || GAMESTATE->m_Position.m_bDelay )
|
||||
{
|
||||
// spin the camera Matrix-style
|
||||
m_CameraPanYStart += fDelta*40;
|
||||
@@ -184,7 +184,7 @@ void DancingCharacters::Update( float fDelta )
|
||||
else
|
||||
{
|
||||
// make the characters move
|
||||
float fBPM = GAMESTATE->m_fCurBPS*60;
|
||||
float fBPM = GAMESTATE->m_Position.m_fCurBPS*60;
|
||||
float fUpdateScale = SCALE( fBPM, 60.f, 300.f, 0.75f, 1.5f );
|
||||
CLAMP( fUpdateScale, 0.75f, 1.5f );
|
||||
|
||||
@@ -209,8 +209,8 @@ void DancingCharacters::Update( float fDelta )
|
||||
}
|
||||
bWasGameplayStarting = bGameplayStarting;
|
||||
|
||||
static float fLastBeat = GAMESTATE->m_fSongBeat;
|
||||
float fThisBeat = GAMESTATE->m_fSongBeat;
|
||||
static float fLastBeat = GAMESTATE->m_Position.m_fSongBeat;
|
||||
float fThisBeat = GAMESTATE->m_Position.m_fSongBeat;
|
||||
if( fLastBeat < GAMESTATE->m_pCurSong->m_fFirstBeat &&
|
||||
fThisBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat )
|
||||
{
|
||||
@@ -220,7 +220,7 @@ void DancingCharacters::Update( float fDelta )
|
||||
fLastBeat = fThisBeat;
|
||||
|
||||
// time for a new sweep?
|
||||
if( GAMESTATE->m_fSongBeat > m_fThisCameraEndBeat )
|
||||
if( GAMESTATE->m_Position.m_fSongBeat > m_fThisCameraEndBeat )
|
||||
{
|
||||
if( RandomInt(6) >= 4 )
|
||||
{
|
||||
@@ -248,7 +248,7 @@ void DancingCharacters::Update( float fDelta )
|
||||
m_fLookAtHeight = CAMERA_STILL_LOOK_AT_HEIGHT;
|
||||
}
|
||||
|
||||
int iCurBeat = (int)GAMESTATE->m_fSongBeat;
|
||||
int iCurBeat = (int)GAMESTATE->m_Position.m_fSongBeat;
|
||||
iCurBeat -= iCurBeat%8;
|
||||
|
||||
m_fThisCameraStartBeat = (float) iCurBeat;
|
||||
@@ -313,7 +313,7 @@ void DancingCharacters::DrawPrimitives()
|
||||
if(m_fThisCameraStartBeat == m_fThisCameraEndBeat)
|
||||
fPercentIntoSweep = 0;
|
||||
else
|
||||
fPercentIntoSweep = SCALE(GAMESTATE->m_fSongBeat, m_fThisCameraStartBeat, m_fThisCameraEndBeat, 0.f, 1.f );
|
||||
fPercentIntoSweep = SCALE(GAMESTATE->m_Position.m_fSongBeat, m_fThisCameraStartBeat, m_fThisCameraEndBeat, 0.f, 1.f );
|
||||
float fCameraPanY = SCALE( fPercentIntoSweep, 0.f, 1.f, m_CameraPanYStart, m_CameraPanYEnd );
|
||||
float fCameraHeight = SCALE( fPercentIntoSweep, 0.f, 1.f, m_fCameraHeightStart, m_fCameraHeightEnd );
|
||||
|
||||
|
||||
+8
-8
@@ -41,9 +41,9 @@ void Foreground::LoadFromSong( const Song *pSong )
|
||||
bga.m_fStartBeat = change.m_fStartBeat;
|
||||
bga.m_bFinished = false;
|
||||
|
||||
const float fStartSecond = pSong->m_Timing.GetElapsedTimeFromBeat( bga.m_fStartBeat );
|
||||
const float fStartSecond = pSong->m_SongTiming.GetElapsedTimeFromBeat( bga.m_fStartBeat );
|
||||
const float fStopSecond = fStartSecond + bga.m_bga->GetTweenTimeLeft();
|
||||
bga.m_fStopBeat = pSong->m_Timing.GetBeatFromElapsedTime( fStopSecond );
|
||||
bga.m_fStopBeat = pSong->m_SongTiming.GetBeatFromElapsedTime( fStopSecond );
|
||||
|
||||
bga.m_bga->SetVisible( false );
|
||||
|
||||
@@ -65,7 +65,7 @@ void Foreground::Update( float fDeltaTime )
|
||||
{
|
||||
LoadedBGA &bga = m_BGAnimations[i];
|
||||
|
||||
if( GAMESTATE->m_fSongBeat < bga.m_fStartBeat )
|
||||
if( GAMESTATE->m_Position.m_fSongBeat < bga.m_fStartBeat )
|
||||
{
|
||||
// The animation hasn't started yet.
|
||||
continue;
|
||||
@@ -82,12 +82,12 @@ void Foreground::Update( float fDeltaTime )
|
||||
bga.m_bga->SetVisible( true );
|
||||
bga.m_bga->PlayCommand( "On" );
|
||||
|
||||
const float fStartSecond = m_pSong->m_Timing.GetElapsedTimeFromBeat( bga.m_fStartBeat );
|
||||
lDeltaTime = GAMESTATE->m_fMusicSeconds - fStartSecond;
|
||||
const float fStartSecond = m_pSong->m_SongTiming.GetElapsedTimeFromBeat( bga.m_fStartBeat );
|
||||
lDeltaTime = GAMESTATE->m_Position.m_fMusicSeconds - fStartSecond;
|
||||
}
|
||||
else
|
||||
{
|
||||
lDeltaTime = GAMESTATE->m_fMusicSeconds - m_fLastMusicSeconds;
|
||||
lDeltaTime = GAMESTATE->m_Position.m_fMusicSeconds - m_fLastMusicSeconds;
|
||||
}
|
||||
|
||||
// This shouldn't go down, but be safe:
|
||||
@@ -95,7 +95,7 @@ void Foreground::Update( float fDeltaTime )
|
||||
|
||||
bga.m_bga->Update( lDeltaTime / fRate );
|
||||
|
||||
if( GAMESTATE->m_fSongBeat > bga.m_fStopBeat )
|
||||
if( GAMESTATE->m_Position.m_fSongBeat > bga.m_fStopBeat )
|
||||
{
|
||||
// Finished.
|
||||
bga.m_bga->SetVisible( false );
|
||||
@@ -104,7 +104,7 @@ void Foreground::Update( float fDeltaTime )
|
||||
}
|
||||
}
|
||||
|
||||
m_fLastMusicSeconds = GAMESTATE->m_fMusicSeconds;
|
||||
m_fLastMusicSeconds = GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+14
-14
@@ -146,7 +146,7 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
SSCLoader::LoadFromSSCFile(ToPlay.m_sTimingFile, song) )
|
||||
{
|
||||
ToPlay.HasTiming = true;
|
||||
ToPlay.m_TimingData = song.m_Timing;
|
||||
ToPlay.m_TimingData = song.m_SongTiming;
|
||||
// get cabinet lights if any
|
||||
Steps *pStepsCabinetLights = SongUtil::GetOneSteps( &song, StepsType_lights_cabinet );
|
||||
if( pStepsCabinetLights )
|
||||
@@ -156,7 +156,7 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
SMLoader::LoadFromSMFile(ToPlay.m_sTimingFile, song) )
|
||||
{
|
||||
ToPlay.HasTiming = true;
|
||||
ToPlay.m_TimingData = song.m_Timing;
|
||||
ToPlay.m_TimingData = song.m_SongTiming;
|
||||
// get cabinet lights if any
|
||||
Steps *pStepsCabinetLights = SongUtil::GetOneSteps( &song, StepsType_lights_cabinet );
|
||||
if( pStepsCabinetLights )
|
||||
@@ -201,7 +201,7 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
{
|
||||
/* This song has no real timing data. The offset is arbitrary. Change it so
|
||||
* the beat will line up to where we are now, so we don't have to delay. */
|
||||
float fDestBeat = fmodfp( GAMESTATE->m_fSongBeatNoOffset, 1 );
|
||||
float fDestBeat = fmodfp( GAMESTATE->m_Position.m_fSongBeatNoOffset, 1 );
|
||||
float fTime = NewMusic->m_NewTiming.GetElapsedTimeFromBeatNoOffset( fDestBeat );
|
||||
|
||||
NewMusic->m_NewTiming.m_fBeat0OffsetInSeconds = fTime;
|
||||
@@ -223,7 +223,7 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
* common when starting a precached sound, but our sound isn't, so it'll
|
||||
* probably take a little longer. Nudge the latency up. */
|
||||
const float fPresumedLatency = SOUNDMAN->GetPlayLatency() + 0.040f;
|
||||
const float fCurSecond = GAMESTATE->m_fMusicSeconds + fPresumedLatency;
|
||||
const float fCurSecond = GAMESTATE->m_Position.m_fMusicSeconds + fPresumedLatency;
|
||||
const float fCurBeat = g_Playing->m_Timing.GetBeatFromElapsedTimeNoOffset( fCurSecond );
|
||||
|
||||
/* The beat that the new sound will start on. */
|
||||
@@ -236,9 +236,9 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
|
||||
const float fSecondToStartOn = g_Playing->m_Timing.GetElapsedTimeFromBeatNoOffset( fCurBeatToStartOn );
|
||||
const float fMaximumDistance = 2;
|
||||
const float fDistance = min( fSecondToStartOn - GAMESTATE->m_fMusicSeconds, fMaximumDistance );
|
||||
const float fDistance = min( fSecondToStartOn - GAMESTATE->m_Position.m_fMusicSeconds, fMaximumDistance );
|
||||
|
||||
when = GAMESTATE->m_LastBeatUpdate + fDistance;
|
||||
when = GAMESTATE->m_Position.m_LastBeatUpdate + fDistance;
|
||||
}
|
||||
|
||||
/* Important: don't hold the mutex while we load and seek the actual sound. */
|
||||
@@ -552,8 +552,8 @@ void GameSoundManager::Update( float fDeltaTime )
|
||||
if( !g_Playing->m_Music->IsPlaying() )
|
||||
{
|
||||
/* There's no song playing. Fake it. */
|
||||
CHECKPOINT_M( ssprintf("%f, delta %f", GAMESTATE->m_fMusicSeconds, fDeltaTime) );
|
||||
GAMESTATE->UpdateSongPosition( GAMESTATE->m_fMusicSeconds + fDeltaTime, g_Playing->m_Timing );
|
||||
CHECKPOINT_M( ssprintf("%f, delta %f", GAMESTATE->m_Position.m_fMusicSeconds, fDeltaTime) );
|
||||
GAMESTATE->UpdateSongPosition( GAMESTATE->m_Position.m_fMusicSeconds + fDeltaTime, g_Playing->m_Timing );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -570,8 +570,8 @@ void GameSoundManager::Update( float fDeltaTime )
|
||||
//
|
||||
if( PREFSMAN->m_bLogSkips && !g_Playing->m_bTimingDelayed )
|
||||
{
|
||||
const float fExpectedTimePassed = (tm - GAMESTATE->m_LastBeatUpdate) * g_Playing->m_Music->GetPlaybackRate();
|
||||
const float fSoundTimePassed = fSeconds - GAMESTATE->m_fMusicSeconds;
|
||||
const float fExpectedTimePassed = (tm - GAMESTATE->m_Position.m_LastBeatUpdate) * g_Playing->m_Music->GetPlaybackRate();
|
||||
const float fSoundTimePassed = fSeconds - GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
const float fDiff = fExpectedTimePassed - fSoundTimePassed;
|
||||
|
||||
static RString sLastFile = "";
|
||||
@@ -580,7 +580,7 @@ void GameSoundManager::Update( float fDeltaTime )
|
||||
/* If fSoundTimePassed < 0, the sound has probably looped. */
|
||||
if( sLastFile == ThisFile && fSoundTimePassed >= 0 && fabsf(fDiff) > 0.003f )
|
||||
LOG->Trace("Song position skip in %s: expected %.3f, got %.3f (cur %f, prev %f) (%.3f difference)",
|
||||
Basename(ThisFile).c_str(), fExpectedTimePassed, fSoundTimePassed, fSeconds, GAMESTATE->m_fMusicSeconds, fDiff );
|
||||
Basename(ThisFile).c_str(), fExpectedTimePassed, fSoundTimePassed, fSeconds, GAMESTATE->m_Position.m_fMusicSeconds, fDiff );
|
||||
sLastFile = ThisFile;
|
||||
}
|
||||
|
||||
@@ -599,7 +599,7 @@ void GameSoundManager::Update( float fDeltaTime )
|
||||
{
|
||||
/* We're still waiting for the new sound to start playing, so keep using the
|
||||
* old timing data and fake the time. */
|
||||
GAMESTATE->UpdateSongPosition( GAMESTATE->m_fMusicSeconds + fDeltaTime, g_Playing->m_Timing );
|
||||
GAMESTATE->UpdateSongPosition( GAMESTATE->m_Position.m_fMusicSeconds + fDeltaTime, g_Playing->m_Timing );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -614,7 +614,7 @@ void GameSoundManager::Update( float fDeltaTime )
|
||||
{
|
||||
static int iBeatLastCrossed = 0;
|
||||
|
||||
float fSongBeat = GAMESTATE->m_fSongBeat;
|
||||
float fSongBeat = GAMESTATE->m_Position.m_fSongBeat;
|
||||
|
||||
int iRowNow = BeatToNoteRowNotRounded( fSongBeat );
|
||||
iRowNow = max( 0, iRowNow );
|
||||
@@ -638,7 +638,7 @@ void GameSoundManager::Update( float fDeltaTime )
|
||||
NoteData &lights = g_Playing->m_Lights;
|
||||
if( lights.GetNumTracks() > 0 ) // lights data was loaded
|
||||
{
|
||||
const float fSongBeat = GAMESTATE->m_fLightSongBeat;
|
||||
const float fSongBeat = GAMESTATE->m_Position.m_fLightSongBeat;
|
||||
const int iSongRow = BeatToNoteRowNotRounded( fSongBeat );
|
||||
|
||||
static int iRowLastCrossed = 0;
|
||||
|
||||
+33
-66
@@ -127,7 +127,9 @@ GameState::GameState() :
|
||||
m_pEditSourceSteps( Message_EditSourceStepsChanged ),
|
||||
m_stEditSource( Message_EditSourceStepsTypeChanged ),
|
||||
m_iEditCourseEntryIndex( Message_EditCourseEntryIndexChanged ),
|
||||
m_sEditLocalProfileID( Message_EditLocalProfileIDChanged )
|
||||
m_sEditLocalProfileID( Message_EditLocalProfileIDChanged ),
|
||||
m_bIsUsingStepTiming( true ),
|
||||
m_bInStepEditor( false )
|
||||
{
|
||||
g_pImpl = new GameStateImpl;
|
||||
|
||||
@@ -893,22 +895,15 @@ const float GameState::MUSIC_SECONDS_INVALID = -5000.0f;
|
||||
|
||||
void GameState::ResetMusicStatistics()
|
||||
{
|
||||
m_fMusicSeconds = 0; // MUSIC_SECONDS_INVALID;
|
||||
// todo: move me to FOREACH_EnabledPlayer( p ) after [NUM_PLAYERS]ing
|
||||
m_fSongBeat = 0;
|
||||
m_fSongBeatNoOffset = 0;
|
||||
m_fCurBPS = 10;
|
||||
//m_bStop = false;
|
||||
m_bFreeze = false;
|
||||
m_bDelay = false;
|
||||
m_iWarpBeginRow = -1; // Set to -1 because some song may want to warp to row 0. -aj
|
||||
m_fWarpDestination = -1; // Set when a warp is encountered. also see above. -aj
|
||||
m_fMusicSecondsVisible = 0;
|
||||
m_fSongBeatVisible = 0;
|
||||
m_Position.Reset();
|
||||
|
||||
Actor::SetBGMTime( 0, 0, 0, 0 );
|
||||
|
||||
FOREACH_PlayerNumber( p )
|
||||
{
|
||||
m_pPlayerState[p]->ClearHopoState();
|
||||
m_pPlayerState[p]->m_Position.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::ResetStageStatistics()
|
||||
@@ -947,57 +942,23 @@ void GameState::ResetStageStatistics()
|
||||
m_iStageSeed = rand();
|
||||
}
|
||||
|
||||
static Preference<float> g_fVisualDelaySeconds( "VisualDelaySeconds", 0.0f );
|
||||
|
||||
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp )
|
||||
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp, bool bUpdatePlayers )
|
||||
{
|
||||
if( !timestamp.IsZero() )
|
||||
m_LastBeatUpdate = timestamp;
|
||||
else
|
||||
m_LastBeatUpdate.Touch();
|
||||
|
||||
m_Position.UpdateSongPosition( fPositionSeconds, timing, timestamp );
|
||||
|
||||
// xxx testing: only do this on monotune survivor
|
||||
/*
|
||||
if( m_pCurSong && m_pCurSong->GetDisplayFullTitle() == "monotune survivor" )
|
||||
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_fWarpDestination );
|
||||
// "Crash reason : -243478.890625 -48695.773438"
|
||||
ASSERT_M( m_fSongBeat > -2000, ssprintf("Song beat %f at %f seconds", m_fSongBeat, fPositionSeconds) );
|
||||
|
||||
m_fMusicSeconds = fPositionSeconds;
|
||||
|
||||
m_fLightSongBeat = timing.GetBeatFromElapsedTime( fPositionSeconds + g_fLightsAheadSeconds );
|
||||
|
||||
m_fSongBeatNoOffset = timing.GetBeatFromElapsedTimeNoOffset( fPositionSeconds );
|
||||
|
||||
m_fMusicSecondsVisible = fPositionSeconds - g_fVisualDelaySeconds.Get();
|
||||
float fThrowAway, fThrowAway2;
|
||||
bool bThrowAway;
|
||||
int iThrowAway;
|
||||
timing.GetBeatAndBPSFromElapsedTime( m_fMusicSecondsVisible, m_fSongBeatVisible, fThrowAway, bThrowAway, bThrowAway, iThrowAway, fThrowAway2 );
|
||||
|
||||
/*
|
||||
// xxx testing: only do this on monotune survivor
|
||||
if( m_pCurSong && m_pCurSong->GetDisplayFullTitle() == "monotune survivor" )
|
||||
if( bUpdatePlayers )
|
||||
{
|
||||
// and only do it in the known negative bpm region. HACKITY HACK
|
||||
if(m_fSongBeat >= 445.490f && m_fSongBeat <= 453.72f)
|
||||
FOREACH_EnabledPlayer( pn )
|
||||
{
|
||||
LOG->Trace( ssprintf("fPositionSeconds = %f",fPositionSeconds) );
|
||||
LOG->Trace( ssprintf("Song beat: %f (%f seconds), BPS = %f (%f BPM)",m_fSongBeat,m_fMusicSecondsVisible,m_fCurBPS,m_fCurBPS*60.0f) );
|
||||
//LOG->Trace( ssprintf("Music seconds visible %f = fPositionSeconds %f - g_fVisualDelaySeconds %f", m_fMusicSecondsVisible,fPositionSeconds,g_fVisualDelaySeconds.Get()) );
|
||||
}
|
||||
else if(m_fSongBeat == 445.500f)
|
||||
{
|
||||
LOG->Trace( ssprintf("[beat 445.500] fPositionSeconds = %f",fPositionSeconds) );
|
||||
LOG->Trace( ssprintf("Song beat: %f (%f seconds), BPS = %f (%f BPM)",m_fSongBeat,m_fMusicSecondsVisible,m_fCurBPS,m_fCurBPS*60.0f) );
|
||||
if( m_pCurSteps[pn] )
|
||||
{
|
||||
m_pPlayerState[pn]->m_Position.UpdateSongPosition( fPositionSeconds, m_pCurSteps[pn]->m_Timing, timestamp );
|
||||
Actor::SetPlayerBGMBeat( pn, m_pPlayerState[pn]->m_Position.m_fSongBeatVisible, m_pPlayerState[pn]->m_Position.m_fSongBeatNoOffset );
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Actor::SetBGMTime( m_fMusicSecondsVisible, m_fSongBeatVisible, fPositionSeconds, m_fSongBeatNoOffset );
|
||||
Actor::SetBGMTime( GAMESTATE->m_Position.m_fMusicSecondsVisible, GAMESTATE->m_Position.m_fSongBeatVisible, fPositionSeconds, GAMESTATE->m_Position.m_fSongBeatNoOffset );
|
||||
// LOG->Trace( "m_fMusicSeconds = %f, m_fSongBeat = %f, m_fCurBPS = %f, m_bFreeze = %f", m_fMusicSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze );
|
||||
}
|
||||
|
||||
@@ -2291,11 +2252,16 @@ public:
|
||||
DEFINE_METHOD( GetHardestStepsDifficulty, GetHardestStepsDifficulty() )
|
||||
DEFINE_METHOD( IsEventMode, IsEventMode() )
|
||||
DEFINE_METHOD( GetNumPlayersEnabled, GetNumPlayersEnabled() )
|
||||
DEFINE_METHOD( GetSongBeat, m_fSongBeat )
|
||||
DEFINE_METHOD( GetSongBeatVisible, m_fSongBeatVisible )
|
||||
DEFINE_METHOD( GetSongBPS, m_fCurBPS )
|
||||
DEFINE_METHOD( GetSongFreeze, m_bFreeze )
|
||||
DEFINE_METHOD( GetSongDelay, m_bDelay )
|
||||
/*DEFINE_METHOD( GetSongBeat, m_Position.m_fSongBeat )
|
||||
DEFINE_METHOD( GetSongBeatVisible, m_Position.m_fSongBeatVisible )
|
||||
DEFINE_METHOD( GetSongBPS, m_Position.m_fCurBPS )
|
||||
DEFINE_METHOD( GetSongFreeze, m_Position.m_bFreeze )
|
||||
DEFINE_METHOD( GetSongDelay, m_Position.m_bDelay )*/
|
||||
static int GetSongPosition( T* p, lua_State *L )
|
||||
{
|
||||
p->m_Position.PushSelf(L);
|
||||
return 1;
|
||||
}
|
||||
DEFINE_METHOD( GetGameplayLeadIn, m_bGameplayLeadIn )
|
||||
DEFINE_METHOD( GetCoins, m_iCoins )
|
||||
DEFINE_METHOD( IsSideJoined, m_bSideIsJoined[Enum::Check<PlayerNumber>(L, 1)] )
|
||||
@@ -2434,7 +2400,7 @@ public:
|
||||
static int JoinPlayer( T* p, lua_State *L ) { p->JoinPlayer(Enum::Check<PlayerNumber>(L, 1)); return 0; }
|
||||
static int UnjoinPlayer( T* p, lua_State *L ) { p->UnjoinPlayer(Enum::Check<PlayerNumber>(L, 1)); return 0; }
|
||||
static int GetSongPercent( T* p, lua_State *L ) { lua_pushnumber(L, p->GetSongPercent(FArg(1))); return 1; }
|
||||
DEFINE_METHOD( GetCurMusicSeconds, m_fMusicSeconds )
|
||||
DEFINE_METHOD( GetCurMusicSeconds, m_Position.m_fMusicSeconds )
|
||||
|
||||
DEFINE_METHOD( GetWorkoutGoalComplete, m_bWorkoutGoalComplete )
|
||||
static int GetCharacter( T* p, lua_State *L ) { p->m_pCurCharacters[Enum::Check<PlayerNumber>(L, 1)]->PushSelf(L); return 1; }
|
||||
@@ -2500,11 +2466,12 @@ public:
|
||||
ADD_METHOD( GetHardestStepsDifficulty );
|
||||
ADD_METHOD( IsEventMode );
|
||||
ADD_METHOD( GetNumPlayersEnabled );
|
||||
ADD_METHOD( GetSongBeat );
|
||||
/*ADD_METHOD( GetSongBeat );
|
||||
ADD_METHOD( GetSongBeatVisible );
|
||||
ADD_METHOD( GetSongBPS );
|
||||
ADD_METHOD( GetSongFreeze );
|
||||
ADD_METHOD( GetSongDelay );
|
||||
ADD_METHOD( GetSongDelay );*/
|
||||
ADD_METHOD( GetSongPosition );
|
||||
ADD_METHOD( GetGameplayLeadIn );
|
||||
ADD_METHOD( GetCoins );
|
||||
ADD_METHOD( IsSideJoined );
|
||||
|
||||
+44
-29
@@ -10,6 +10,7 @@
|
||||
#include "RageTimer.h"
|
||||
#include "PlayerOptions.h"
|
||||
#include "SongOptions.h"
|
||||
#include "SongPosition.h"
|
||||
#include "Preference.h"
|
||||
|
||||
#include <map>
|
||||
@@ -43,7 +44,8 @@ public:
|
||||
void ResetPlayer( PlayerNumber pn );
|
||||
void ApplyCmdline(); // called by Reset
|
||||
void ApplyGameCommand( const RString &sCommand, PlayerNumber pn=PLAYER_INVALID );
|
||||
void BeginGame(); // called when first player joins
|
||||
/** @brief Start the game when the first player joins in. */
|
||||
void BeginGame();
|
||||
void JoinPlayer( PlayerNumber pn );
|
||||
void UnjoinPlayer( PlayerNumber pn );
|
||||
bool JoinInput( PlayerNumber pn );
|
||||
@@ -55,13 +57,25 @@ public:
|
||||
bool HaveProfileToSave();
|
||||
void SaveLocalData();
|
||||
void LoadCurrentSettingsFromProfile( PlayerNumber pn );
|
||||
void SaveCurrentSettingsToProfile( PlayerNumber pn ); // called at the beginning of each stage
|
||||
/**
|
||||
* @brief Save the specified player's settings to his/her profile.
|
||||
*
|
||||
* This is called at the beginning of each stage.
|
||||
* @param pn the PlayerNumber to save the stats to. */
|
||||
void SaveCurrentSettingsToProfile( PlayerNumber pn );
|
||||
Song* GetDefaultSong() const;
|
||||
|
||||
void Update( float fDelta );
|
||||
|
||||
// Main state info
|
||||
void SetCurGame( const Game *pGame ); // Call this instead of m_pCurGame.Set to make sure PREFSMAN->m_sCurrentGame stays in sync
|
||||
|
||||
/**
|
||||
* @brief State what the current game is.
|
||||
*
|
||||
* Call this instead of m_pCurGame.Set to make sure that
|
||||
* PREFSMAN->m_sCurrentGame stays in sync.
|
||||
* @param pGame the game to start using. */
|
||||
void SetCurGame( const Game *pGame );
|
||||
BroadcastOnChangePtr<const Game> m_pCurGame;
|
||||
BroadcastOnChangePtr<const Style> m_pCurStyle;
|
||||
/** @brief Determine which side is joined.
|
||||
@@ -129,6 +143,9 @@ public:
|
||||
bool IsCourseMode() const;
|
||||
bool IsBattleMode() const; // not Rave
|
||||
|
||||
/**
|
||||
* @brief Do we show the W1 timing judgment?
|
||||
* @return true if we do, or false otherwise. */
|
||||
bool ShowW1() const;
|
||||
|
||||
BroadcastOnChange<RString> m_sPreferredSongGroup; // GROUP_ALL denotes no preferred group
|
||||
@@ -141,7 +158,11 @@ public:
|
||||
SortOrder m_PreferredSortOrder; // used by MusicWheel
|
||||
EditMode m_EditMode;
|
||||
bool IsEditing() const { return m_EditMode != EditMode_Invalid; }
|
||||
bool m_bDemonstrationOrJukebox; // ScreenGameplay does special stuff when this is true
|
||||
/**
|
||||
* @brief Are we in the demonstration or jukebox mode?
|
||||
*
|
||||
* ScreenGameplay often does special things when this is set to true. */
|
||||
bool m_bDemonstrationOrJukebox;
|
||||
bool m_bJukeboxUsesModifiers;
|
||||
int m_iNumStagesOfThisSong;
|
||||
/**
|
||||
@@ -194,29 +215,9 @@ public:
|
||||
bool m_bBackedOutOfFinalStage;
|
||||
|
||||
// Music statistics:
|
||||
// Arcade - the current stage (one song).
|
||||
// Oni/Endless - a single song in a course.
|
||||
// Let a lot of classes access this info here so they don't have to keep their own copies.
|
||||
// todo: [NUM_PLAYERS] this for split bpm lolol -aj
|
||||
float m_fMusicSeconds; // time into the current song, not scaled by music rate
|
||||
float m_fSongBeat;
|
||||
float m_fSongBeatNoOffset;
|
||||
float m_fCurBPS;
|
||||
float m_fLightSongBeat; // g_fLightsFalloffSeconds ahead
|
||||
//bool m_bStop; // in the middle of a stop (freeze or delay)
|
||||
/** @brief A flag to determine if we're in the middle of a freeze/stop. */
|
||||
bool m_bFreeze;
|
||||
/** @brief A flag to determine if we're in the middle of a delay (Pump style stop). */
|
||||
bool m_bDelay;
|
||||
/** @brief The row used to start a warp. */
|
||||
int m_iWarpBeginRow;
|
||||
/** @brief The beat to warp to afterwards. */
|
||||
float m_fWarpDestination;
|
||||
RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
|
||||
BroadcastOnChange<bool> m_bGameplayLeadIn;
|
||||
SongPosition m_Position;
|
||||
|
||||
float m_fMusicSecondsVisible;
|
||||
float m_fSongBeatVisible;
|
||||
BroadcastOnChange<bool> m_bGameplayLeadIn;
|
||||
|
||||
// if re-adding noteskin changes in courses, add functions and such here -aj
|
||||
void GetAllUsedNoteSkins( vector<RString> &out ) const;
|
||||
@@ -224,7 +225,7 @@ public:
|
||||
static const float MUSIC_SECONDS_INVALID;
|
||||
|
||||
void ResetMusicStatistics(); // Call this when it's time to play a new song. Clears the values above.
|
||||
void UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp = RageZeroTimer );
|
||||
void UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp = RageZeroTimer, bool bUpdatePlayers = false );
|
||||
float GetSongPercent( float beat ) const;
|
||||
|
||||
bool AllAreInDangerOrWorse() const;
|
||||
@@ -259,8 +260,11 @@ public:
|
||||
// Options stuff
|
||||
ModsGroup<SongOptions> m_SongOptions;
|
||||
|
||||
// True if the current mode has changed the default NoteSkin, such as Edit/Sync Songs does.
|
||||
// Note: any mode that wants to use it must set it
|
||||
/**
|
||||
* @brief Did the current game mode change the default Noteskin?
|
||||
*
|
||||
* This is true if it has: see Edit/Sync Songs for a common example.
|
||||
* Note: any mode that wants to use this must set it explicitly. */
|
||||
bool m_bDidModeChangeNoteSkin;
|
||||
|
||||
void GetDefaultPlayerOptions( PlayerOptions &po );
|
||||
@@ -327,6 +331,17 @@ public:
|
||||
Premium GetPremium() const;
|
||||
|
||||
// Edit stuff
|
||||
|
||||
/**
|
||||
* @brief Is the game right now using Song timing or Steps timing?
|
||||
*
|
||||
* Different options are available depending on this setting. */
|
||||
bool m_bIsUsingStepTiming;
|
||||
/**
|
||||
* @brief Are we presently in the Step Editor, where some rules apply differently?
|
||||
*
|
||||
* TODO: Find a better way to implement this. */
|
||||
bool m_bInStepEditor;
|
||||
BroadcastOnChange<StepsType> m_stEdit;
|
||||
BroadcastOnChange<CourseDifficulty> m_cdEdit;
|
||||
BroadcastOnChangePtr<Steps> m_pEditSourceSteps;
|
||||
|
||||
@@ -25,9 +25,9 @@ void GameplayAssist::PlayTicks( const NoteData &nd )
|
||||
* will start coming out the speaker. Compensate for this by boosting fPositionSeconds
|
||||
* ahead. This is just to make sure that we request the sound early enough for it to
|
||||
* come out on time; the actual precise timing is handled by SetStartTime. */
|
||||
float fPositionSeconds = GAMESTATE->m_fMusicSeconds;
|
||||
float fPositionSeconds = GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
fPositionSeconds += SOUNDMAN->GetPlayLatency() + (float)CommonMetrics::TICK_EARLY_SECONDS + 0.250f;
|
||||
const TimingData &timing = GAMESTATE->m_pCurSong->m_Timing;
|
||||
const TimingData &timing = GAMESTATE->m_pCurSong->m_SongTiming;
|
||||
const float fSongBeat = timing.GetBeatFromElapsedTimeNoOffset( fPositionSeconds );
|
||||
|
||||
const int iSongRow = max( 0, BeatToNoteRowNotRounded( fSongBeat ) );
|
||||
@@ -47,11 +47,11 @@ void GameplayAssist::PlayTicks( const NoteData &nd )
|
||||
{
|
||||
const float fTickBeat = NoteRowToBeat( iClapRow );
|
||||
const float fTickSecond = timing.GetElapsedTimeFromBeatNoOffset( fTickBeat );
|
||||
float fSecondsUntil = fTickSecond - GAMESTATE->m_fMusicSeconds;
|
||||
float fSecondsUntil = fTickSecond - GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
fSecondsUntil /= GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; /* 2x music rate means the time until the tick is halved */
|
||||
|
||||
RageSoundParams p;
|
||||
p.m_StartTime = GAMESTATE->m_LastBeatUpdate + (fSecondsUntil - (float)CommonMetrics::TICK_EARLY_SECONDS);
|
||||
p.m_StartTime = GAMESTATE->m_Position.m_LastBeatUpdate + (fSecondsUntil - (float)CommonMetrics::TICK_EARLY_SECONDS);
|
||||
m_soundAssistClap.Play( &p );
|
||||
}
|
||||
}
|
||||
@@ -83,11 +83,11 @@ void GameplayAssist::PlayTicks( const NoteData &nd )
|
||||
{
|
||||
const float fTickBeat = NoteRowToBeat( iMetronomeRow );
|
||||
const float fTickSecond = timing.GetElapsedTimeFromBeatNoOffset( fTickBeat );
|
||||
float fSecondsUntil = fTickSecond - GAMESTATE->m_fMusicSeconds;
|
||||
float fSecondsUntil = fTickSecond - GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
fSecondsUntil /= GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; /* 2x music rate means the time until the tick is halved */
|
||||
|
||||
RageSoundParams p;
|
||||
p.m_StartTime = GAMESTATE->m_LastBeatUpdate + (fSecondsUntil - (float)CommonMetrics::TICK_EARLY_SECONDS);
|
||||
p.m_StartTime = GAMESTATE->m_Position.m_LastBeatUpdate + (fSecondsUntil - (float)CommonMetrics::TICK_EARLY_SECONDS);
|
||||
if( bIsMeasure )
|
||||
m_soundAssistMetronomeMeasure.Play( &p );
|
||||
else
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ void Inventory::Update( float fDelta )
|
||||
|
||||
// use items if this player is CPU-controlled
|
||||
if( m_pPlayerState->m_PlayerController != PC_HUMAN &&
|
||||
GAMESTATE->m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat )
|
||||
GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat )
|
||||
{
|
||||
// every 1 seconds, try to use an item
|
||||
int iLastSecond = (int)(RageTimer::GetTimeSinceStartFast() - fDelta);
|
||||
|
||||
@@ -230,12 +230,12 @@ void LightsManager::Update( float fDeltaTime )
|
||||
static float fLastBeat;
|
||||
static int iLight;
|
||||
|
||||
if( fracf(GAMESTATE->m_fLightSongBeat) < fracf(fLastBeat) )
|
||||
if( fracf(GAMESTATE->m_Position.m_fLightSongBeat) < fracf(fLastBeat) )
|
||||
{
|
||||
++iLight;
|
||||
wrap( iLight, 4 );
|
||||
}
|
||||
fLastBeat = GAMESTATE->m_fLightSongBeat;
|
||||
fLastBeat = GAMESTATE->m_Position.m_fLightSongBeat;
|
||||
switch( iLight )
|
||||
{
|
||||
case 0: m_LightsState.m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT] = true; break;
|
||||
@@ -405,7 +405,7 @@ void LightsManager::Update( float fDeltaTime )
|
||||
// If not joined, has enough credits, and not too late to join, then
|
||||
// blink the menu buttons rapidly so they'll press Start
|
||||
{
|
||||
int iBeat = (int)(GAMESTATE->m_fLightSongBeat*4);
|
||||
int iBeat = (int)(GAMESTATE->m_Position.m_fLightSongBeat*4);
|
||||
bool bBlinkOn = (iBeat%2)==0;
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
|
||||
@@ -41,9 +41,9 @@ void LyricDisplay::Update( float fDeltaTime )
|
||||
return;
|
||||
|
||||
// If the song has changed (in a course), reset.
|
||||
if( GAMESTATE->m_fMusicSeconds < m_fLastSecond )
|
||||
if( GAMESTATE->m_Position.m_fMusicSeconds < m_fLastSecond )
|
||||
Init();
|
||||
m_fLastSecond = GAMESTATE->m_fMusicSeconds;
|
||||
m_fLastSecond = GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
|
||||
if( m_iCurLyricNumber >= GAMESTATE->m_pCurSong->m_LyricSegments.size() )
|
||||
return;
|
||||
@@ -51,7 +51,7 @@ void LyricDisplay::Update( float fDeltaTime )
|
||||
const Song *pSong = GAMESTATE->m_pCurSong;
|
||||
const float fStartTime = (pSong->m_LyricSegments[m_iCurLyricNumber].m_fStartTime) - IN_LENGTH.GetValue();
|
||||
|
||||
if( GAMESTATE->m_fMusicSeconds < fStartTime )
|
||||
if( GAMESTATE->m_Position.m_fMusicSeconds < fStartTime )
|
||||
return;
|
||||
|
||||
// Clamp this lyric to the beginning of the next or the end of the music.
|
||||
@@ -59,7 +59,7 @@ 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->GetElapsedTimeFromBeat( pSong->m_fLastBeat );
|
||||
fEndTime = pSong->m_SongTiming.GetElapsedTimeFromBeat( pSong->m_fLastBeat );
|
||||
|
||||
const float fDistance = fEndTime - pSong->m_LyricSegments[m_iCurLyricNumber].m_fStartTime;
|
||||
const float fTweenBufferTime = IN_LENGTH.GetValue() + OUT_LENGTH.GetValue();
|
||||
|
||||
+2
-1
@@ -373,7 +373,8 @@ ScoreDisplayBattle.cpp ScoreDisplayBattle.h \
|
||||
ScoreDisplayCalories.cpp ScoreDisplayCalories.h \
|
||||
ScoreDisplayLifeTime.cpp ScoreDisplayLifeTime.h \
|
||||
ScoreDisplayNormal.cpp ScoreDisplayNormal.h ScoreDisplayOni.cpp ScoreDisplayOni.h \
|
||||
ScoreDisplayPercentage.cpp ScoreDisplayPercentage.h ScoreDisplayRave.cpp ScoreDisplayRave.h
|
||||
ScoreDisplayPercentage.cpp ScoreDisplayPercentage.h ScoreDisplayRave.cpp ScoreDisplayRave.h \
|
||||
SongPosition.cpp SongPosition.h
|
||||
|
||||
PCRE = ../extern/pcre/get.c ../extern/pcre/internal.h ../extern/pcre/maketables.c ../extern/pcre/pcre.c ../extern/pcre/pcre.h ../extern/pcre/study.c
|
||||
EXTRA_DIST += ../extern/pcre/chartables.c
|
||||
|
||||
@@ -73,9 +73,9 @@ void SongMeterDisplay::Update( float fDeltaTime )
|
||||
{
|
||||
if( GAMESTATE->m_pCurSong )
|
||||
{
|
||||
float fSongStartSeconds = GAMESTATE->m_pCurSong->m_Timing.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fFirstBeat );
|
||||
float fSongEndSeconds = GAMESTATE->m_pCurSong->m_Timing.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat );
|
||||
float fPercentPositionSong = SCALE( GAMESTATE->m_fMusicSeconds, fSongStartSeconds, fSongEndSeconds, 0.0f, 1.0f );
|
||||
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 fPercentPositionSong = SCALE( GAMESTATE->m_Position.m_fMusicSeconds, fSongStartSeconds, fSongEndSeconds, 0.0f, 1.0f );
|
||||
CLAMP( fPercentPositionSong, 0, 1 );
|
||||
|
||||
SetPercent( fPercentPositionSong );
|
||||
|
||||
@@ -2215,7 +2215,7 @@ void NoteDataUtil::AddTapAttacks( NoteData &nd, Song* pSong )
|
||||
|
||||
for( float sec=15; sec<pSong->m_fMusicLengthSeconds; sec+=30 )
|
||||
{
|
||||
float fBeat = pSong->GetBeatFromElapsedTime( sec );
|
||||
float fBeat = pSong->m_SongTiming.GetBeatFromElapsedTime( sec );
|
||||
int iBeat = (int)fBeat;
|
||||
int iTrack = iBeat % nd.GetNumTracks(); // deterministically calculates track
|
||||
TapNote tn(
|
||||
@@ -2448,7 +2448,7 @@ void NoteDataUtil::SetHopoPossibleFlags( const Song *pSong, NoteData& ndInOut )
|
||||
FOREACH_NONEMPTY_ROW_ALL_TRACKS( ndInOut, r )
|
||||
{
|
||||
float fBeat = NoteRowToBeat( r );
|
||||
float fSeconds = pSong->GetElapsedTimeFromBeat( fBeat );
|
||||
float fSeconds = pSong->m_SongTiming.GetElapsedTimeFromBeat( fBeat );
|
||||
|
||||
int iLastTapTrack = ndInOut.GetLastTrackWithTapOrHoldHead( r );
|
||||
if( iLastTapTrack != -1 && fSeconds <= fLastRowMusicSeconds + g_fTimingWindowHopo )
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ void NoteDisplay::Update( float fDeltaTime )
|
||||
void NoteDisplay::SetActiveFrame( float fNoteBeat, Actor &actorToSet, float fAnimationLength, bool bVivid )
|
||||
{
|
||||
/* -inf ... inf */
|
||||
float fBeatOrSecond = cache->m_bAnimationBasedOnBeats ? GAMESTATE->m_fSongBeat : GAMESTATE->m_fMusicSeconds;
|
||||
float fBeatOrSecond = cache->m_bAnimationBasedOnBeats ? m_pPlayerState->m_Position.m_fSongBeat : m_pPlayerState->m_Position.m_fMusicSeconds;
|
||||
/* -len ... +len */
|
||||
float fPercentIntoAnimation = fmodf( fBeatOrSecond, fAnimationLength );
|
||||
/* -1 ... 1 */
|
||||
|
||||
+216
-92
@@ -32,6 +32,33 @@ static ThemeMetric<float> FADE_FAIL_TIME( "NoteField", "FadeFailTime" );
|
||||
static RString RoutineNoteSkinName( size_t i ) { return ssprintf("RoutineNoteSkinP%i",int(i+1)); }
|
||||
static ThemeMetric1D<RString> ROUTINE_NOTESKIN( "NoteField", RoutineNoteSkinName, NUM_PLAYERS );
|
||||
|
||||
|
||||
inline const TimingData *GetRealTiming(const PlayerState *pPlayerState)
|
||||
{
|
||||
if( GAMESTATE->m_pCurSteps[pPlayerState->m_PlayerNumber] != NULL )
|
||||
return &GAMESTATE->m_pCurSteps[pPlayerState->m_PlayerNumber]->m_Timing;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline const TimingData *GetDisplayedTiming(const PlayerState *pPlayerState)
|
||||
{
|
||||
if( !GAMESTATE->m_bIsUsingStepTiming )
|
||||
return &GAMESTATE->m_pCurSong->m_SongTiming;
|
||||
return GetRealTiming(pPlayerState);
|
||||
}
|
||||
|
||||
inline const SongPosition *GetRealPosition(const PlayerState *pPlayerState)
|
||||
{
|
||||
return &pPlayerState->m_Position;
|
||||
}
|
||||
|
||||
inline const SongPosition *GetDisplayedPosition(const PlayerState *pPlayerState)
|
||||
{
|
||||
if( !GAMESTATE->m_bIsUsingStepTiming )
|
||||
return &GAMESTATE->m_Position;
|
||||
return GetRealPosition(pPlayerState);
|
||||
}
|
||||
|
||||
NoteField::NoteField()
|
||||
{
|
||||
m_pNoteData = NULL;
|
||||
@@ -240,7 +267,7 @@ void NoteField::Update( float fDeltaTime )
|
||||
ActorFrame::Update( fDeltaTime );
|
||||
|
||||
// update m_fBoardOffsetPixels, m_fCurrentBeatLastUpdate, m_fYPosCurrentBeatLastUpdate
|
||||
const float fCurrentBeat = GAMESTATE->m_fSongBeat;
|
||||
const float fCurrentBeat = GetDisplayedPosition(m_pPlayerState)->m_fSongBeat;
|
||||
bool bTweeningOn = m_sprBoard->GetCurrentDiffuseAlpha() >= 0.98 && m_sprBoard->GetCurrentDiffuseAlpha() < 1.00; // HACK
|
||||
if( !bTweeningOn && m_fCurrentBeatLastUpdate != -1 )
|
||||
{
|
||||
@@ -427,6 +454,8 @@ static ThemeMetric<RageColor> TIME_SIGNATURE_COLOR ( "NoteField", "TimeSignature
|
||||
static ThemeMetric<RageColor> TICKCOUNT_COLOR ( "NoteField", "TickcountColor" );
|
||||
static ThemeMetric<RageColor> COMBO_COLOR ( "NoteField", "ComboColor" );
|
||||
static ThemeMetric<RageColor> LABEL_COLOR ( "NoteField", "LabelColor" );
|
||||
static ThemeMetric<RageColor> SPEED_COLOR ( "NoteField", "SpeedColor" );
|
||||
static ThemeMetric<RageColor> FAKE_COLOR ("NoteField", "FakeColor" );
|
||||
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" );
|
||||
@@ -435,6 +464,8 @@ static ThemeMetric<bool> TIME_SIGNATURE_IS_LEFT_SIDE ( "NoteField", "TimeSignatu
|
||||
static ThemeMetric<bool> TICKCOUNT_IS_LEFT_SIDE ( "NoteField", "TickcountIsLeftSide" );
|
||||
static ThemeMetric<bool> COMBO_IS_LEFT_SIDE ( "NoteField", "ComboIsLeftSide" );
|
||||
static ThemeMetric<bool> LABEL_IS_LEFT_SIDE ( "NoteField", "LabelIsLeftSide" );
|
||||
static ThemeMetric<bool> SPEED_IS_LEFT_SIDE ( "NoteField", "SpeedIsLeftSide" );
|
||||
static ThemeMetric<bool> FAKE_IS_LEFT_SIDE ( "NoteField", "FakeIsLeftSide" );
|
||||
static ThemeMetric<float> BPM_OFFSETX ( "NoteField", "BPMOffsetX" );
|
||||
static ThemeMetric<float> STOP_OFFSETX ( "NoteField", "StopOffsetX" );
|
||||
static ThemeMetric<float> DELAY_OFFSETX ( "NoteField", "DelayOffsetX" );
|
||||
@@ -443,6 +474,8 @@ static ThemeMetric<float> TIME_SIGNATURE_OFFSETX ( "NoteField", "TimeSignatureOf
|
||||
static ThemeMetric<float> TICKCOUNT_OFFSETX ( "NoteField", "TickcountOffsetX" );
|
||||
static ThemeMetric<float> COMBO_OFFSETX ( "NoteField", "ComboOffsetX" );
|
||||
static ThemeMetric<float> LABEL_OFFSETX ( "NoteField", "LabelOffsetX" );
|
||||
static ThemeMetric<float> SPEED_OFFSETX ( "NoteField", "SpeedOffsetX" );
|
||||
static ThemeMetric<float> FAKE_OFFSETX ( "NoteField", "FakeOffsetX" );
|
||||
|
||||
void NoteField::DrawBPMText( const float fBeat, const float fBPM )
|
||||
{
|
||||
@@ -572,6 +605,40 @@ void NoteField::DrawLabelText( const float fBeat, RString sLabel )
|
||||
m_textMeasureNumber.Draw();
|
||||
}
|
||||
|
||||
void NoteField::DrawSpeedText( const float fBeat, float fPercent, float fWait, unsigned short usMode )
|
||||
{
|
||||
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 = SPEED_OFFSETX * fZoom;
|
||||
|
||||
m_textMeasureNumber.SetZoom( fZoom );
|
||||
m_textMeasureNumber.SetHorizAlign( SPEED_IS_LEFT_SIDE ? align_right : align_left );
|
||||
m_textMeasureNumber.SetDiffuse( SPEED_COLOR );
|
||||
m_textMeasureNumber.SetGlow( RageColor(1,1,1,RageFastCos(RageTimer::GetTimeSinceStartFast()*2)/2+0.5f) );
|
||||
m_textMeasureNumber.SetText( ssprintf("%.3f\n%s\n%.3f", fPercent, (usMode == 1 ? "S" : "B"), fWait) );
|
||||
m_textMeasureNumber.SetXY( (SPEED_IS_LEFT_SIDE ? -xBase - xOffset : xBase + xOffset), fYPos );
|
||||
m_textMeasureNumber.Draw();
|
||||
}
|
||||
|
||||
void NoteField::DrawFakeText( 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 = FAKE_OFFSETX * fZoom;
|
||||
|
||||
m_textMeasureNumber.SetZoom( fZoom );
|
||||
m_textMeasureNumber.SetHorizAlign( FAKE_IS_LEFT_SIDE ? align_right : align_left );
|
||||
m_textMeasureNumber.SetDiffuse( FAKE_COLOR );
|
||||
m_textMeasureNumber.SetGlow( RageColor(1,1,1,RageFastCos(RageTimer::GetTimeSinceStartFast()*2)/2+0.5f) );
|
||||
m_textMeasureNumber.SetText( ssprintf("%.3f", fNewBeat) );
|
||||
m_textMeasureNumber.SetXY( (FAKE_IS_LEFT_SIDE ? -xBase - xOffset : xBase + xOffset), fYPos );
|
||||
m_textMeasureNumber.Draw();
|
||||
}
|
||||
|
||||
void NoteField::DrawAttackText( const float fBeat, const Attack &attack )
|
||||
{
|
||||
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
|
||||
@@ -606,7 +673,7 @@ void NoteField::DrawBGChangeText( const float fBeat, const RString sNewBGName )
|
||||
// change this probing to binary search
|
||||
float FindFirstDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceAfterTargetsPixels )
|
||||
{
|
||||
float fFirstBeatToDraw = GAMESTATE->m_fSongBeat-4; // Adjust to balance off performance and showing enough notes.
|
||||
float fFirstBeatToDraw = GetDisplayedPosition(pPlayerState)->m_fSongBeat-4; // Adjust to balance off performance and showing enough notes.
|
||||
|
||||
/* In Boomerang, we'll usually have two sections of notes: before and after
|
||||
* the peak. We always start drawing before the peak, and end after it, or
|
||||
@@ -618,7 +685,7 @@ float FindFirstDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistance
|
||||
bBoomerang = (fAccels[PlayerOptions::ACCEL_BOOMERANG] != 0);
|
||||
}
|
||||
|
||||
while( fFirstBeatToDraw < GAMESTATE->m_fSongBeat )
|
||||
while( fFirstBeatToDraw < GetDisplayedPosition(pPlayerState)->m_fSongBeat )
|
||||
{
|
||||
bool bIsPastPeakYOffset;
|
||||
float fPeakYOffset;
|
||||
@@ -640,7 +707,7 @@ float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceB
|
||||
// Probe for last note to draw. Worst case is 0.25x + boost.
|
||||
// Adjust search distance so that notes don't pop onto the screen.
|
||||
float fSearchDistance = 10;
|
||||
float fLastBeatToDraw = GAMESTATE->m_fSongBeat+fSearchDistance;
|
||||
float fLastBeatToDraw = GetDisplayedPosition(pPlayerState)->m_fSongBeat+fSearchDistance;
|
||||
|
||||
const int NUM_ITERATIONS = 20;
|
||||
|
||||
@@ -669,6 +736,19 @@ float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceB
|
||||
return fLastBeatToDraw;
|
||||
}
|
||||
|
||||
inline float NoteRowToVisibleBeat( const PlayerState *pPlayerState, int iRow )
|
||||
{
|
||||
/*
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
*/
|
||||
return NoteRowToBeat(iRow);
|
||||
/*
|
||||
}
|
||||
return GetDisplayedTiming(pPlayerState)->GetBeatFromElapsedTime(GetRealTiming(pPlayerState)->GetElapsedTimeFromBeat(NoteRowToBeat(iRow)));
|
||||
*/
|
||||
}
|
||||
|
||||
bool NoteField::IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const
|
||||
{
|
||||
// TRICKY: If boomerang is on, then ones in the range
|
||||
@@ -739,10 +819,13 @@ void NoteField::DrawPrimitives()
|
||||
cur->m_ReceptorArrowRow.Draw();
|
||||
}
|
||||
|
||||
const TimingData *pTiming = GetDisplayedTiming(m_pPlayerState);
|
||||
|
||||
// Draw beat bars
|
||||
if( GAMESTATE->IsEditing() || SHOW_BEAT_BARS )
|
||||
if( ( GAMESTATE->IsEditing() || SHOW_BEAT_BARS ) && pTiming != NULL )
|
||||
{
|
||||
const vector<TimeSignatureSegment> &vTimeSignatureSegments = GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments;
|
||||
const TimingData &timing = *pTiming;
|
||||
const vector<TimeSignatureSegment> &vTimeSignatureSegments = timing.m_vTimeSignatureSegments;
|
||||
int iMeasureIndex = 0;
|
||||
FOREACH_CONST( TimeSignatureSegment, vTimeSignatureSegments, iter )
|
||||
{
|
||||
@@ -781,12 +864,14 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
}
|
||||
|
||||
if( GAMESTATE->IsEditing() )
|
||||
if( GAMESTATE->IsEditing() && pTiming != NULL )
|
||||
{
|
||||
ASSERT(GAMESTATE->m_pCurSong);
|
||||
|
||||
const TimingData &timing = *pTiming;
|
||||
|
||||
// BPM text
|
||||
FOREACH_CONST( BPMSegment, GAMESTATE->m_pCurSong->m_Timing.m_BPMSegments, seg )
|
||||
FOREACH_CONST( BPMSegment, timing.m_BPMSegments, seg )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
@@ -797,7 +882,7 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
// Freeze text
|
||||
FOREACH_CONST( StopSegment, GAMESTATE->m_pCurSong->m_Timing.m_StopSegments, seg )
|
||||
FOREACH_CONST( StopSegment, timing.m_StopSegments, seg )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
@@ -808,18 +893,21 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
// Warp text
|
||||
FOREACH_CONST( WarpSegment, GAMESTATE->m_pCurSong->m_Timing.m_WarpSegments, seg )
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
FOREACH_CONST( WarpSegment, timing.m_WarpSegments, seg )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawWarpText( fBeat, seg->m_fEndBeat );
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawWarpText( fBeat, seg->m_fLengthBeats );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Time Signature text
|
||||
FOREACH_CONST( TimeSignatureSegment, GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments, seg )
|
||||
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, seg )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
@@ -829,30 +917,36 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
}
|
||||
|
||||
// Tickcount text
|
||||
FOREACH_CONST( TickcountSegment, GAMESTATE->m_pCurSong->m_Timing.m_TickcountSegments, seg )
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
// Tickcount text
|
||||
FOREACH_CONST( TickcountSegment, timing.m_TickcountSegments, seg )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawTickcountText( fBeat, seg->m_iTicks );
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawTickcountText( fBeat, seg->m_iTicks );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combo text
|
||||
FOREACH_CONST( ComboSegment, GAMESTATE->m_pCurSong->m_Timing.m_ComboSegments, seg )
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
// Combo text
|
||||
FOREACH_CONST( ComboSegment, timing.m_ComboSegments, seg )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawComboText( fBeat, seg->m_iCombo );
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawComboText( fBeat, seg->m_iCombo );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Label text
|
||||
FOREACH_CONST( LabelSegment, GAMESTATE->m_pCurSong->m_Timing.m_LabelSegments, seg )
|
||||
FOREACH_CONST( LabelSegment, timing.m_LabelSegments, seg )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
@@ -861,6 +955,33 @@ void NoteField::DrawPrimitives()
|
||||
DrawLabelText( fBeat, seg->m_sLabel );
|
||||
}
|
||||
}
|
||||
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
FOREACH_CONST( SpeedSegment, timing.m_SpeedSegments, seg )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawSpeedText( fBeat, seg->m_fPercent, seg->m_fWait, seg->m_usMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Speed text
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
FOREACH_CONST( FakeSegment, timing.m_FakeSegments, seg )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawFakeText( fBeat, seg->m_fLengthBeats );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Course mods text
|
||||
const Course *pCourse = GAMESTATE->m_pCurCourse;
|
||||
@@ -872,7 +993,7 @@ void NoteField::DrawPrimitives()
|
||||
FOREACH_CONST( Attack, ce.attacks, a )
|
||||
{
|
||||
float fSecond = a->fStartSecond;
|
||||
float fBeat = GAMESTATE->m_pCurSong->m_Timing.GetBeatFromElapsedTime( fSecond );
|
||||
float fBeat = timing.GetBeatFromElapsedTime( fSecond );
|
||||
|
||||
if( BeatToNoteRow(fBeat) >= iFirstRowToDraw &&
|
||||
BeatToNoteRow(fBeat) <= iLastRowToDraw)
|
||||
@@ -882,71 +1003,74 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BGChange text
|
||||
switch( GAMESTATE->m_EditMode )
|
||||
|
||||
if( !GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
case EditMode_Home:
|
||||
case EditMode_CourseMods:
|
||||
case EditMode_Practice:
|
||||
break;
|
||||
case EditMode_Full:
|
||||
{
|
||||
vector<BackgroundChange>::iterator iter[NUM_BackgroundLayer];
|
||||
FOREACH_BackgroundLayer( i )
|
||||
iter[i] = GAMESTATE->m_pCurSong->GetBackgroundChanges(i).begin();
|
||||
|
||||
while( 1 )
|
||||
// BGChange text
|
||||
switch( GAMESTATE->m_EditMode )
|
||||
{
|
||||
case EditMode_Home:
|
||||
case EditMode_CourseMods:
|
||||
case EditMode_Practice:
|
||||
break;
|
||||
case EditMode_Full:
|
||||
{
|
||||
float fLowestBeat = FLT_MAX;
|
||||
vector<BackgroundLayer> viLowestIndex;
|
||||
|
||||
vector<BackgroundChange>::iterator iter[NUM_BackgroundLayer];
|
||||
FOREACH_BackgroundLayer( i )
|
||||
iter[i] = GAMESTATE->m_pCurSong->GetBackgroundChanges(i).begin();
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if( iter[i] == GAMESTATE->m_pCurSong->GetBackgroundChanges(i).end() )
|
||||
continue;
|
||||
|
||||
float fBeat = iter[i]->m_fStartBeat;
|
||||
if( fBeat < fLowestBeat )
|
||||
{
|
||||
fLowestBeat = fBeat;
|
||||
viLowestIndex.clear();
|
||||
viLowestIndex.push_back( i );
|
||||
}
|
||||
else if( fBeat == fLowestBeat )
|
||||
{
|
||||
viLowestIndex.push_back( i );
|
||||
}
|
||||
}
|
||||
|
||||
if( viLowestIndex.empty() )
|
||||
{
|
||||
float fLowestBeat = FLT_MAX;
|
||||
vector<BackgroundLayer> viLowestIndex;
|
||||
|
||||
FOREACH_BackgroundLayer( i )
|
||||
ASSERT( iter[i] == GAMESTATE->m_pCurSong->GetBackgroundChanges(i).end() );
|
||||
break;
|
||||
}
|
||||
|
||||
if( IS_ON_SCREEN(fLowestBeat) )
|
||||
{
|
||||
vector<RString> vsBGChanges;
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, i )
|
||||
{
|
||||
ASSERT( iter[*i] != GAMESTATE->m_pCurSong->GetBackgroundChanges(*i).end() );
|
||||
const BackgroundChange& change = *iter[*i];
|
||||
RString s = change.GetTextDescription();
|
||||
if( *i!=0 )
|
||||
s = ssprintf("%d: ",*i) + s;
|
||||
vsBGChanges.push_back( s );
|
||||
if( iter[i] == GAMESTATE->m_pCurSong->GetBackgroundChanges(i).end() )
|
||||
continue;
|
||||
|
||||
float fBeat = iter[i]->m_fStartBeat;
|
||||
if( fBeat < fLowestBeat )
|
||||
{
|
||||
fLowestBeat = fBeat;
|
||||
viLowestIndex.clear();
|
||||
viLowestIndex.push_back( i );
|
||||
}
|
||||
else if( fBeat == fLowestBeat )
|
||||
{
|
||||
viLowestIndex.push_back( i );
|
||||
}
|
||||
}
|
||||
DrawBGChangeText( fLowestBeat, join("\n",vsBGChanges) );
|
||||
|
||||
if( viLowestIndex.empty() )
|
||||
{
|
||||
FOREACH_BackgroundLayer( i )
|
||||
ASSERT( iter[i] == GAMESTATE->m_pCurSong->GetBackgroundChanges(i).end() );
|
||||
break;
|
||||
}
|
||||
|
||||
if( IS_ON_SCREEN(fLowestBeat) )
|
||||
{
|
||||
vector<RString> vsBGChanges;
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, i )
|
||||
{
|
||||
ASSERT( iter[*i] != GAMESTATE->m_pCurSong->GetBackgroundChanges(*i).end() );
|
||||
const BackgroundChange& change = *iter[*i];
|
||||
RString s = change.GetTextDescription();
|
||||
if( *i!=0 )
|
||||
s = ssprintf("%d: ",*i) + s;
|
||||
vsBGChanges.push_back( s );
|
||||
}
|
||||
DrawBGChangeText( fLowestBeat, join("\n",vsBGChanges) );
|
||||
}
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, i )
|
||||
iter[*i]++;
|
||||
}
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, i )
|
||||
iter[*i]++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw marker bars
|
||||
@@ -1012,8 +1136,8 @@ void NoteField::DrawPrimitives()
|
||||
float fThrowAway;
|
||||
bool bStartIsPastPeak = false;
|
||||
bool bEndIsPastPeak = false;
|
||||
float fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iStartRow), fThrowAway, bStartIsPastPeak );
|
||||
float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iEndRow), fThrowAway, bEndIsPastPeak );
|
||||
float fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToVisibleBeat(m_pPlayerState, iStartRow), fThrowAway, bStartIsPastPeak );
|
||||
float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToVisibleBeat(m_pPlayerState, iEndRow), fThrowAway, bEndIsPastPeak );
|
||||
|
||||
bool bTailIsOnVisible = iDrawDistanceAfterTargetsPixels <= fEndYOffset && fEndYOffset <= iDrawDistanceBeforeTargetsPixels;
|
||||
bool bHeadIsVisible = iDrawDistanceAfterTargetsPixels <= fStartYOffset && fStartYOffset <= iDrawDistanceBeforeTargetsPixels;
|
||||
@@ -1043,7 +1167,7 @@ void NoteField::DrawPrimitives()
|
||||
displayCols->display[c].DrawHold( tn, c, iStartRow, bIsHoldingNote, Result, bUseAdditionColoring, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail,
|
||||
m_fYReverseOffsetPixels, (float) iDrawDistanceAfterTargetsPixels, (float) iDrawDistanceBeforeTargetsPixels, iDrawDistanceBeforeTargetsPixels, FADE_BEFORE_TARGETS_PERCENT );
|
||||
|
||||
bool bNoteIsUpcoming = NoteRowToBeat(iStartRow) > GAMESTATE->m_fSongBeat;
|
||||
bool bNoteIsUpcoming = NoteRowToBeat(iStartRow) > GetDisplayedPosition(m_pPlayerState)->m_fSongBeat;
|
||||
bAnyUpcomingInThisCol |= bNoteIsUpcoming;
|
||||
}
|
||||
}
|
||||
@@ -1085,7 +1209,7 @@ void NoteField::DrawPrimitives()
|
||||
continue; // skip
|
||||
|
||||
ASSERT_M( NoteRowToBeat(q) > -2000, ssprintf("%i %i %i, %f %f", q, iLastRowToDraw,
|
||||
iFirstRowToDraw, GAMESTATE->m_fSongBeat, GAMESTATE->m_fMusicSeconds) );
|
||||
iFirstRowToDraw, GetDisplayedPosition(m_pPlayerState)->m_fSongBeat, GetDisplayedPosition(m_pPlayerState)->m_fMusicSeconds) );
|
||||
|
||||
// See if there is a hold step that begins on this index.
|
||||
// Only do this if the noteskin cares.
|
||||
@@ -1110,12 +1234,12 @@ void NoteField::DrawPrimitives()
|
||||
bool bIsHopoPossible = (tn.bHopoPossible);
|
||||
bool bUseAdditionColoring = bIsAddition || bIsHopoPossible;
|
||||
NoteDisplayCols *displayCols = tn.pn == PLAYER_INVALID ? m_pCurDisplay : m_pDisplays[tn.pn];
|
||||
displayCols->display[c].DrawTap( tn, c, NoteRowToBeat(q), bHoldNoteBeginsOnThisBeat,
|
||||
displayCols->display[c].DrawTap( tn, c, NoteRowToVisibleBeat(m_pPlayerState, q), bHoldNoteBeginsOnThisBeat,
|
||||
bUseAdditionColoring, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail,
|
||||
m_fYReverseOffsetPixels, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels,
|
||||
FADE_BEFORE_TARGETS_PERCENT );
|
||||
|
||||
bool bNoteIsUpcoming = NoteRowToBeat(q) > GAMESTATE->m_fSongBeat;
|
||||
bool bNoteIsUpcoming = NoteRowToBeat(q) > GetDisplayedPosition(m_pPlayerState)->m_fSongBeat;
|
||||
bAnyUpcomingInThisCol |= bNoteIsUpcoming;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -1,6 +1,8 @@
|
||||
#ifndef NOTE_FIELD_H
|
||||
#define NOTE_FIELD_H
|
||||
|
||||
#include "TimingData.h"
|
||||
#include "SongPosition.h"
|
||||
#include "Sprite.h"
|
||||
#include "ActorFrame.h"
|
||||
#include "BitmapText.h"
|
||||
@@ -62,10 +64,12 @@ protected:
|
||||
void DrawTickcountText( const float fBeat, int iTicks );
|
||||
void DrawComboText( const float fBeat, int iCombo );
|
||||
void DrawLabelText( const float fBeat, RString sLabel );
|
||||
void DrawSpeedText( const float fBeat, float fPercent, float fWait, unsigned short usMode );
|
||||
void DrawFakeText( const float fBeat, const float fNewBeat );
|
||||
void DrawAttackText( const float fBeat, const Attack &attack );
|
||||
void DrawBGChangeText( const float fBeat, const RString sNewBGName );
|
||||
float GetWidth() const;
|
||||
|
||||
|
||||
const NoteData *m_pNoteData;
|
||||
|
||||
float m_fPercentFadeToFail; // -1 if not fading to fail
|
||||
|
||||
@@ -43,11 +43,9 @@ bool NotesLoader::LoadFromDir( const RString &sPath, Song &out, set<RString> &Bl
|
||||
SMLoader::GetApplicableFiles( sPath, list );
|
||||
if (!list.empty() )
|
||||
return SMLoader::LoadFromDir( sPath, out );
|
||||
#if defined(_MSC_VER) || defined(MACOSX)
|
||||
SMALoader::GetApplicableFiles( sPath, list );
|
||||
if (!list.empty() )
|
||||
return SMALoader::LoadFromDir( sPath, out );
|
||||
#endif
|
||||
DWILoader::GetApplicableFiles( sPath, list );
|
||||
if( !list.empty() )
|
||||
return DWILoader::LoadFromDir( sPath, out, BlacklistedImages );
|
||||
|
||||
+232
-178
@@ -435,9 +435,121 @@ static void ReadTimeSigs( const NameToData_t &mapNameToData, MeasureToTimeSig_t
|
||||
static const int BEATS_PER_MEASURE = 4;
|
||||
static const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
|
||||
|
||||
static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameToData, Steps &out,
|
||||
const MeasureToTimeSig_t &sigAdjustments, const map<RString,int> &idToKeySoundIndex )
|
||||
static bool SearchForKeysound( const RString &sPath, RString nDataOriginal, map<RString, int> &mapFilenameToKeysoundIndex, Song &out, int &outKeysoundIndex )
|
||||
{
|
||||
|
||||
// Search for memoized file names:
|
||||
{
|
||||
RString nDataToSearchFor = nDataOriginal;
|
||||
nDataToSearchFor.MakeLower();
|
||||
map<RString, int>::iterator it = mapFilenameToKeysoundIndex.find(nDataToSearchFor);
|
||||
if (it != mapFilenameToKeysoundIndex.end()) {
|
||||
outKeysoundIndex = it->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: garbled song names seem to crash the app.
|
||||
// this might not be the best place to put this code.
|
||||
if( !utf8_is_valid(nDataOriginal) )
|
||||
return false;
|
||||
|
||||
/* Due to bugs in some programs, many BMS files have a "WAV" extension
|
||||
* on files in the BMS for files that actually have some other extension.
|
||||
* Do a search. Don't do a wildcard search; if sData is "song.wav",
|
||||
* we might also have "song.png", which we shouldn't match. */
|
||||
RString nData = nDataOriginal;
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
{
|
||||
const char *exts[] = { "oga", "ogg", "wav", "mp3", NULL }; // XXX: stop duplicating these everywhere
|
||||
for( unsigned i = 0; exts[i] != NULL; ++i )
|
||||
{
|
||||
RString fn = SetExtension( nData, exts[i] );
|
||||
if( IsAFile(out.GetSongDir()+fn) )
|
||||
{
|
||||
nData = fn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
{
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "references key \"%s\" that can't be found", nData.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Let's again search for memoized file names (we got the normalized one!):
|
||||
{
|
||||
RString nDataToSearchFor = nData;
|
||||
nDataToSearchFor.MakeLower();
|
||||
map<RString, int>::iterator it = mapFilenameToKeysoundIndex.find(nDataToSearchFor);
|
||||
if (it != mapFilenameToKeysoundIndex.end()) {
|
||||
outKeysoundIndex = it->second;
|
||||
|
||||
{
|
||||
RString nDataToAdd = nDataOriginal;
|
||||
nDataToAdd.MakeLower();
|
||||
mapFilenameToKeysoundIndex[nDataToAdd] = outKeysoundIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Now this is a new sample.
|
||||
out.m_vsKeysoundFile.push_back( nData );
|
||||
outKeysoundIndex = out.m_vsKeysoundFile.size() - 1;
|
||||
|
||||
{
|
||||
RString nDataToAdd = nDataOriginal;
|
||||
nDataToAdd.MakeLower();
|
||||
mapFilenameToKeysoundIndex[nDataToAdd] = outKeysoundIndex;
|
||||
}
|
||||
|
||||
{
|
||||
RString nDataToAdd = nData;
|
||||
nDataToAdd.MakeLower();
|
||||
mapFilenameToKeysoundIndex[nDataToAdd] = outKeysoundIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static bool SearchForKeysound( const RString &sPath, RString sNoteId, const NameToData_t &mapNameToData, map<RString, int> &mapIdToKeysoundIndex, map<RString, int> &mapFilenameToKeysoundIndex, Song &out, int &outKeysoundIndex )
|
||||
{
|
||||
|
||||
sNoteId.MakeLower();
|
||||
{
|
||||
map<RString, int>::iterator it = mapIdToKeysoundIndex.find(sNoteId);
|
||||
if (it != mapIdToKeysoundIndex.end())
|
||||
{
|
||||
outKeysoundIndex = it->second;
|
||||
return outKeysoundIndex >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
RString sTagToLookFor = ssprintf( "#wav%s", sNoteId.c_str() );
|
||||
RString nDataOriginal;
|
||||
if( !GetTagFromMap( mapNameToData, sTagToLookFor, nDataOriginal ) )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath.c_str(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retval = SearchForKeysound(sPath, nDataOriginal, mapFilenameToKeysoundIndex, out, outKeysoundIndex);
|
||||
mapIdToKeysoundIndex[sNoteId] = outKeysoundIndex;
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameToData, Steps &out, Song &outSong, map<RString, int> &mapFilenameToKeysoundIndex )
|
||||
{
|
||||
|
||||
map<RString, int> mapIdToKeysoundIndex;
|
||||
MeasureToTimeSig_t sigAdjustments;
|
||||
|
||||
LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() );
|
||||
|
||||
out.m_StepsType = StepsType_Invalid;
|
||||
@@ -453,11 +565,121 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
NoteData ndNotes;
|
||||
ndNotes.SetNumTracks( NUM_BMS_TRACKS );
|
||||
|
||||
// Read BPM
|
||||
if( GetTagFromMap(mapNameToData, "#bpm", sData) )
|
||||
{
|
||||
const float fBPM = StringToFloat( sData );
|
||||
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( 0, fBPM );
|
||||
out.m_Timing.AddBPMSegment( newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath.c_str(), "has an invalid BPM change at beat %f, BPM %f.",
|
||||
NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Read time signatures. Note that these can differ across files in the same
|
||||
* song. */
|
||||
MeasureToTimeSig_t mapMeasureToTimeSig;
|
||||
ReadTimeSigs( mapNameToData, mapMeasureToTimeSig );
|
||||
|
||||
for( NameToData_t::const_iterator it = mapNameToData.lower_bound("#00000"); it != mapNameToData.end(); ++it )
|
||||
{
|
||||
const RString &sName = it->first;
|
||||
if( sName.size() != 6 || sName[0] != '#' || !IsAnInt( sName.substr(1,5) ) )
|
||||
continue;
|
||||
// this is step or offset data. Looks like "#00705"
|
||||
int iMeasureNo = atoi( sName.substr(1, 3).c_str() );
|
||||
int iBMSTrackNo = atoi( sName.substr(4, 2).c_str() );
|
||||
int iStepIndex = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||
float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||
int iRowsPerMeasure = BeatToNoteRow( fBeatsPerMeasure );
|
||||
|
||||
RString nData = it->second;
|
||||
int totalPairs = nData.size() / 2;
|
||||
for( int i = 0; i < totalPairs; ++i )
|
||||
{
|
||||
RString sPair = nData.substr( i*2, 2 );
|
||||
|
||||
int iRow = iStepIndex + (i * iRowsPerMeasure) / totalPairs;
|
||||
float fBeat = NoteRowToBeat( iRow );
|
||||
int iVal = 0;
|
||||
sscanf( sPair, "%x", &iVal );
|
||||
|
||||
if (sPair == "00")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch( iBMSTrackNo )
|
||||
{
|
||||
case BMS_TRACK_BPM:
|
||||
if( iVal > 0 )
|
||||
{
|
||||
out.m_Timing.SetBPMAtBeat( fBeat, (float) iVal );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %i", fBeat, iVal );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath.c_str(), "has an invalid BPM change at beat %f, BPM %d.",
|
||||
fBeat, iVal );
|
||||
}
|
||||
break;
|
||||
|
||||
case BMS_TRACK_BPM_REF:
|
||||
{
|
||||
RString sTagToLookFor = ssprintf( "#bpm%s", sPair.c_str() );
|
||||
RString sBPM;
|
||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBPM ) )
|
||||
{
|
||||
float fBPM = StringToFloat( sBPM );
|
||||
out.m_Timing.SetBPMAtBeat( fBeat, fBPM );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath.c_str(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMS_TRACK_STOP:
|
||||
{
|
||||
if( iVal == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
RString sTagToLookFor = ssprintf( "#stop%02x", iVal );
|
||||
RString sBeats;
|
||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBeats ) )
|
||||
{
|
||||
// find the BPM at the time of this freeze
|
||||
float fBPS = out.m_Timing.GetBPMAtBeat(fBeat) / 60.0f;
|
||||
float fBeats = StringToFloat( sBeats ) / 48.0f;
|
||||
float fFreezeSecs = fBeats / fBPS;
|
||||
|
||||
StopSegment newSeg( BeatToNoteRow(fBeat), fFreezeSecs );
|
||||
out.m_Timing.AddStopSegment( newSeg );
|
||||
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg.m_fStopSeconds );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath.c_str(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now that we're done reading BPMs, factor out weird time signatures.
|
||||
SetTimeSigAdjustments( mapMeasureToTimeSig, outSong, sigAdjustments );
|
||||
|
||||
int iHoldStarts[NUM_BMS_TRACKS];
|
||||
TapNote iHoldHeads[NUM_BMS_TRACKS];
|
||||
|
||||
@@ -487,10 +709,9 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
RString sNoteId = sNoteData.substr( i, 2 );
|
||||
if( sNoteId != "00" )
|
||||
{
|
||||
vTapNotes.push_back( TAP_ORIGINAL_TAP );
|
||||
map<RString,int>::const_iterator rInt = idToKeySoundIndex.find( sNoteId );
|
||||
if( rInt != idToKeySoundIndex.end() )
|
||||
vTapNotes.back().iKeysoundIndex = rInt->second;
|
||||
TapNote tn = TAP_ORIGINAL_TAP;
|
||||
SearchForKeysound( sPath, sNoteId, mapNameToData, mapIdToKeysoundIndex, mapFilenameToKeysoundIndex, outSong, tn.iKeysoundIndex );
|
||||
vTapNotes.push_back( tn );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -781,7 +1002,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, MeasureToTimeSig_t &sigAdjustmentsOut, map<RString,int> &idToKeySoundIndexOut )
|
||||
static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out )
|
||||
{
|
||||
RString sData;
|
||||
if( GetTagFromMap(mapNameToData, "#title", sData) )
|
||||
@@ -792,172 +1013,6 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
GetTagFromMap( mapNameToData, "#backbmp", out.m_sBackgroundFile );
|
||||
GetTagFromMap( mapNameToData, "#wav", out.m_sMusicFile );
|
||||
|
||||
if( GetTagFromMap(mapNameToData, "#bpm", sData) )
|
||||
{
|
||||
const float fBPM = StringToFloat( sData );
|
||||
|
||||
if( PREFSMAN->m_bQuirksMode )
|
||||
{
|
||||
BPMSegment newSeg( 0, fBPM );
|
||||
out.AddBPMSegment( newSeg );
|
||||
if( fBPM > 0.0f )
|
||||
LOG->Trace( "Inserting new positive BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||
else
|
||||
LOG->Trace( "Inserting new negative BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( 0, fBPM );
|
||||
out.AddBPMSegment( newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "has an invalid BPM change at beat %f, BPM %f.",
|
||||
NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NameToData_t::const_iterator it;
|
||||
for( it = mapNameToData.lower_bound("#wav"); it != mapNameToData.end(); ++it )
|
||||
{
|
||||
const RString &sName = it->first;
|
||||
|
||||
if( sName.size() != 6 || sName.Left(4) != "#wav" )
|
||||
continue;
|
||||
|
||||
// this is keysound file name. Looks like "#WAV1A"
|
||||
RString nData = it->second;
|
||||
RString sWavID = sName.Right(2);
|
||||
|
||||
// FIXME: garbled song names seem to crash the app.
|
||||
// this might not be the best place to put this code.
|
||||
if( !utf8_is_valid(nData) )
|
||||
continue;
|
||||
|
||||
/* Due to bugs in some programs, many BMS files have a "WAV" extension
|
||||
* on files in the BMS for files that actually have some other extension.
|
||||
* Do a search. Don't do a wildcard search; if sData is "song.wav",
|
||||
* we might also have "song.png", which we shouldn't match. */
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
{
|
||||
const char *exts[] = { "oga", "ogg", "wav", "mp3", NULL }; // XXX: stop duplicating these everywhere
|
||||
for( unsigned i = 0; exts[i] != NULL; ++i )
|
||||
{
|
||||
RString fn = SetExtension( nData, exts[i] );
|
||||
if( IsAFile(out.GetSongDir()+fn) )
|
||||
{
|
||||
nData = fn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "references key \"%s\" that can't be found", nData.c_str() );
|
||||
|
||||
sWavID.MakeUpper(); // HACK: undo the MakeLower()
|
||||
out.m_vsKeysoundFile.push_back( nData );
|
||||
idToKeySoundIndexOut[ sWavID ] = out.m_vsKeysoundFile.size()-1;
|
||||
LOG->Trace( "Inserting keysound index %u '%s'", unsigned(out.m_vsKeysoundFile.size()-1), sWavID.c_str() );
|
||||
}
|
||||
|
||||
// Time signature tags affect all other global timing tags, so read them first.
|
||||
MeasureToTimeSig_t mapMeasureToTimeSig;
|
||||
ReadTimeSigs( mapNameToData, mapMeasureToTimeSig );
|
||||
|
||||
for( it = mapNameToData.lower_bound("#00000"); it != mapNameToData.end(); ++it )
|
||||
{
|
||||
const RString &sName = it->first;
|
||||
if( sName.size() != 6 || sName[0] != '#' || !IsAnInt( sName.substr(1,5) ) )
|
||||
continue;
|
||||
// this is step or offset data. Looks like "#00705"
|
||||
int iMeasureNo = atoi( sName.substr(1, 3).c_str() );
|
||||
int iBMSTrackNo = atoi( sName.substr(4, 2).c_str() );
|
||||
int iStepIndex = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut );
|
||||
float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut );
|
||||
int iRowsPerMeasure = BeatToNoteRow( fBeatsPerMeasure );
|
||||
|
||||
RString nData = it->second;
|
||||
int totalPairs = nData.size() / 2;
|
||||
for( int i = 0; i < totalPairs; ++i )
|
||||
{
|
||||
RString sPair = nData.substr( i*2, 2 );
|
||||
|
||||
int iRow = iStepIndex + (i * iRowsPerMeasure) / totalPairs;
|
||||
float fBeat = NoteRowToBeat( iRow );
|
||||
int iVal = 0;
|
||||
sscanf( sPair, "%x", &iVal );
|
||||
|
||||
if (sPair == "00")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch( iBMSTrackNo )
|
||||
{
|
||||
case BMS_TRACK_BPM:
|
||||
if( iVal > 0 )
|
||||
{
|
||||
out.SetBPMAtBeat( fBeat, (float) iVal );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %i", fBeat, iVal );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "has an invalid BPM change at beat %f, BPM %d.",
|
||||
fBeat, iVal );
|
||||
}
|
||||
break;
|
||||
|
||||
case BMS_TRACK_BPM_REF:
|
||||
{
|
||||
RString sTagToLookFor = ssprintf( "#bpm%s", sPair.c_str() );
|
||||
RString sBPM;
|
||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBPM ) )
|
||||
{
|
||||
float fBPM = StringToFloat( sBPM );
|
||||
out.SetBPMAtBeat( fBeat, fBPM );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMS_TRACK_STOP:
|
||||
{
|
||||
if( iVal == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
RString sTagToLookFor = ssprintf( "#stop%02x", iVal );
|
||||
RString sBeats;
|
||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBeats ) )
|
||||
{
|
||||
// find the BPM at the time of this freeze
|
||||
float fBPS = out.m_Timing.GetBPMAtBeat(fBeat) / 60.0f;
|
||||
float fBeats = StringToFloat( sBeats ) / 48.0f;
|
||||
float fFreezeSecs = fBeats / fBPS;
|
||||
|
||||
StopSegment newSeg( BeatToNoteRow(fBeat), fFreezeSecs );
|
||||
out.AddStopSegment( newSeg );
|
||||
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg.m_fStopSeconds );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now that we're done reading BPMs, factor out weird time signatures.
|
||||
SetTimeSigAdjustments( mapMeasureToTimeSig, out, sigAdjustmentsOut );
|
||||
}
|
||||
|
||||
static void SlideDuplicateDifficulties( Song &p )
|
||||
@@ -1031,7 +1086,7 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
// Create a Steps for each.
|
||||
vector<Steps*> apSteps;
|
||||
for( unsigned i=0; i<arrayBMSFileNames.size(); i++ )
|
||||
apSteps.push_back( new Steps );
|
||||
apSteps.push_back( out.CreateSteps() );
|
||||
|
||||
// Now, with our fancy little substring, trim the titles and
|
||||
// figure out where each goes.
|
||||
@@ -1100,9 +1155,7 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
if( apSteps[i]->GetDifficulty() == Difficulty_Medium )
|
||||
iMainDataIndex = i;
|
||||
|
||||
MeasureToTimeSig_t sigAdjustments;
|
||||
map<RString,int> idToKeysoundIndex;
|
||||
ReadGlobalTags( aBMSData[iMainDataIndex], out, sigAdjustments, idToKeysoundIndex );
|
||||
ReadGlobalTags( aBMSData[iMainDataIndex], out );
|
||||
|
||||
// The brackets before the difficulty are in common substring, so remove them if it's found.
|
||||
if( commonSubstring.size() > 2 && commonSubstring[commonSubstring.size() - 2] == ' ' )
|
||||
@@ -1124,10 +1177,11 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
|
||||
// Now that we've parsed the keysound data, load the Steps from the rest
|
||||
// of the .bms files.
|
||||
map<RString, int> mapFilenameToKeysoundIndex;
|
||||
for( unsigned i=0; i<arrayBMSFileNames.size(); i++ )
|
||||
{
|
||||
Steps* pNewNotes = apSteps[i];
|
||||
const bool ok = LoadFromBMSFile( out.GetSongDir() + arrayBMSFileNames[i], aBMSData[i], *pNewNotes, sigAdjustments, idToKeysoundIndex );
|
||||
const bool ok = LoadFromBMSFile( out.GetSongDir() + arrayBMSFileNames[i], aBMSData[i], *pNewNotes, out, mapFilenameToKeysoundIndex );
|
||||
if( ok )
|
||||
out.AddSteps( pNewNotes );
|
||||
else
|
||||
|
||||
@@ -509,11 +509,11 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
|
||||
if( PREFSMAN->m_bQuirksMode )
|
||||
{
|
||||
out.AddBPMSegment( BPMSegment(0, fBPM) );
|
||||
out.m_SongTiming.AddBPMSegment( BPMSegment(0, fBPM) );
|
||||
}
|
||||
else{
|
||||
if( fBPM > 0.0f )
|
||||
out.AddBPMSegment( BPMSegment(0, fBPM) );
|
||||
out.m_SongTiming.AddBPMSegment( BPMSegment(0, fBPM) );
|
||||
else
|
||||
LOG->UserLog( "Song file", sPath, "has an invalid BPM change at beat %f, BPM %f.",
|
||||
NoteRowToBeat(0), fBPM );
|
||||
@@ -545,7 +545,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
|
||||
else if( sValueName.EqualsNoCase("GAP") )
|
||||
// the units of GAP is 1/1000 second
|
||||
out.m_Timing.m_fBeat0OffsetInSeconds = -StringToInt( sParams[1] ) / 1000.0f;
|
||||
out.m_SongTiming.m_fBeat0OffsetInSeconds = -StringToInt( sParams[1] ) / 1000.0f;
|
||||
|
||||
else if( sValueName.EqualsNoCase("SAMPLESTART") )
|
||||
out.m_fMusicSampleStartSeconds = ParseBrokenDWITimestamp(sParams[1], sParams[2], sParams[3]);
|
||||
@@ -570,7 +570,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
int iFreezeRow = BeatToNoteRow( StringToFloat(arrayFreezeValues[0]) / 4.0f );
|
||||
float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] ) / 1000.0f;
|
||||
|
||||
out.AddStopSegment( StopSegment(iFreezeRow, fFreezeSeconds) );
|
||||
out.m_SongTiming.AddStopSegment( StopSegment(iFreezeRow, fFreezeSeconds) );
|
||||
// LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", fFreezeBeat, fFreezeSeconds );
|
||||
}
|
||||
}
|
||||
@@ -595,7 +595,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment bs( iStartIndex, fBPM );
|
||||
out.AddBPMSegment( bs );
|
||||
out.m_SongTiming.AddBPMSegment( bs );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -610,7 +610,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
sValueName.EqualsNoCase("COUPLE") ||
|
||||
sValueName.EqualsNoCase("SOLO") )
|
||||
{
|
||||
Steps* pNewNotes = new Steps;
|
||||
Steps* pNewNotes = out.CreateSteps();
|
||||
LoadFromDWITokens(
|
||||
sParams[0],
|
||||
sParams[1],
|
||||
@@ -656,7 +656,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
// do nothing. We don't care about this value name
|
||||
}
|
||||
}
|
||||
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ static void Deserialize( Song &out, const Json::Value &root )
|
||||
out.m_sLyricsFile = root["LyricsFile"].asString();
|
||||
out.m_sCDTitleFile = root["CDTitle"].asString();
|
||||
out.m_sMusicFile = root["Music"].asString();
|
||||
out.m_Timing.m_fBeat0OffsetInSeconds = (float)root["Offset"].asDouble();
|
||||
out.m_SongTiming.m_fBeat0OffsetInSeconds = (float)root["Offset"].asDouble();
|
||||
out.m_fMusicSampleStartSeconds = (float)root["SampleStart"].asDouble();
|
||||
out.m_fMusicSampleLengthSeconds = (float)root["SampleLength"].asDouble();
|
||||
RString sSelectable = root["Selectable"].asString();
|
||||
@@ -167,7 +167,7 @@ static void Deserialize( Song &out, const Json::Value &root )
|
||||
out.m_fSpecifiedBPMMax = (float)root["SpecifiedBpmMax"].asDouble();
|
||||
}
|
||||
|
||||
Deserialize( out.m_Timing, root["TimingData"] );
|
||||
Deserialize( out.m_SongTiming, root["TimingData"] );
|
||||
JsonUtil::DeserializeVectorObjects( out.m_LyricSegments, Deserialize, root["LyricSegments"] );
|
||||
|
||||
{
|
||||
|
||||
+187
-82
@@ -9,6 +9,51 @@
|
||||
#include "Song.h"
|
||||
#include "Steps.h"
|
||||
|
||||
static void HandleBunki( TimingData &timing, const float fEarlyBPM,
|
||||
const float fCurBPM, const float fGap,
|
||||
const float fPos )
|
||||
{
|
||||
const float BeatsPerSecond = fEarlyBPM / 60.0f;
|
||||
const float beat = (fPos + fGap) * BeatsPerSecond;
|
||||
LOG->Trace( "BPM %f, BPS %f, BPMPos %f, beat %f",
|
||||
fEarlyBPM, BeatsPerSecond, fPos, beat );
|
||||
timing.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), fCurBPM) );
|
||||
}
|
||||
|
||||
static bool HandlePipeChars( TimingData &timing, const RString sNoteRow,
|
||||
const float fCurBeat, int &iTickCount )
|
||||
{
|
||||
RString temp = sNoteRow.substr(2,sNoteRow.size()-3);
|
||||
float numTemp = StringToFloat(temp);
|
||||
if (BeginsWith(sNoteRow, "|T"))
|
||||
{
|
||||
iTickCount = static_cast<int>(numTemp);
|
||||
timing.SetTickcountAtBeat( fCurBeat, clamp(iTickCount, 0, ROWS_PER_BEAT) );
|
||||
return true;
|
||||
}
|
||||
else if (BeginsWith(sNoteRow, "|B"))
|
||||
{
|
||||
timing.SetBPMAtBeat( fCurBeat, numTemp );
|
||||
return true;
|
||||
}
|
||||
else if (BeginsWith(sNoteRow, "|E"))
|
||||
{
|
||||
// Finally! the |E| tag is working as it should. I can die happy now -DaisuMaster
|
||||
float fCurDelay = 60 / timing.GetBPMAtBeat(fCurBeat) * numTemp / iTickCount;
|
||||
fCurDelay += timing.GetDelayAtRow(BeatToNoteRow(fCurBeat) );
|
||||
timing.SetStopAtBeat( fCurBeat, fCurDelay, true );
|
||||
return true;
|
||||
}
|
||||
else if (BeginsWith(sNoteRow, "|D"))
|
||||
{
|
||||
float fCurDelay = timing.GetStopAtRow(BeatToNoteRow(fCurBeat) );
|
||||
fCurDelay += numTemp / 1000;
|
||||
timing.SetStopAtBeat( fCurBeat, fCurDelay, true );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, bool bKIUCompliant )
|
||||
{
|
||||
LOG->Trace( "Steps::LoadFromKSFFile( '%s' )", sPath.c_str() );
|
||||
@@ -25,6 +70,9 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
|
||||
// According to Aldo_MX, there is a default BPM and it's 60. -aj
|
||||
bool bDoublesChart = false;
|
||||
|
||||
TimingData stepsTiming;
|
||||
float SMGap1 = 0, SMGap2 = 0, BPM1 = -1, BPMPos2 = -1, BPM2 = -1, BPMPos3 = -1, BPM3 = -1;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
@@ -32,8 +80,87 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
RString sValueName = sParams[0];
|
||||
sValueName.MakeUpper();
|
||||
|
||||
// handle the data
|
||||
if( sValueName=="TICKCOUNT" )
|
||||
/* handle the data...well, not this data: not related to steps.
|
||||
* Skips INTRO, MUSICINTRO, TITLEFILE, DISCFILE, SONGFILE. */
|
||||
if (sValueName=="TITLE" || EndsWith(sValueName, "INTRO")
|
||||
|| EndsWith(sValueName, "FILE") )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
else if( sValueName=="BPM" )
|
||||
{
|
||||
BPM1 = StringToFloat(sParams[1]);
|
||||
stepsTiming.AddBPMSegment( BPMSegment(0, BPM1) );
|
||||
}
|
||||
else if( sValueName=="BPM2" )
|
||||
{
|
||||
if (bKIUCompliant)
|
||||
{
|
||||
BPM2 = StringToFloat( sParams[1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG an error.
|
||||
}
|
||||
}
|
||||
else if( sValueName=="BPM3" )
|
||||
{
|
||||
if (bKIUCompliant)
|
||||
{
|
||||
BPM3 = StringToFloat( sParams[1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG an error.
|
||||
}
|
||||
}
|
||||
else if( sValueName=="BUNKI" )
|
||||
{
|
||||
if (bKIUCompliant)
|
||||
{
|
||||
BPMPos2 = StringToFloat( sParams[1] ) / 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG an error.
|
||||
}
|
||||
}
|
||||
else if( sValueName=="BUNKI2" )
|
||||
{
|
||||
if (bKIUCompliant)
|
||||
{
|
||||
BPMPos3 = StringToFloat( sParams[1] ) / 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG an error.
|
||||
}
|
||||
}
|
||||
else if( sValueName=="STARTTIME" )
|
||||
{
|
||||
SMGap1 = -StringToFloat( sParams[1] )/100;
|
||||
stepsTiming.m_fBeat0OffsetInSeconds = SMGap1;
|
||||
}
|
||||
// This is currently required for more accurate KIU BPM changes.
|
||||
else if( sValueName=="STARTTIME2" )
|
||||
{
|
||||
if (bKIUCompliant)
|
||||
{
|
||||
SMGap2 = -StringToFloat( sParams[1] )/100;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG an error.
|
||||
}
|
||||
}
|
||||
else if ( sValueName=="STARTTIME3" )
|
||||
{
|
||||
// STARTTIME3 only ensures this is a KIU compliant simfile.
|
||||
bKIUCompliant = true;
|
||||
}
|
||||
|
||||
else if( sValueName=="TICKCOUNT" )
|
||||
{
|
||||
iTickCount = StringToInt( sParams[1] );
|
||||
if( iTickCount <= 0 )
|
||||
@@ -41,13 +168,9 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
LOG->UserLog( "Song file", sPath, "has an invalid tick count: %d.", iTickCount );
|
||||
return false;
|
||||
}
|
||||
stepsTiming.AddTickcountSegment(TickcountSegment(0, iTickCount));
|
||||
}
|
||||
else if( sValueName=="STEP" )
|
||||
{
|
||||
RString theSteps = sParams[1];
|
||||
TrimLeft( theSteps );
|
||||
split( theSteps, "\n", vNoteRows, true );
|
||||
}
|
||||
|
||||
else if( sValueName=="DIFFICULTY" )
|
||||
{
|
||||
out.SetMeter( max(StringToInt(sParams[1]), 0) );
|
||||
@@ -60,13 +183,34 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
if( sPlayer.find( "double" ) != string::npos )
|
||||
bDoublesChart = true;
|
||||
}
|
||||
// This should always be last.
|
||||
else if( sValueName=="STEP" )
|
||||
{
|
||||
RString theSteps = sParams[1];
|
||||
TrimLeft( theSteps );
|
||||
split( theSteps, "\n", vNoteRows, true );
|
||||
}
|
||||
}
|
||||
|
||||
if( iTickCount == -1 )
|
||||
{
|
||||
iTickCount = 2; // Direct Move 0.5 has a default value of 4... -aj
|
||||
iTickCount = 4;
|
||||
LOG->UserLog( "Song file", sPath, "doesn't have a TICKCOUNT. Defaulting to %i.", iTickCount );
|
||||
}
|
||||
|
||||
// Prepare BPM stuff already if the file uses KSF syntax.
|
||||
if( bKIUCompliant )
|
||||
{
|
||||
if( BPM2 > 0 && BPMPos2 > 0 )
|
||||
{
|
||||
HandleBunki( stepsTiming, BPM1, BPM2, SMGap1, BPMPos2 );
|
||||
}
|
||||
|
||||
if( BPM3 > 0 && BPMPos3 > 0 )
|
||||
{
|
||||
HandleBunki( stepsTiming, BPM2, BPM3, SMGap2, BPMPos3 );
|
||||
}
|
||||
}
|
||||
|
||||
NoteData notedata; // read it into here
|
||||
|
||||
@@ -133,6 +277,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
case StepsType_pump_single: notedata.SetNumTracks( 5 ); break;
|
||||
case StepsType_pump_couple: notedata.SetNumTracks( 10 ); break;
|
||||
case StepsType_pump_double: notedata.SetNumTracks( 10 ); break;
|
||||
case StepsType_pump_routine: notedata.SetNumTracks( 10 ); break; // future files may have this?
|
||||
case StepsType_pump_halfdouble: notedata.SetNumTracks( 6 ); break;
|
||||
default: FAIL_M( ssprintf("%i", out.m_StepsType) );
|
||||
}
|
||||
@@ -146,6 +291,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
int newTick = -1;
|
||||
float fCurBeat = 0.0f;
|
||||
float prevBeat = 0.0f; // Used for hold tails.
|
||||
|
||||
for( unsigned r=0; r<vNoteRows.size(); r++ )
|
||||
{
|
||||
RString& sRowString = vNoteRows[r];
|
||||
@@ -155,7 +301,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
continue; // skip
|
||||
|
||||
// All 2s indicates the end of the song.
|
||||
if( sRowString == "2222222222222" )
|
||||
else if( sRowString == "2222222222222" )
|
||||
{
|
||||
// Finish any holds that didn't get...well, finished.
|
||||
for( t=0; t < notedata.GetNumTracks(); t++ )
|
||||
@@ -171,29 +317,21 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
break;
|
||||
}
|
||||
|
||||
// Why do this? Rows made with precise DM05 tags can go up to 13 too -DaisuMaster
|
||||
//if( sRowString.size() != 13 )
|
||||
//this is wrong in many ways...
|
||||
/*if( bKIUCompliant )
|
||||
else if( BeginsWith(sRowString, "|") )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has illegal syntax \"%s\" which can't be in KIU complient files.",
|
||||
sRowString.c_str() );
|
||||
return false;
|
||||
//In other words: you can't mix ksf's with DM05 tags and ksf's without any DM05 tags
|
||||
//Either one set or another will be read...
|
||||
}*/
|
||||
if( BeginsWith(sRowString, "|B") || BeginsWith(sRowString, "|D") || BeginsWith(sRowString, "|E") )
|
||||
{
|
||||
// These don't have to be worried about here: the changes and stops were already added.
|
||||
continue;
|
||||
}
|
||||
else if ( BeginsWith(sRowString, "|T") )
|
||||
{
|
||||
RString temp = sRowString.substr(2,sRowString.size()-3);
|
||||
newTick = StringToInt(temp);
|
||||
bTickChangeNeeded = true;
|
||||
if (bKIUCompliant)
|
||||
{
|
||||
// Log an error, ignore the line.
|
||||
continue;
|
||||
}
|
||||
if ( !HandlePipeChars( stepsTiming, sRowString, fCurBeat, iTickCount ) )
|
||||
{
|
||||
// LOG it first.
|
||||
}
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Is this why improper ksf or some kiucompilant ksf mixed with dm05 ksf are ignored?? -DaisuMaster
|
||||
@@ -270,6 +408,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
}
|
||||
|
||||
out.SetNoteData( notedata );
|
||||
out.m_Timing = stepsTiming;
|
||||
|
||||
out.TidyUpData();
|
||||
|
||||
@@ -358,7 +497,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
else if( sValueName=="BPM" )
|
||||
{
|
||||
BPM1 = StringToFloat(sParams[1]);
|
||||
out.AddBPMSegment( BPMSegment(0, BPM1) );
|
||||
out.m_SongTiming.AddBPMSegment( BPMSegment(0, BPM1) );
|
||||
}
|
||||
else if( sValueName=="BPM2" )
|
||||
{
|
||||
@@ -383,7 +522,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
else if( sValueName=="STARTTIME" )
|
||||
{
|
||||
SMGap1 = -StringToFloat( sParams[1] )/100;
|
||||
out.m_Timing.m_fBeat0OffsetInSeconds = SMGap1;
|
||||
out.m_SongTiming.m_fBeat0OffsetInSeconds = SMGap1;
|
||||
}
|
||||
// This is currently required for more accurate KIU BPM changes.
|
||||
else if( sValueName=="STARTTIME2" )
|
||||
@@ -409,7 +548,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
TickcountSegment tcs;
|
||||
tcs.m_iStartRow = BeatToNoteRow(0.0f);
|
||||
tcs.m_iTicks = iTickCount > ROWS_PER_BEAT ? ROWS_PER_BEAT : iTickCount;
|
||||
out.m_Timing.AddTickcountSegment( tcs );
|
||||
out.m_SongTiming.AddTickcountSegment( tcs );
|
||||
}
|
||||
else if ( sValueName=="STEP" )
|
||||
{
|
||||
@@ -465,21 +604,12 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
{
|
||||
if( BPM2 > 0 && BPMPos2 > 0 )
|
||||
{
|
||||
const float BeatsPerSecond = BPM1 / 60.0f;
|
||||
const float beat = (BPMPos2 + SMGap1) * BeatsPerSecond;
|
||||
LOG->Trace( "BPM %f, BPS %f, BPMPos2 %f, beat %f",
|
||||
BPM1, BeatsPerSecond, BPMPos2, beat );
|
||||
out.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), BPM2) );
|
||||
HandleBunki( out.m_SongTiming, BPM1, BPM2, SMGap1, BPMPos2 );
|
||||
}
|
||||
|
||||
if( BPM3 > 0 && BPMPos3 > 0 )
|
||||
{
|
||||
const float BeatsPerSecond = BPM2 / 60.0f;
|
||||
//The line below isn't perfect, but works better than previous versions.
|
||||
const float beat = (BPMPos3 + SMGap2) * BeatsPerSecond;
|
||||
LOG->Trace( "BPM %f, BPS %f, BPMPos3 %f, beat %f",
|
||||
BPM2, BeatsPerSecond, BPMPos3, beat );
|
||||
out.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), BPM3) );
|
||||
HandleBunki( out.m_SongTiming, BPM2, BPM3, SMGap2, BPMPos3 );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -507,40 +637,11 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
BeginsWith(NoteRowString, "|D") || BeginsWith(NoteRowString, "|E") )
|
||||
{
|
||||
bDMRequired = true;
|
||||
RString temp = NoteRowString.substr(2,NoteRowString.size()-3);
|
||||
float numTemp = StringToFloat(temp);
|
||||
if (BeginsWith(NoteRowString, "|T"))
|
||||
if ( !HandlePipeChars( out.m_SongTiming, NoteRowString, fCurBeat, iTickCount ) )
|
||||
{
|
||||
iTickCount = (int)numTemp;
|
||||
TickcountSegment tcs;
|
||||
tcs.m_iStartRow = BeatToNoteRow(fCurBeat);
|
||||
tcs.m_iTicks = iTickCount > ROWS_PER_BEAT ? ROWS_PER_BEAT : iTickCount;
|
||||
out.m_Timing.AddTickcountSegment( tcs );
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (BeginsWith(NoteRowString, "|B"))
|
||||
{
|
||||
float fCurBpm = (float)numTemp;
|
||||
//out.m_Timing.AddBPMSegment( BPMSegment( BeatToNoteRow(fCurBeat), (float)numTemp ) );
|
||||
out.m_Timing.SetBPMAtBeat( fCurBeat, fCurBpm );
|
||||
continue;
|
||||
}
|
||||
else if (BeginsWith(NoteRowString, "|E"))
|
||||
{
|
||||
// Finally! the |E| tag is working as it should. I can die happy now -DaisuMaster
|
||||
float fCurDelay = 60 / out.m_Timing.GetBPMAtBeat(fCurBeat) * (float)numTemp / iTickCount;
|
||||
fCurDelay += out.m_Timing.GetStopAtRow(BeatToNoteRow(fCurBeat) );
|
||||
out.m_Timing.SetStopAtBeat( fCurBeat, fCurDelay, true );
|
||||
continue;
|
||||
}
|
||||
else if (BeginsWith(NoteRowString, "|D"))
|
||||
{
|
||||
float fCurDelay = out.m_Timing.GetStopAtRow(BeatToNoteRow(fCurBeat) );
|
||||
fCurDelay += (float)numTemp / 1000;
|
||||
out.m_Timing.SetStopAtBeat( fCurBeat, fCurDelay, true );
|
||||
continue;
|
||||
// LOG it first.
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -580,16 +681,19 @@ bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
ASSERT( arrayKSFFileNames.size() );
|
||||
|
||||
bool bKIUCompliant = false;
|
||||
/* If only the first file is read, it will cause problems for other simfiles with
|
||||
* different BPM changes and tickcounts. This command will probably have to be
|
||||
* changed in the future. */
|
||||
if( !LoadGlobalData(out.GetSongDir() + arrayKSFFileNames[0], out, bKIUCompliant) )
|
||||
/* With Split Timing, there has to be a backup Song Timing in case
|
||||
* anything goes wrong. As these files are kept in alphabetical
|
||||
* order (hopefully), it is best to use the LAST file for timing
|
||||
* purposes, for that is the "normal", or easiest difficulty.
|
||||
* Usually. */
|
||||
unsigned files = arrayKSFFileNames.size();
|
||||
if( !LoadGlobalData(out.GetSongDir() + arrayKSFFileNames[files - 1], out, bKIUCompliant) )
|
||||
return false;
|
||||
|
||||
// load the Steps from the rest of the KSF files
|
||||
for( unsigned i=0; i<arrayKSFFileNames.size(); i++ )
|
||||
for( unsigned i=0; i<files; i++ )
|
||||
{
|
||||
Steps* pNewNotes = new Steps;
|
||||
Steps* pNewNotes = out.CreateSteps();
|
||||
if( !LoadFromKSFFile(out.GetSongDir() + arrayKSFFileNames[i], *pNewNotes, out, bKIUCompliant) )
|
||||
{
|
||||
delete pNewNotes;
|
||||
@@ -598,6 +702,7 @@ bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
out.TidyUpData();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -682,7 +682,7 @@ static bool LoadFromMidi( const RString &sPath, Song &songOut )
|
||||
double fSecondsPerBeat = (iter->tickSeconds * GUITAR_MIDI_COUNTS_PER_BEAT);
|
||||
bpmSeg.m_fBPS = float( 1. / fSecondsPerBeat );
|
||||
|
||||
songOut.m_Timing.AddBPMSegment( bpmSeg );
|
||||
songOut.m_SongTiming.AddBPMSegment( bpmSeg );
|
||||
}
|
||||
|
||||
FOREACH_CONST( MidiFileIn::TimeSignatureChange, midi.timeSignatureEvents_, iter )
|
||||
@@ -692,7 +692,7 @@ static bool LoadFromMidi( const RString &sPath, Song &songOut )
|
||||
seg.m_iNumerator = iter->numerator;
|
||||
seg.m_iDenominator = iter->denominator;
|
||||
|
||||
songOut.m_Timing.AddTimeSignatureSegment( seg );
|
||||
songOut.m_SongTiming.AddTimeSignatureSegment( seg );
|
||||
}
|
||||
|
||||
|
||||
@@ -877,13 +877,13 @@ skip_track:
|
||||
}
|
||||
}
|
||||
|
||||
Steps *pSteps = new Steps;
|
||||
Steps *pSteps = songOut.CreateSteps();
|
||||
pSteps->m_StepsType = StepsType_guitar_five;
|
||||
pSteps->SetDifficulty( (Difficulty)(gd+1) );
|
||||
pSteps->SetNoteData( noteData );
|
||||
songOut.AddSteps( pSteps );
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -956,6 +956,7 @@ bool MidiLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
if( !LoadFromMidi(sDir+vsFiles[0], out) )
|
||||
return false;
|
||||
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -591,7 +591,7 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( 0, fBPM );
|
||||
out.AddBPMSegment( newSeg );
|
||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
else
|
||||
@@ -673,7 +673,7 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
case PMS_TRACK_BPM:
|
||||
if( iVal > 0 )
|
||||
{
|
||||
out.SetBPMAtBeat( fBeat, (float) iVal );
|
||||
out.m_SongTiming.SetBPMAtBeat( fBeat, (float) iVal );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %i", fBeat, iVal );
|
||||
}
|
||||
else
|
||||
@@ -694,7 +694,7 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( BeatToNoteRow(fBeat), fBPM );
|
||||
out.AddBPMSegment( newSeg );
|
||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", fBeat, newSeg.GetBPM() );
|
||||
}
|
||||
else
|
||||
@@ -716,12 +716,12 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBeats ) )
|
||||
{
|
||||
// find the BPM at the time of this freeze
|
||||
float fBPS = out.m_Timing.GetBPMAtBeat(fBeat) / 60.0f;
|
||||
float fBPS = out.m_SongTiming.GetBPMAtBeat(fBeat) / 60.0f;
|
||||
float fBeats = StringToFloat( sBeats ) / 48.0f;
|
||||
float fFreezeSecs = fBeats / fBPS;
|
||||
|
||||
StopSegment newSeg( BeatToNoteRow(fBeat), fFreezeSecs );
|
||||
out.AddStopSegment( newSeg );
|
||||
out.m_SongTiming.AddStopSegment( newSeg );
|
||||
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg.m_fStopSeconds );
|
||||
}
|
||||
else
|
||||
@@ -750,7 +750,7 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( iStepIndex, fBPM );
|
||||
out.AddBPMSegment( newSeg );
|
||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(newSeg.m_iStartRow), newSeg.GetBPM() );
|
||||
|
||||
}
|
||||
@@ -843,7 +843,7 @@ bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
/* Create a Steps for each. */
|
||||
vector<Steps*> apSteps;
|
||||
for( unsigned i=0; i<arrayPMSFileNames.size(); i++ )
|
||||
apSteps.push_back( new Steps );
|
||||
apSteps.push_back( out.CreateSteps() );
|
||||
|
||||
// Now, with our fancy little substring, trim the titles and
|
||||
// figure out where each goes.
|
||||
@@ -940,7 +940,7 @@ bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
ConvertString( out.m_sArtist, "utf-8,japanese" );
|
||||
ConvertString( out.m_sGenre, "utf-8,japanese" );
|
||||
|
||||
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+339
-318
@@ -15,11 +15,6 @@
|
||||
|
||||
/** @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,
|
||||
@@ -103,6 +98,331 @@ bool SMLoader::LoadTimingFromFile( const RString &fn, TimingData &out )
|
||||
return true;
|
||||
}
|
||||
|
||||
void SMLoader::ProcessBGChanges( Song &out, const RString &sValueName, const RString &sPath, const RString &sParam )
|
||||
{
|
||||
BackgroundLayer iLayer = BACKGROUND_LAYER_1;
|
||||
if( sscanf(sValueName, "BGCHANGES%d", &*ConvertValue<int>(&iLayer)) == 1 )
|
||||
enum_add(iLayer, -1); // #BGCHANGES2 = BACKGROUND_LAYER_2
|
||||
|
||||
bool bValid = iLayer>=0 && iLayer<NUM_BackgroundLayer;
|
||||
if( !bValid )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has a #BGCHANGES tag \"%s\" that is out of range.", sValueName.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<RString> aBGChangeExpressions;
|
||||
split( sParam, ",", aBGChangeExpressions );
|
||||
|
||||
for( unsigned b=0; b<aBGChangeExpressions.size(); b++ )
|
||||
{
|
||||
BackgroundChange change;
|
||||
if( LoadFromBGChangesString( change, aBGChangeExpressions[b] ) )
|
||||
out.AddBackgroundChange( iLayer, change );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessAttacks( Song &out, MsdFile::value_t sParams )
|
||||
{
|
||||
// Build the RString vector here so we can write it to file again later
|
||||
for( unsigned s=1; s < sParams.params.size(); ++s )
|
||||
out.m_sAttackString.push_back( sParams[s] );
|
||||
|
||||
Attack attack;
|
||||
float end = -9999;
|
||||
|
||||
for( unsigned j=1; j < sParams.params.size(); ++j )
|
||||
{
|
||||
vector<RString> sBits;
|
||||
split( sParams[j], "=", sBits, false );
|
||||
|
||||
// Need an identifer and a value for this to work
|
||||
if( sBits.size() < 2 )
|
||||
continue;
|
||||
|
||||
TrimLeft( sBits[0] );
|
||||
TrimRight( sBits[0] );
|
||||
|
||||
if( !sBits[0].CompareNoCase("TIME") )
|
||||
attack.fStartSecond = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("LEN") )
|
||||
attack.fSecsRemaining = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("END") )
|
||||
end = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("MODS") )
|
||||
{
|
||||
attack.sModifiers = sBits[1];
|
||||
|
||||
if( end != -9999 )
|
||||
{
|
||||
attack.fSecsRemaining = end - attack.fStartSecond;
|
||||
end = -9999;
|
||||
}
|
||||
|
||||
if( attack.fSecsRemaining < 0.0f )
|
||||
attack.fSecsRemaining = 0.0f;
|
||||
|
||||
out.m_Attacks.push_back( attack );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessInstrumentTracks( Song &out, const RString &sParam )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParam, ",", vs1 );
|
||||
FOREACH_CONST( RString, vs1, s )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s, "=", vs2 );
|
||||
if( vs2.size() >= 2 )
|
||||
{
|
||||
InstrumentTrack it = StringToInstrumentTrack( vs2[0] );
|
||||
if( it != InstrumentTrack_Invalid )
|
||||
out.m_sInstrumentTrackFile[it] = vs2[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayBPMChangeExpressions;
|
||||
split( sParam, ",", arrayBPMChangeExpressions );
|
||||
|
||||
// prepare storage variables for negative BPMs -> Warps.
|
||||
float negBeat = -1;
|
||||
float negBPM = 1;
|
||||
float highspeedBeat = -1;
|
||||
bool bNotEmpty = false;
|
||||
|
||||
for( unsigned b=0; b<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 #BPMs value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayBPMChangeExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
bNotEmpty = true;
|
||||
|
||||
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( BeatToNoteRow( fBeat ), fNewBPM );
|
||||
out.AddBPMSegment( new_seg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bNotEmpty;
|
||||
}
|
||||
|
||||
void SMLoader::ProcessStops( TimingData &out, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayFreezeExpressions;
|
||||
split( sParam, ",", arrayFreezeExpressions );
|
||||
|
||||
// Prepare variables for negative stop conversion.
|
||||
float negBeat = -1;
|
||||
float negPause = 0;
|
||||
|
||||
for( unsigned f=0; f<arrayFreezeExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayFreezeValues;
|
||||
split( arrayFreezeExpressions[f], "=", arrayFreezeValues );
|
||||
if( arrayFreezeValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #STOPS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayFreezeExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] );
|
||||
const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] );
|
||||
|
||||
// Process the prior stop.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
if( negBeat + fSkipBeats > fFreezeBeat )
|
||||
fSkipBeats = fFreezeBeat - negBeat;
|
||||
|
||||
WarpSegment ws( negBeat, fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
|
||||
negBeat = -1;
|
||||
negPause = 0;
|
||||
}
|
||||
|
||||
if( fFreezeSeconds < 0.0f )
|
||||
{
|
||||
negBeat = fFreezeBeat;
|
||||
negPause = -fFreezeSeconds;
|
||||
}
|
||||
else if( fFreezeSeconds > 0.0f )
|
||||
{
|
||||
StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
|
||||
out.AddStopSegment( ss );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Process the prior stop if there was one.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
WarpSegment ws( negBeat, negBeat + fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessDelays( TimingData &out, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayDelayExpressions;
|
||||
split( sParam, ",", arrayDelayExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayDelayExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayDelayValues;
|
||||
split( arrayDelayExpressions[f], "=", arrayDelayValues );
|
||||
if( arrayDelayValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #DELAYS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayDelayExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = StringToFloat( arrayDelayValues[0] );
|
||||
const float fFreezeSeconds = StringToFloat( arrayDelayValues[1] );
|
||||
|
||||
StopSegment new_seg( BeatToNoteRow(fFreezeBeat), fFreezeSeconds, true );
|
||||
// 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)
|
||||
out.AddStopSegment( new_seg );
|
||||
else
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid delay at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessTimeSignatures( TimingData &out, const RString sParam )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParam, ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s1, "=", vs2 );
|
||||
|
||||
if( vs2.size() < 3 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with %i values.", (int)vs2.size() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( vs2[0] );
|
||||
|
||||
TimeSignatureSegment seg( BeatToNoteRow( fBeat ), StringToInt( vs2[1] ), StringToInt( vs2[2] ));
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f.", fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.m_iNumerator < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iNumerator %i.", fBeat, seg.m_iNumerator );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.m_iDenominator < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iDenominator %i.", fBeat, seg.m_iDenominator );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddTimeSignatureSegment( seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessTickcounts( TimingData &out, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayTickcountExpressions;
|
||||
split( sParam, ",", arrayTickcountExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayTickcountExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayTickcountValues;
|
||||
split( arrayTickcountExpressions[f], "=", arrayTickcountValues );
|
||||
if( arrayTickcountValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #TICKCOUNTS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayTickcountExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fTickcountBeat = StringToFloat( arrayTickcountValues[0] );
|
||||
int iTicks = clamp(atoi( arrayTickcountValues[1] ), 0, ROWS_PER_BEAT);
|
||||
|
||||
TickcountSegment new_seg( BeatToNoteRow(fTickcountBeat), iTicks );
|
||||
out.AddTickcountSegment( new_seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
{
|
||||
out.m_fBeat0OffsetInSeconds = 0;
|
||||
@@ -123,255 +443,27 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
|
||||
}
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ProcessBPMs(out, sParams[1]);
|
||||
}
|
||||
|
||||
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;
|
||||
split( arrayFreezeExpressions[f], "=", arrayFreezeValues );
|
||||
if( arrayFreezeValues.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(), arrayFreezeExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] );
|
||||
const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] );
|
||||
|
||||
// Process the prior stop.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
if( negBeat + fSkipBeats > fFreezeBeat )
|
||||
fSkipBeats = fFreezeBeat - negBeat;
|
||||
|
||||
WarpSegment ws( negBeat, negBeat + fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
|
||||
negBeat = -1;
|
||||
negPause = 0;
|
||||
}
|
||||
|
||||
if( fFreezeSeconds < 0.0f )
|
||||
{
|
||||
negBeat = fFreezeBeat;
|
||||
negPause = -fFreezeSeconds;
|
||||
}
|
||||
else if( fFreezeSeconds > 0.0f )
|
||||
{
|
||||
StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
|
||||
out.AddStopSegment( ss );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Process the prior stop if there was one.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
WarpSegment ws( negBeat, negBeat + fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
}
|
||||
ProcessStops(out, sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="DELAYS" )
|
||||
{
|
||||
vector<RString> arrayDelayExpressions;
|
||||
split( sParams[1], ",", arrayDelayExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayDelayExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayDelayValues;
|
||||
split( arrayDelayExpressions[f], "=", arrayDelayValues );
|
||||
if( arrayDelayValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
|
||||
sValueName.c_str(), arrayDelayExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = StringToFloat( arrayDelayValues[0] );
|
||||
const float fFreezeSeconds = StringToFloat( arrayDelayValues[1] );
|
||||
|
||||
StopSegment new_seg( BeatToNoteRow(fFreezeBeat), fFreezeSeconds, true );
|
||||
// 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)
|
||||
out.AddStopSegment( new_seg );
|
||||
else
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid delay at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
|
||||
}
|
||||
ProcessDelays(out, sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="TIMESIGNATURES" )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParams[1], ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s1, "=", vs2 );
|
||||
|
||||
if( vs2.size() < 3 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with %i values.", (int)vs2.size() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( vs2[0] );
|
||||
|
||||
TimeSignatureSegment seg;
|
||||
seg.m_iStartRow = BeatToNoteRow(fBeat);
|
||||
seg.m_iNumerator = StringToInt( vs2[1] );
|
||||
seg.m_iDenominator = StringToInt( vs2[2] );
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f.", fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.m_iNumerator < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iNumerator %i.", fBeat, seg.m_iNumerator );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.m_iDenominator < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iDenominator %i.", fBeat, seg.m_iDenominator );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddTimeSignatureSegment( seg );
|
||||
}
|
||||
ProcessTimeSignatures(out, sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="TICKCOUNTS" )
|
||||
{
|
||||
vector<RString> arrayTickcountExpressions;
|
||||
split( sParams[1], ",", arrayTickcountExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayTickcountExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayTickcountValues;
|
||||
split( arrayTickcountExpressions[f], "=", arrayTickcountValues );
|
||||
if( arrayTickcountValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
|
||||
sValueName.c_str(), arrayTickcountExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fTickcountBeat = StringToFloat( arrayTickcountValues[0] );
|
||||
int iTicks = StringToInt( arrayTickcountValues[1] );
|
||||
// you're lazy, let SM do the work for you... -DaisuMaster
|
||||
if( iTicks < 1) iTicks = 1;
|
||||
if( iTicks > ROWS_PER_BEAT ) iTicks = ROWS_PER_BEAT;
|
||||
|
||||
TickcountSegment new_seg( BeatToNoteRow(fTickcountBeat), iTicks );
|
||||
out.AddTickcountSegment( new_seg );
|
||||
|
||||
if(iTicks >= 1 && iTicks <= ROWS_PER_BEAT ) // Constants
|
||||
{
|
||||
// LOG->Trace( "Adding a tickcount segment: beat: %f, ticks = %d", fTickcountBeat, iTicks );
|
||||
//out.AddTickcountSegment( new_seg );
|
||||
}
|
||||
else
|
||||
{
|
||||
//LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid tickcount at beat %f, ticks %d.", fTickcountBeat, iTicks );
|
||||
//LOG->UserLog( "Song file", "(UNKNOWN)", "Clamping tickcount value to %d at beat %f.", iTicks, fTickcountBeat);
|
||||
//etc
|
||||
}
|
||||
}
|
||||
ProcessTickcounts(out, sParams[1]);
|
||||
}
|
||||
// Ensure all of the warps are handled right.
|
||||
sort(out.m_WarpSegments.begin(), out.m_WarpSegments.end());
|
||||
@@ -457,8 +549,8 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
return false;
|
||||
}
|
||||
|
||||
out.m_Timing.m_sFile = sPath;
|
||||
LoadTimingFromSMFile( msd, out.m_Timing );
|
||||
out.m_SongTiming.m_sFile = sPath;
|
||||
LoadTimingFromSMFile( msd, out.m_SongTiming );
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
@@ -512,19 +604,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
|
||||
else if( sValueName=="INSTRUMENTTRACK" )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParams[1], ",", vs1 );
|
||||
FOREACH_CONST( RString, vs1, s )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s, "=", vs2 );
|
||||
if( vs2.size() >= 2 )
|
||||
{
|
||||
InstrumentTrack it = StringToInstrumentTrack( vs2[0] );
|
||||
if( it != InstrumentTrack_Invalid )
|
||||
out.m_sInstrumentTrackFile[it] = vs2[1];
|
||||
}
|
||||
}
|
||||
ProcessInstrumentTracks( out, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="MUSICLENGTH" )
|
||||
@@ -617,27 +697,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
|
||||
else if( sValueName.Left(strlen("BGCHANGES"))=="BGCHANGES" || sValueName=="ANIMATIONS" )
|
||||
{
|
||||
BackgroundLayer iLayer = BACKGROUND_LAYER_1;
|
||||
if( sscanf(sValueName, "BGCHANGES%d", &*ConvertValue<int>(&iLayer)) == 1 )
|
||||
enum_add(iLayer, -1); // #BGCHANGES2 = BACKGROUND_LAYER_2
|
||||
|
||||
bool bValid = iLayer>=0 && iLayer<NUM_BackgroundLayer;
|
||||
if( !bValid )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has a #BGCHANGES tag \"%s\" that is out of range.", sValueName.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<RString> aBGChangeExpressions;
|
||||
split( sParams[1], ",", aBGChangeExpressions );
|
||||
|
||||
for( unsigned b=0; b<aBGChangeExpressions.size(); b++ )
|
||||
{
|
||||
BackgroundChange change;
|
||||
if( LoadFromBGChangesString( change, aBGChangeExpressions[b] ) )
|
||||
out.AddBackgroundChange( iLayer, change );
|
||||
}
|
||||
}
|
||||
ProcessBGChanges( out, sValueName, sPath, sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="FGCHANGES" )
|
||||
@@ -661,47 +721,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
// Attacks loaded from file
|
||||
else if( sValueName=="ATTACKS" )
|
||||
{
|
||||
// Build the RString vector here so we can write it to file again later
|
||||
for( unsigned s=1; s < sParams.params.size(); ++s )
|
||||
out.m_sAttackString.push_back( sParams[s] );
|
||||
|
||||
Attack attack;
|
||||
float end = -9999;
|
||||
|
||||
for( unsigned j=1; j < sParams.params.size(); ++j )
|
||||
{
|
||||
vector<RString> sBits;
|
||||
split( sParams[j], "=", sBits, false );
|
||||
|
||||
// Need an identifer and a value for this to work
|
||||
if( sBits.size() < 2 )
|
||||
continue;
|
||||
|
||||
TrimLeft( sBits[0] );
|
||||
TrimRight( sBits[0] );
|
||||
|
||||
if( !sBits[0].CompareNoCase("TIME") )
|
||||
attack.fStartSecond = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("LEN") )
|
||||
attack.fSecsRemaining = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("END") )
|
||||
end = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("MODS") )
|
||||
{
|
||||
attack.sModifiers = sBits[1];
|
||||
|
||||
if( end != -9999 )
|
||||
{
|
||||
attack.fSecsRemaining = end - attack.fStartSecond;
|
||||
end = -9999;
|
||||
}
|
||||
|
||||
if( attack.fSecsRemaining < 0.0f )
|
||||
attack.fSecsRemaining = 0.0f;
|
||||
|
||||
out.m_Attacks.push_back( attack );
|
||||
}
|
||||
}
|
||||
ProcessAttacks( out, sParams );
|
||||
}
|
||||
|
||||
else if( sValueName=="NOTES" || sValueName=="NOTES2" )
|
||||
@@ -712,7 +732,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
continue;
|
||||
}
|
||||
|
||||
Steps* pNewNotes = new Steps;
|
||||
Steps* pNewNotes = out.CreateSteps();
|
||||
LoadFromSMTokens(
|
||||
sParams[1],
|
||||
sParams[2],
|
||||
@@ -733,7 +753,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
else
|
||||
LOG->UserLog( "Song file", sPath, "has an unexpected value named \"%s\".", sValueName.c_str() );
|
||||
}
|
||||
|
||||
TidyUpData( out, bFromCache );
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -846,7 +866,7 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath
|
||||
if( !bAddStepsToSong )
|
||||
return true;
|
||||
|
||||
Steps* pNewNotes = new Steps;
|
||||
Steps* pNewNotes = pSong->CreateSteps();
|
||||
LoadFromSMTokens(
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6],
|
||||
*pNewNotes);
|
||||
@@ -926,6 +946,7 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache )
|
||||
bg.push_back( BackgroundChange(song.m_fLastBeat,song.m_sBackgroundFile) );
|
||||
} while(0);
|
||||
}
|
||||
song.TidyUpData();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+19
-1
@@ -3,11 +3,18 @@
|
||||
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "BackgroundUtil.h"
|
||||
#include "MsdFile.h" // we require the struct from here.
|
||||
|
||||
class MsdFile;
|
||||
class Song;
|
||||
class Steps;
|
||||
class TimingData;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/** @brief Reads a Song from an .SM file. */
|
||||
namespace SMLoader
|
||||
{
|
||||
@@ -25,6 +32,17 @@ namespace SMLoader
|
||||
bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot );
|
||||
bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChangeExpression );
|
||||
|
||||
|
||||
bool ProcessBPMs( TimingData &, const RString );
|
||||
void ProcessStops( TimingData &, const RString );
|
||||
void ProcessDelays( TimingData &, const RString );
|
||||
void ProcessTimeSignatures( TimingData &, const RString );
|
||||
void ProcessTickcounts( TimingData &, const RString );
|
||||
void ProcessBGChanges( Song &out, const RString &sValueName,
|
||||
const RString &sPath, const RString &sParam );
|
||||
void ProcessAttacks( Song &out, MsdFile::value_t sParams );
|
||||
void ProcessInstrumentTracks( Song &out, const RString &sParam );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+484
-389
@@ -12,12 +12,379 @@
|
||||
#include "Song.h"
|
||||
#include "SongManager.h"
|
||||
#include "Steps.h"
|
||||
#include "Attack.h"
|
||||
|
||||
/**
|
||||
* @brief A custom .edit file can only be so big before we have to reject it.
|
||||
*/
|
||||
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60 KB
|
||||
|
||||
bool SMALoader::LoadFromBGChangesString( BackgroundChange &change,
|
||||
const RString &sBGChangeExpression )
|
||||
{
|
||||
return SMLoader::LoadFromBGChangesString(change, sBGChangeExpression);
|
||||
}
|
||||
|
||||
bool SMALoader::LoadFromDir( const RString &sPath, Song &out )
|
||||
{
|
||||
vector<RString> aFileNames;
|
||||
GetApplicableFiles( sPath, aFileNames );
|
||||
|
||||
if( aFileNames.size() > 1 )
|
||||
{
|
||||
LOG->UserLog( "Song", sPath, "has more than one SMA file. Only one SMA file is allowed per song." );
|
||||
return false;
|
||||
}
|
||||
ASSERT( aFileNames.size() == 1 );
|
||||
return LoadFromSMAFile( sPath + aFileNames[0], out );
|
||||
}
|
||||
|
||||
float SMALoader::RowToBeat( RString sLine, const int iRowsPerBeat )
|
||||
{
|
||||
if( sLine.find("R") || sLine.find("r") )
|
||||
{
|
||||
sLine = sLine.Left(sLine.size()-1);
|
||||
return StringToFloat( sLine ) / iRowsPerBeat;
|
||||
}
|
||||
else
|
||||
{
|
||||
return StringToFloat( sLine );
|
||||
}
|
||||
}
|
||||
|
||||
bool SMALoader::ProcessBPMs( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayBPMChangeExpressions;
|
||||
split( sParam, ",", arrayBPMChangeExpressions );
|
||||
|
||||
// prepare storage variables for negative BPMs -> Warps.
|
||||
float negBeat = -1;
|
||||
float negBPM = 1;
|
||||
float highspeedBeat = -1;
|
||||
bool bNotEmpty = false;
|
||||
|
||||
for( unsigned b=0; b<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 #BPMs value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayBPMChangeExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
bNotEmpty = true;
|
||||
|
||||
const float fBeat = RowToBeat( arrayBPMChangeValues[0], iRowsPerBeat );
|
||||
const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] );
|
||||
|
||||
if( fNewBPM < 0.0f )
|
||||
{
|
||||
out.m_bHasNegativeBpms = true;
|
||||
negBeat = fBeat;
|
||||
negBPM = fNewBPM;
|
||||
}
|
||||
else if( fNewBPM > 0.0f )
|
||||
{
|
||||
// add in a warp.
|
||||
if( negBPM < 0 )
|
||||
{
|
||||
float endBeat = fBeat + (fNewBPM / -negBPM) * (fBeat - negBeat);
|
||||
WarpSegment new_seg(negBeat, endBeat);
|
||||
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( BeatToNoteRow( fBeat ), fNewBPM );
|
||||
out.AddBPMSegment( new_seg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bNotEmpty;
|
||||
}
|
||||
|
||||
void SMALoader::ProcessStops( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayFreezeExpressions;
|
||||
split( sParam, ",", arrayFreezeExpressions );
|
||||
|
||||
// Prepare variables for negative stop conversion.
|
||||
float negBeat = -1;
|
||||
float negPause = 0;
|
||||
|
||||
for( unsigned f=0; f<arrayFreezeExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayFreezeValues;
|
||||
split( arrayFreezeExpressions[f], "=", arrayFreezeValues );
|
||||
if( arrayFreezeValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #STOPS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayFreezeExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = RowToBeat( arrayFreezeValues[0], iRowsPerBeat );
|
||||
const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] );
|
||||
|
||||
// Process the prior stop.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
if( negBeat + fSkipBeats > fFreezeBeat )
|
||||
fSkipBeats = fFreezeBeat - negBeat;
|
||||
|
||||
WarpSegment ws( negBeat, negBeat + fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
|
||||
negBeat = -1;
|
||||
negPause = 0;
|
||||
}
|
||||
|
||||
if( fFreezeSeconds < 0.0f )
|
||||
{
|
||||
negBeat = fFreezeBeat;
|
||||
negPause = -fFreezeSeconds;
|
||||
}
|
||||
else if( fFreezeSeconds > 0.0f )
|
||||
{
|
||||
StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
|
||||
out.AddStopSegment( ss );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Process the prior stop if there was one.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
WarpSegment ws( negBeat, negBeat + fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
}
|
||||
}
|
||||
|
||||
void SMALoader::ProcessDelays( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayDelayExpressions;
|
||||
split( sParam, ",", arrayDelayExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayDelayExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayDelayValues;
|
||||
split( arrayDelayExpressions[f], "=", arrayDelayValues );
|
||||
if( arrayDelayValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #DELAYS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayDelayExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = RowToBeat( arrayDelayValues[0], iRowsPerBeat );
|
||||
const float fFreezeSeconds = StringToFloat( arrayDelayValues[1] );
|
||||
|
||||
StopSegment new_seg( BeatToNoteRow(fFreezeBeat), fFreezeSeconds, true );
|
||||
// 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)
|
||||
out.AddStopSegment( new_seg );
|
||||
else
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid delay at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
|
||||
}
|
||||
}
|
||||
|
||||
void SMALoader::ProcessTickcounts( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayTickcountExpressions;
|
||||
split( sParam, ",", arrayTickcountExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayTickcountExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayTickcountValues;
|
||||
split( arrayTickcountExpressions[f], "=", arrayTickcountValues );
|
||||
if( arrayTickcountValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #TICKCOUNTS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayTickcountExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fTickcountBeat = RowToBeat( arrayTickcountValues[0], iRowsPerBeat );
|
||||
int iTicks = clamp(atoi( arrayTickcountValues[1] ), 0, ROWS_PER_BEAT);
|
||||
|
||||
TickcountSegment new_seg( BeatToNoteRow(fTickcountBeat), iTicks );
|
||||
out.AddTickcountSegment( new_seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SMALoader::ProcessMultipliers( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayMultiplierExpressions;
|
||||
split( sParam, ",", arrayMultiplierExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayMultiplierExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayMultiplierValues;
|
||||
split( arrayMultiplierExpressions[f], "=", arrayMultiplierValues );
|
||||
if( arrayMultiplierValues.size() != 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #MULTIPLIER value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayMultiplierExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
const float fComboBeat = RowToBeat( arrayMultiplierValues[0], iRowsPerBeat );
|
||||
const int iCombos = StringToInt( arrayMultiplierValues[1] );
|
||||
ComboSegment new_seg( BeatToNoteRow( fComboBeat ), iCombos );
|
||||
out.AddComboSegment( new_seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SMALoader::ProcessBeatsPerMeasure( TimingData &out, const RString sParam )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParam, ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s1, "=", vs2 );
|
||||
|
||||
if( vs2.size() < 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid beats per measure change with %i values.", (int)vs2.size() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( vs2[0] );
|
||||
|
||||
TimeSignatureSegment seg( BeatToNoteRow( fBeat ), StringToInt( vs2[1] ), 4 );
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f.", fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.m_iNumerator < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iNumerator %i.", fBeat, seg.m_iNumerator );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddTimeSignatureSegment( seg );
|
||||
}
|
||||
}
|
||||
|
||||
float BeatToSeconds(float fromBeat, RString toSomething)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SMALoader::ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParam, ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s1, "=", vs2 );
|
||||
|
||||
if( RowToBeat(vs2[0], iRowsPerBeat) == 0 && vs2.size() == 2 ) // First one always seems to have 2.
|
||||
{
|
||||
vs2.push_back("0");
|
||||
}
|
||||
|
||||
if( vs2.size() < 3 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with %i values.", (int)vs2.size() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = RowToBeat( vs2[0], iRowsPerBeat );
|
||||
|
||||
unsigned short tmp = ( (vs2[2].find("s") || vs2[2].find("S") )
|
||||
? 1 : 0);
|
||||
|
||||
SpeedSegment seg( fBeat, StringToFloat( vs2[1] ), StringToFloat( vs2[2] ), tmp);
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f.", fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.m_fWait < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f, fWait %f.", fBeat, seg.m_fWait );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddSpeedSegment( seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SMALoader::ProcessFakes( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<RString> arrayFakeExpressions;
|
||||
split( sParam, ",", arrayFakeExpressions );
|
||||
|
||||
for( unsigned b=0; b<arrayFakeExpressions.size(); b++ )
|
||||
{
|
||||
vector<RString> arrayFakeValues;
|
||||
split( arrayFakeExpressions[b], "=", arrayFakeValues );
|
||||
// XXX: Hard to tell which file caused this.
|
||||
if( arrayFakeValues.size() != 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #FAKES value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayFakeExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = RowToBeat( arrayFakeValues[0], iRowsPerBeat );
|
||||
const float fNewBeat = StringToFloat( arrayFakeValues[1] );
|
||||
|
||||
if(fNewBeat > 0)
|
||||
out.AddFakeSegment( FakeSegment(fBeat, fNewBeat) );
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Fake at beat %f, BPM %f.", fBeat, fNewBeat );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SMALoader::LoadFromSMATokens(
|
||||
RString sStepsType,
|
||||
RString sDescription,
|
||||
@@ -28,73 +395,9 @@ void SMALoader::LoadFromSMATokens(
|
||||
Steps &out
|
||||
)
|
||||
{
|
||||
// we're loading from disk, so this is by definition already saved:
|
||||
out.SetSavedToDisk( true );
|
||||
|
||||
Trim( sStepsType );
|
||||
Trim( sDescription );
|
||||
Trim( sDifficulty );
|
||||
Trim( sNoteData );
|
||||
|
||||
// LOG->Trace( "Steps::LoadFromSMTokens()" );
|
||||
|
||||
// insert stepstype hacks from GameManager.cpp here? -aj
|
||||
out.m_StepsType = GAMEMAN->StringToStepsType( sStepsType );
|
||||
out.SetDescription( sDescription );
|
||||
out.SetCredit( sDescription ); // this is often used for both.
|
||||
out.SetDifficulty( StringToDifficulty(sDifficulty) );
|
||||
|
||||
sDescription.MakeLower();
|
||||
|
||||
// Handle hacks that originated back when StepMania didn't have
|
||||
// Difficulty_Challenge. (At least v1.64, possibly v3.0 final...)
|
||||
if( out.GetDifficulty() == Difficulty_Hard )
|
||||
{
|
||||
// HACK: SMANIAC used to be Difficulty_Hard with a special description.
|
||||
if( sDescription == "smaniac" )
|
||||
out.SetDifficulty( Difficulty_Challenge );
|
||||
|
||||
// HACK: CHALLENGE used to be Difficulty_Hard with a special description.
|
||||
if( sDescription == "challenge" )
|
||||
out.SetDifficulty( Difficulty_Challenge );
|
||||
}
|
||||
|
||||
out.SetMeter( StringToInt(sMeter) );
|
||||
vector<RString> saValues;
|
||||
split( sRadarValues, ",", saValues, true );
|
||||
int categories = NUM_RadarCategory - 1; // Fakes aren't counted in the radar values.
|
||||
if( saValues.size() == (unsigned)categories * NUM_PLAYERS )
|
||||
{
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
// Can't use the foreach anymore due to flexible radar lines.
|
||||
for( RadarCategory rc = (RadarCategory)0; rc < categories;
|
||||
enum_add<RadarCategory>( rc, 1 ) )
|
||||
{
|
||||
v[pn][rc] = StringToFloat( saValues[pn*categories + rc] );
|
||||
}
|
||||
}
|
||||
out.SetCachedRadarValues( v );
|
||||
}
|
||||
|
||||
out.SetSMNoteData( sNoteData );
|
||||
|
||||
out.TidyUpData();
|
||||
}
|
||||
|
||||
bool SMALoader::LoadFromDir( const RString &sPath, Song &out )
|
||||
{
|
||||
vector<RString> aFileNames;
|
||||
GetApplicableFiles( sPath, aFileNames );
|
||||
|
||||
if( aFileNames.size() > 1 )
|
||||
{
|
||||
LOG->UserLog( "Song", sPath, "has more than one SMA file. There can be only one!" );
|
||||
return false;
|
||||
}
|
||||
ASSERT( aFileNames.size() == 1 );
|
||||
return LoadFromSMAFile( sPath + aFileNames[0], out );
|
||||
SMLoader::LoadFromSMTokens( sStepsType, sDescription,
|
||||
sDifficulty, sMeter, sRadarValues,
|
||||
sNoteData, out );
|
||||
}
|
||||
|
||||
void SMALoader::TidyUpData( Song &song, bool bFromCache )
|
||||
@@ -113,8 +416,12 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
return false;
|
||||
}
|
||||
|
||||
out.m_Timing.m_sFile = sPath;
|
||||
LoadTimingFromSMAFile( msd, out.m_Timing );
|
||||
out.m_SongTiming.m_sFile = sPath; // songs still have their fallback timing.
|
||||
|
||||
int state = SMA_GETTING_SONG_INFO;
|
||||
Steps* pNewNotes = NULL;
|
||||
TimingData stepsTiming;
|
||||
int iRowsPerBeat = -1; // Start with an invalid value: needed for checking.
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
@@ -168,19 +475,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
|
||||
else if( sValueName=="INSTRUMENTTRACK" )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParams[1], ",", vs1 );
|
||||
FOREACH_CONST( RString, vs1, s )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s, "=", vs2 );
|
||||
if( vs2.size() >= 2 )
|
||||
{
|
||||
InstrumentTrack it = StringToInstrumentTrack( vs2[0] );
|
||||
if( it != InstrumentTrack_Invalid )
|
||||
out.m_sInstrumentTrackFile[it] = vs2[1];
|
||||
}
|
||||
}
|
||||
SMLoader::ProcessInstrumentTracks( out, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="MUSICLENGTH" )
|
||||
@@ -202,7 +497,8 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
}
|
||||
else if( sValueName=="LASTBEAT" )
|
||||
{
|
||||
; }
|
||||
;
|
||||
}
|
||||
else if( sValueName=="SONGFILENAME" )
|
||||
{
|
||||
;
|
||||
@@ -226,6 +522,11 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
//else if( sValueName=="SAMPLEPATH" )
|
||||
//out.m_sMusicSamplePath = sParams[1];
|
||||
|
||||
else if( sValueName=="LISTSORT" )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
else if( sValueName=="DISPLAYBPM" )
|
||||
{
|
||||
// #DISPLAYBPM:[xxx][xxx:xxx]|[*];
|
||||
@@ -242,6 +543,41 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="SMAVERSION" )
|
||||
{
|
||||
; // ignore it.
|
||||
}
|
||||
|
||||
else if( sValueName=="ROWSPERBEAT" )
|
||||
{
|
||||
/* This value is used to help translate the timings
|
||||
* the SMA format uses. Starting with the second
|
||||
* appearance, it delimits NoteData. Right now, this
|
||||
* value doesn't seem to be editable in SMA. When it
|
||||
* becomes so, make adjustments to this code. */
|
||||
if( iRowsPerBeat < 0 )
|
||||
{
|
||||
vector<RString> arrayBeatChangeExpressions;
|
||||
split( sParams[1], ",", arrayBeatChangeExpressions );
|
||||
|
||||
vector<RString> arrayBeatChangeValues;
|
||||
split( arrayBeatChangeExpressions[0], "=", arrayBeatChangeValues );
|
||||
iRowsPerBeat = StringToInt(arrayBeatChangeValues[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
state = SMA_GETTING_STEP_INFO;
|
||||
pNewNotes = new Steps;
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="BEATSPERMEASURE" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessBeatsPerMeasure( timing, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="SELECTABLE" )
|
||||
{
|
||||
if(sParams[1].EqualsNoCase("YES"))
|
||||
@@ -265,27 +601,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
|
||||
else if( sValueName.Left(strlen("BGCHANGES"))=="BGCHANGES" || sValueName=="ANIMATIONS" )
|
||||
{
|
||||
BackgroundLayer iLayer = BACKGROUND_LAYER_1;
|
||||
if( sscanf(sValueName, "BGCHANGES%d", &*ConvertValue<int>(&iLayer)) == 1 )
|
||||
enum_add(iLayer, -1); // #BGCHANGES2 = BACKGROUND_LAYER_2
|
||||
|
||||
bool bValid = iLayer>=0 && iLayer<NUM_BackgroundLayer;
|
||||
if( !bValid )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has a #BGCHANGES tag \"%s\" that is out of range.", sValueName.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<RString> aBGChangeExpressions;
|
||||
split( sParams[1], ",", aBGChangeExpressions );
|
||||
|
||||
for( unsigned b=0; b<aBGChangeExpressions.size(); b++ )
|
||||
{
|
||||
BackgroundChange change;
|
||||
if( LoadFromBGChangesString( change, aBGChangeExpressions[b] ) )
|
||||
out.AddBackgroundChange( iLayer, change );
|
||||
}
|
||||
}
|
||||
SMLoader::ProcessBGChanges( out, sValueName, sPath, sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="FGCHANGES" )
|
||||
@@ -301,6 +617,65 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="OFFSET" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
timing.m_fBeat0OffsetInSeconds = StringToFloat( sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="BPMS" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessBPMs( timing, iRowsPerBeat, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="STOPS" || sValueName=="FREEZES" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessStops( timing, iRowsPerBeat, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="DELAYS" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessDelays( timing, iRowsPerBeat, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="TICKCOUNT" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessTickcounts( timing, iRowsPerBeat, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="SPEED" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessSpeeds( timing, iRowsPerBeat, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="MULTIPLIER" )
|
||||
{
|
||||
ProcessMultipliers( pNewNotes->m_Timing, iRowsPerBeat, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="FAKES" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessFakes( timing, iRowsPerBeat, sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="METERTYPE" )
|
||||
{
|
||||
; // We don't use this...yet.
|
||||
}
|
||||
|
||||
else if( sValueName=="KEYSOUNDS" )
|
||||
{
|
||||
split( sParams[1], ",", out.m_vsKeysoundFile );
|
||||
@@ -309,47 +684,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
// Attacks loaded from file
|
||||
else if( sValueName=="ATTACKS" )
|
||||
{
|
||||
// Build the RString vector here so we can write it to file again later
|
||||
for( unsigned s=1; s < sParams.params.size(); ++s )
|
||||
out.m_sAttackString.push_back( sParams[s] );
|
||||
|
||||
Attack attack;
|
||||
float end = -9999;
|
||||
|
||||
for( unsigned j=1; j < sParams.params.size(); ++j )
|
||||
{
|
||||
vector<RString> sBits;
|
||||
split( sParams[j], "=", sBits, false );
|
||||
|
||||
// Need an identifer and a value for this to work
|
||||
if( sBits.size() < 2 )
|
||||
continue;
|
||||
|
||||
TrimLeft( sBits[0] );
|
||||
TrimRight( sBits[0] );
|
||||
|
||||
if( !sBits[0].CompareNoCase("TIME") )
|
||||
attack.fStartSecond = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("LEN") )
|
||||
attack.fSecsRemaining = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("END") )
|
||||
end = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("MODS") )
|
||||
{
|
||||
attack.sModifiers = sBits[1];
|
||||
|
||||
if( end != -9999 )
|
||||
{
|
||||
attack.fSecsRemaining = end - attack.fStartSecond;
|
||||
end = -9999;
|
||||
}
|
||||
|
||||
if( attack.fSecsRemaining < 0.0f )
|
||||
attack.fSecsRemaining = 0.0f;
|
||||
|
||||
out.m_Attacks.push_back( attack );
|
||||
}
|
||||
}
|
||||
SMLoader::ProcessAttacks( out, sParams );
|
||||
}
|
||||
|
||||
else if( sValueName=="NOTES" || sValueName=="NOTES2" )
|
||||
@@ -360,7 +695,6 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
continue;
|
||||
}
|
||||
|
||||
Steps* pNewNotes = new Steps;
|
||||
LoadFromSMATokens(
|
||||
sParams[1],
|
||||
sParams[2],
|
||||
@@ -372,16 +706,13 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
/*
|
||||
* We used to check for timing data in this section. That has
|
||||
* since been moved to a dedicated function.
|
||||
*/
|
||||
else if( sValueName=="OFFSET" || sValueName=="BPMS" || sValueName=="STOPS" || sValueName=="FREEZES" || sValueName=="DELAYS" || sValueName=="TIMESIGNATURES" || sValueName=="LEADTRACK" || sValueName=="TICKCOUNTS" )
|
||||
else if( sValueName=="TIMESIGNATURES" || sValueName=="LEADTRACK" )
|
||||
;
|
||||
else
|
||||
LOG->UserLog( "Song file", sPath, "has an unexpected value named \"%s\".", sValueName.c_str() );
|
||||
}
|
||||
|
||||
TidyUpData(out, false);
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -390,236 +721,6 @@ void SMALoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.sma"), out );
|
||||
}
|
||||
|
||||
bool SMALoader::LoadTimingFromFile( const RString &fn, TimingData &out )
|
||||
{
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( fn, true ) ) // unescape
|
||||
{
|
||||
LOG->UserLog( "Song file", fn, "couldn't be loaded: %s", msd.GetError().c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
out.m_sFile = fn;
|
||||
LoadTimingFromSMAFile( msd, out );
|
||||
return true;
|
||||
}
|
||||
|
||||
void SMALoader::LoadTimingFromSMAFile( const MsdFile &msd, TimingData &out )
|
||||
{
|
||||
out.m_fBeat0OffsetInSeconds = 0;
|
||||
out.m_BPMSegments.clear();
|
||||
out.m_StopSegments.clear();
|
||||
out.m_WarpSegments.clear();
|
||||
out.m_vTimeSignatureSegments.clear();
|
||||
|
||||
vector<WarpSegment> arrayWarpsFromNegativeBPMs;
|
||||
//vector<WarpSegment> arrayWarpsFromNegativeStops;
|
||||
int rowsPerMeasure = 0;
|
||||
bool encountered = false;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
const MsdFile::value_t &sParams = msd.GetValue(i);
|
||||
RString sValueName = sParams[0];
|
||||
sValueName.MakeUpper();
|
||||
|
||||
if( sValueName=="ROWSPERBEAT")
|
||||
{
|
||||
if( encountered )
|
||||
{
|
||||
break;
|
||||
}
|
||||
encountered = true;
|
||||
rowsPerMeasure = StringToInt( sParams[1] );
|
||||
}
|
||||
else if( sValueName=="BEATSPERMEASURE" )
|
||||
{
|
||||
TimeSignatureSegment new_seg;
|
||||
new_seg.m_iStartRow = 0;
|
||||
new_seg.m_iNumerator = StringToInt( sParams[1] );
|
||||
new_seg.m_iDenominator = 4;
|
||||
out.AddTimeSignatureSegment( new_seg );
|
||||
}
|
||||
else if( sValueName=="OFFSET" )
|
||||
{
|
||||
out.m_fBeat0OffsetInSeconds = StringToFloat( sParams[1] );
|
||||
}
|
||||
else if( sValueName=="STOPS" )
|
||||
{
|
||||
vector<RString> arrayFreezeExpressions;
|
||||
split( sParams[1], ",", arrayFreezeExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayFreezeExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayFreezeValues;
|
||||
split( arrayFreezeExpressions[f], "=", arrayFreezeValues );
|
||||
if( arrayFreezeValues.size() != 2 )
|
||||
{
|
||||
// XXX: Hard to tell which file caused this.
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
|
||||
sValueName.c_str(), arrayFreezeExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
float fFreezeBeat = 0;
|
||||
RString beat = arrayFreezeValues[0];
|
||||
if( beat.Right(0).MakeUpper() == "R" )
|
||||
{
|
||||
beat = beat.Left(beat.size()-1);
|
||||
fFreezeBeat = StringToFloat( beat ) / rowsPerMeasure;
|
||||
}
|
||||
else
|
||||
{
|
||||
fFreezeBeat = StringToFloat(beat);
|
||||
}
|
||||
|
||||
//float fFreezeBeat = StringToFloat( arrayBPMChangeValues[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)
|
||||
{
|
||||
// LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
|
||||
out.AddStopSegment( new_seg );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
float fBeat = 0;
|
||||
RString beat = arrayBPMChangeValues[0];
|
||||
if( beat.Right(0).MakeUpper() == "R" )
|
||||
{
|
||||
beat = beat.Left(beat.size()-1);
|
||||
fBeat = StringToFloat( beat ) / rowsPerMeasure;
|
||||
}
|
||||
else
|
||||
{
|
||||
fBeat = StringToFloat(beat);
|
||||
}
|
||||
|
||||
//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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
bool SMALoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong )
|
||||
{
|
||||
LOG->Trace( "SMALoader::LoadEditFromFile(%s)", sEditFilePath.c_str() );
|
||||
@@ -702,7 +803,7 @@ bool SMALoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
if( !bAddStepsToSong )
|
||||
return true;
|
||||
|
||||
Steps* pNewNotes = new Steps;
|
||||
Steps* pNewNotes = pSong->CreateSteps();
|
||||
LoadFromSMATokens(
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6],
|
||||
*pNewNotes);
|
||||
@@ -730,12 +831,6 @@ bool SMALoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SMALoader::LoadFromBGChangesString( BackgroundChange &change,
|
||||
const RString &sBGChangeExpression )
|
||||
{
|
||||
return SMLoader::LoadFromBGChangesString(change, sBGChangeExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @author Aldo Fregoso, Jason Felds (c) 2009-2011
|
||||
|
||||
+22
-2
@@ -9,6 +9,16 @@ class Song;
|
||||
class Steps;
|
||||
class TimingData;
|
||||
|
||||
/**
|
||||
* @brief The various states while parsing a .sma file.
|
||||
*/
|
||||
enum SMALoadingStates
|
||||
{
|
||||
SMA_GETTING_SONG_INFO, /**< Retrieving song information. */
|
||||
SMA_GETTING_STEP_INFO, /**< Retrieving step information. */
|
||||
NUM_SMALoadingStates /**< The number of states used. */
|
||||
};
|
||||
|
||||
/** @brief Reads a Song from a .SMA file. */
|
||||
namespace SMALoader
|
||||
{
|
||||
@@ -25,12 +35,22 @@ namespace SMALoader
|
||||
|
||||
bool LoadFromSMAFile( const RString &sPath, Song &out );
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadTimingFromFile( const RString &fn, TimingData &out );
|
||||
void LoadTimingFromSMAFile( const MsdFile &msd, TimingData &out );
|
||||
|
||||
bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot );
|
||||
bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChangeExpression );
|
||||
|
||||
void ProcessBeatsPerMeasure( TimingData &out, const RString sParam );
|
||||
bool ProcessBPMs( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessStops( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessDelays( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessTickcounts( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessMultipliers( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessFakes( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
|
||||
float RowToBeat( RString sLine, const int iRowsPerBeat );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+271
-535
File diff suppressed because it is too large
Load Diff
@@ -16,8 +16,6 @@ enum SSCLoadingStates
|
||||
{
|
||||
GETTING_SONG_INFO, /**< Retrieving song information. */
|
||||
GETTING_STEP_INFO, /**< Retrieving step information. */
|
||||
GETTING_STEP_TIMING_INFO, /**< Retrieving a step's individual timing information. */
|
||||
GETTING_NOTE_INFO, /**< Retrieving the specific notes. This state may be deprecated. */
|
||||
NUM_SSCLoadingStates /**< The number of states used. */
|
||||
};
|
||||
|
||||
@@ -25,6 +23,8 @@ enum SSCLoadingStates
|
||||
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 version that formally introduced Split Timing. */
|
||||
const float VERSION_SPLIT_TIMING = 0.7f;
|
||||
|
||||
/**
|
||||
* @brief The SSCLoader handles all of the parsing needed for .ssc files.
|
||||
@@ -75,6 +75,13 @@ namespace SSCLoader
|
||||
* @param bFromCache a flag to determine if this song is loaded from a cache file.
|
||||
*/
|
||||
void TidyUpData( Song &song, bool bFromCache );
|
||||
|
||||
|
||||
void ProcessWarps( TimingData &, const RString, const float );
|
||||
void ProcessLabels( TimingData &, const RString );
|
||||
void ProcessCombos( TimingData &, const RString );
|
||||
void ProcessSpeeds( TimingData &, const RString );
|
||||
void ProcessFakes( TimingData &, const RString );
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
|
||||
+11
-11
@@ -350,10 +350,10 @@ bool NotesWriterDWI::Write( RString sPath, const Song &out )
|
||||
/* Write transliterations, if we have them, since DWI doesn't support UTF-8. */
|
||||
f.PutLine( ssprintf("#TITLE:%s;", DwiEscape(out.GetTranslitFullTitle()).c_str()) );
|
||||
f.PutLine( ssprintf("#ARTIST:%s;", DwiEscape(out.GetTranslitArtist()).c_str()) );
|
||||
ASSERT( out.m_Timing.m_BPMSegments[0].m_iStartRow == 0 );
|
||||
ASSERT( out.m_SongTiming.m_BPMSegments[0].m_iStartRow == 0 );
|
||||
f.PutLine( ssprintf("#FILE:%s;", DwiEscape(out.m_sMusicFile).c_str()) );
|
||||
f.PutLine( ssprintf("#BPM:%.3f;", out.m_Timing.m_BPMSegments[0].GetBPM()) );
|
||||
f.PutLine( ssprintf("#GAP:%ld;", -lrintf( out.m_Timing.m_fBeat0OffsetInSeconds*1000 )) );
|
||||
f.PutLine( ssprintf("#BPM:%.3f;", out.m_SongTiming.m_BPMSegments[0].GetBPM()) );
|
||||
f.PutLine( ssprintf("#GAP:%ld;", -lrintf( out.m_SongTiming.m_fBeat0OffsetInSeconds*1000 )) );
|
||||
f.PutLine( ssprintf("#SAMPLESTART:%.3f;", out.m_fMusicSampleStartSeconds) );
|
||||
f.PutLine( ssprintf("#SAMPLELENGTH:%.3f;", out.m_fMusicSampleLengthSeconds) );
|
||||
if( out.m_sCDTitleFile.size() )
|
||||
@@ -374,29 +374,29 @@ bool NotesWriterDWI::Write( RString sPath, const Song &out )
|
||||
break;
|
||||
}
|
||||
|
||||
if( !out.m_Timing.m_StopSegments.empty() )
|
||||
if( !out.m_SongTiming.m_StopSegments.empty() )
|
||||
{
|
||||
f.Write( "#FREEZE:" );
|
||||
|
||||
for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ )
|
||||
for( unsigned i=0; i<out.m_SongTiming.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_Timing.m_StopSegments[i];
|
||||
const StopSegment &fs = out.m_SongTiming.m_StopSegments[i];
|
||||
f.Write( ssprintf("%.3f=%.3f", fs.m_iStartRow * 4.0f / ROWS_PER_BEAT,
|
||||
roundf(fs.m_fStopSeconds*1000)) );
|
||||
if( i != out.m_Timing.m_StopSegments.size()-1 )
|
||||
if( i != out.m_SongTiming.m_StopSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
if( out.m_Timing.m_BPMSegments.size() > 1)
|
||||
if( out.m_SongTiming.m_BPMSegments.size() > 1)
|
||||
{
|
||||
f.Write( "#CHANGEBPM:" );
|
||||
for( unsigned i=1; i<out.m_Timing.m_BPMSegments.size(); i++ )
|
||||
for( unsigned i=1; i<out.m_SongTiming.m_BPMSegments.size(); i++ )
|
||||
{
|
||||
const BPMSegment &bs = out.m_Timing.m_BPMSegments[i];
|
||||
const BPMSegment &bs = out.m_SongTiming.m_BPMSegments[i];
|
||||
f.Write( ssprintf("%.3f=%.3f", bs.m_iStartRow * 4.0f / ROWS_PER_BEAT, bs.GetBPM() ) );
|
||||
if( i != out.m_Timing.m_BPMSegments.size()-1 )
|
||||
if( i != out.m_SongTiming.m_BPMSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
@@ -137,7 +137,7 @@ bool NotesWriterJson::WriteSong( const RString &sFile, const Song &out, bool bWr
|
||||
root["LyricsFile"] = out.m_sLyricsFile;
|
||||
root["CDTitle"] = out.m_sCDTitleFile;
|
||||
root["Music"] = out.m_sMusicFile;
|
||||
root["Offset"] = out.m_Timing.m_fBeat0OffsetInSeconds;
|
||||
root["Offset"] = out.m_SongTiming.m_fBeat0OffsetInSeconds;
|
||||
root["SampleStart"] = out.m_fMusicSampleStartSeconds;
|
||||
root["SampleLength"] = out.m_fMusicSampleLengthSeconds;
|
||||
if( out.m_SelectionDisplay == Song::SHOW_ALWAYS )
|
||||
@@ -161,7 +161,7 @@ bool NotesWriterJson::WriteSong( const RString &sFile, const Song &out, bool bWr
|
||||
root["SpecifiedBpmMax"] = out.m_fSpecifiedBPMMax;
|
||||
}
|
||||
|
||||
Serialize( out.m_Timing, root["TimingData"] );
|
||||
Serialize( out.m_SongTiming, root["TimingData"] );
|
||||
JsonUtil::SerializeVectorObjects( out.m_LyricSegments, Serialize, root["LyricSegments"] );
|
||||
|
||||
{
|
||||
|
||||
+38
-78
@@ -48,7 +48,7 @@ static RString BackgroundChangeToString( const BackgroundChange &bgc )
|
||||
* @brief Write out the common tags for .SM files.
|
||||
* @param f the file in question.
|
||||
* @param out the Song in question. */
|
||||
static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
static void WriteGlobalTags( RageFile &f, Song &out )
|
||||
{
|
||||
f.PutLine( ssprintf( "#TITLE:%s;", SmEscape(out.m_sMainTitle).c_str() ) );
|
||||
f.PutLine( ssprintf( "#SUBTITLE:%s;", SmEscape(out.m_sSubTitle).c_str() ) );
|
||||
@@ -63,20 +63,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.PutLine( ssprintf( "#LYRICSPATH:%s;", SmEscape(out.m_sLyricsFile).c_str() ) );
|
||||
f.PutLine( ssprintf( "#CDTITLE:%s;", SmEscape(out.m_sCDTitleFile).c_str() ) );
|
||||
f.PutLine( ssprintf( "#MUSIC:%s;", SmEscape(out.m_sMusicFile).c_str() ) );
|
||||
|
||||
{
|
||||
vector<RString> vs;
|
||||
FOREACH_ENUM( InstrumentTrack, it )
|
||||
if( out.HasInstrumentTrack(it) )
|
||||
vs.push_back( InstrumentTrackToString(it) +
|
||||
"=" + out.m_sInstrumentTrackFile[it] );
|
||||
if( !vs.empty() )
|
||||
{
|
||||
RString s = join( ",", vs );
|
||||
f.PutLine( "#INSTRUMENTTRACK:" + s + ";\n" );
|
||||
}
|
||||
}
|
||||
f.PutLine( ssprintf( "#OFFSET:%.3f;", out.m_Timing.m_fBeat0OffsetInSeconds ) );
|
||||
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 )
|
||||
@@ -111,82 +98,55 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
|
||||
|
||||
f.Write( "#BPMS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_BPMSegments.size(); i++ )
|
||||
for( unsigned i=0; i<out.m_SongTiming.m_BPMSegments.size(); i++ )
|
||||
{
|
||||
const BPMSegment &bs = out.m_Timing.m_BPMSegments[i];
|
||||
const BPMSegment &bs = out.m_SongTiming.m_BPMSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", NoteRowToBeat(bs.m_iStartRow), bs.GetBPM() ) );
|
||||
if( i != out.m_Timing.m_BPMSegments.size()-1 )
|
||||
if( i != out.m_SongTiming.m_BPMSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
unsigned wSize = out.m_SongTiming.m_WarpSegments.size();
|
||||
if( wSize > 0 )
|
||||
{
|
||||
for( unsigned i=0; i < wSize; i++ )
|
||||
{
|
||||
int iRow = out.m_SongTiming.m_WarpSegments[i].m_iStartRow;
|
||||
float fBPS = 60 / out.m_SongTiming.GetBPMAtRow(iRow);
|
||||
float fSkip = fBPS * out.m_SongTiming.m_WarpSegments[i].m_fLengthBeats;
|
||||
StopSegment ss;
|
||||
ss.m_iStartRow = iRow;
|
||||
ss.m_fStopSeconds = -fSkip;
|
||||
ss.m_bDelay = false; // Best to be sure.
|
||||
out.m_SongTiming.AddStopSegment( ss );
|
||||
}
|
||||
}
|
||||
|
||||
f.Write( "#STOPS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ )
|
||||
for( unsigned i=0; i<out.m_SongTiming.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_Timing.m_StopSegments[i];
|
||||
|
||||
const StopSegment &fs = out.m_SongTiming.m_StopSegments[i];
|
||||
int iRow = fs.m_iStartRow;
|
||||
float fBeat = NoteRowToBeat(!fs.m_bDelay ? iRow : iRow - 1);
|
||||
|
||||
if(!fs.m_bDelay)
|
||||
{
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
if( i != out.m_Timing.m_StopSegments.size()-1 )
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", fBeat, fs.m_fStopSeconds ) );
|
||||
if( i != out.m_SongTiming.m_StopSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
if( fs.m_fStopSeconds < 0 )
|
||||
{
|
||||
out.m_SongTiming.m_StopSegments.erase(
|
||||
out.m_SongTiming.m_StopSegments.begin()+i,
|
||||
out.m_SongTiming.m_StopSegments.begin()+i+1 );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#ATTACKS:" );
|
||||
for( unsigned j = 0; j < out.m_Attacks.size(); j++ )
|
||||
{
|
||||
const Attack &a = out.m_Attacks[j];
|
||||
f.Write( ssprintf( "TIME=%.2f:LEN=%.2f:MODS=%s",
|
||||
a.fStartSecond, a.fSecsRemaining, a.sModifiers.c_str() ) );
|
||||
|
||||
if( j+1 < out.m_Attacks.size() )
|
||||
f.Write( ":" );
|
||||
f.PutLine( "" );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#DELAYS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_Timing.m_StopSegments[i];
|
||||
|
||||
if( fs.m_bDelay )
|
||||
{
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
if( i != out.m_Timing.m_StopSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() );
|
||||
f.Write( "#TIMESIGNATURES:" );
|
||||
FOREACH_CONST( TimeSignatureSegment, out.m_Timing.m_vTimeSignatureSegments, iter )
|
||||
{
|
||||
f.PutLine( ssprintf( "%.3f=%d=%d", NoteRowToBeat(iter->m_iStartRow),
|
||||
iter->m_iNumerator, iter->m_iDenominator ) );
|
||||
vector<TimeSignatureSegment>::const_iterator iter2 = iter;
|
||||
iter2++;
|
||||
if( iter2 != out.m_Timing.m_vTimeSignatureSegments.end() )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
ASSERT( !out.m_Timing.m_TickcountSegments.empty() );
|
||||
f.Write( "#TICKCOUNTS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_TickcountSegments.size(); i++ )
|
||||
{
|
||||
const TickcountSegment &ts = out.m_Timing.m_TickcountSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.3f=%d", NoteRowToBeat(ts.m_iStartRow), ts.m_iTicks ) );
|
||||
if( i != out.m_Timing.m_TickcountSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
FOREACH_BackgroundLayer( b )
|
||||
{
|
||||
if( b==0 )
|
||||
@@ -277,8 +237,8 @@ static RString GetSMNotesTag( const Song &song, const Steps &in )
|
||||
lines.push_back( ssprintf( " %d:", in.GetMeter() ) );
|
||||
|
||||
vector<RString> asRadarValues;
|
||||
// SM files don't use fakes for radar data. Keep it that way.
|
||||
int categories = NUM_RadarCategory - 1;
|
||||
// OpenITG simfiles use 11 radar categories.
|
||||
int categories = 11;
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
const RadarValues &rv = in.GetRadarValues( pn );
|
||||
@@ -300,7 +260,7 @@ static RString GetSMNotesTag( const Song &song, const Steps &in )
|
||||
return JoinLineList( lines );
|
||||
}
|
||||
|
||||
bool NotesWriterSM::Write( RString sPath, const Song &out, const vector<Steps*>& vpStepsToSave )
|
||||
bool NotesWriterSM::Write( RString sPath, Song &out, const vector<Steps*>& vpStepsToSave )
|
||||
{
|
||||
int flags = RageFile::WRITE;
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ namespace NotesWriterSM
|
||||
* @param sPath the path to write the file.
|
||||
* @param out the Song to be written out.
|
||||
* @return its success or failure. */
|
||||
bool Write( RString sPath, const Song &out, const vector<Steps*>& vpStepsToSave );
|
||||
bool Write( RString sPath, Song &out, const vector<Steps*>& vpStepsToSave );
|
||||
/**
|
||||
* @brief Get some contents about the edit file first.
|
||||
* @param pSong the Song in question.
|
||||
|
||||
+130
-130
@@ -42,7 +42,128 @@ static RString BackgroundChangeToString( const BackgroundChange &bgc )
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write out the common tags for .SM files.
|
||||
* @brief Turn a vector of lines into a single line joined by newline characters.
|
||||
* @param lines the list of lines to join.
|
||||
* @return the joined lines. */
|
||||
static RString JoinLineList( vector<RString> &lines )
|
||||
{
|
||||
for( unsigned i = 0; i < lines.size(); ++i )
|
||||
TrimRight( lines[i] );
|
||||
|
||||
// Skip leading blanks.
|
||||
unsigned j = 0;
|
||||
while( j < lines.size() && lines.size() == 0 )
|
||||
++j;
|
||||
|
||||
return join( "\r\n", lines.begin()+j, lines.end() );
|
||||
}
|
||||
|
||||
|
||||
// A utility class to write timing tags more easily!
|
||||
struct TimingTagWriter {
|
||||
|
||||
vector<RString> *m_pvsLines;
|
||||
RString m_sNext;
|
||||
|
||||
TimingTagWriter( vector<RString> *pvsLines ): m_pvsLines (pvsLines) { }
|
||||
|
||||
void Write( const int row, const char *value )
|
||||
{
|
||||
m_pvsLines->push_back( m_sNext + ssprintf( "%.6f=%s", NoteRowToBeat(row), value ) );
|
||||
m_sNext = ",";
|
||||
}
|
||||
|
||||
void Write( const int row, const float value ) { Write( row, ssprintf( "%.6f", value ) ); }
|
||||
void Write( const int row, const int value ) { Write( row, ssprintf( "%d", value ) ); }
|
||||
void Write( const int row, const int a, const int b ) { Write( row, ssprintf( "%d=%d", a, b ) ); }
|
||||
void Write( const int row, const float a, const float b ) { Write( row, ssprintf( "%.6f=%.6f", a, b) ); }
|
||||
void Write( const int row, const float a, const float b, const unsigned short c )
|
||||
{ Write( row, ssprintf( "%.6f=%.6f=%hd", a, b, c) ); }
|
||||
|
||||
void Init( const RString sTag ) { m_sNext = "#" + sTag + ":"; }
|
||||
void Finish( ) { m_pvsLines->push_back( ( m_sNext != "," ? m_sNext : "" ) + ";" ); }
|
||||
|
||||
};
|
||||
|
||||
static void GetTimingTags( vector<RString> &lines, TimingData timing, bool bIsSong = false )
|
||||
{
|
||||
TimingTagWriter w ( &lines );
|
||||
|
||||
timing.TidyUpData();
|
||||
|
||||
w.Init( "BPMS" );
|
||||
FOREACH_CONST( BPMSegment, timing.m_BPMSegments, bs )
|
||||
w.Write( bs->m_iStartRow, bs->GetBPM() );
|
||||
w.Finish();
|
||||
|
||||
w.Init( "STOPS" );
|
||||
FOREACH_CONST( StopSegment, timing.m_StopSegments, ss )
|
||||
if( !ss->m_bDelay )
|
||||
w.Write( ss->m_iStartRow, ss->m_fStopSeconds );
|
||||
w.Finish();
|
||||
|
||||
w.Init( "DELAYS" );
|
||||
FOREACH_CONST( StopSegment, timing.m_StopSegments, ss )
|
||||
if( ss->m_bDelay )
|
||||
w.Write( ss->m_iStartRow, ss->m_fStopSeconds );
|
||||
w.Finish();
|
||||
|
||||
w.Init( "WARPS" );
|
||||
FOREACH_CONST( WarpSegment, timing.m_WarpSegments, ws )
|
||||
w.Write( ws->m_iStartRow, ws->m_fLengthBeats );
|
||||
w.Finish();
|
||||
|
||||
ASSERT( !timing.m_vTimeSignatureSegments.empty() );
|
||||
w.Init( "TIMESIGNATURES" );
|
||||
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, iter )
|
||||
w.Write( iter->m_iStartRow, iter->m_iNumerator, iter->m_iDenominator );
|
||||
w.Finish();
|
||||
|
||||
ASSERT( !timing.m_TickcountSegments.empty() );
|
||||
w.Init( "TICKCOUNTS" );
|
||||
FOREACH_CONST( TickcountSegment, timing.m_TickcountSegments, ts )
|
||||
w.Write( ts->m_iStartRow, ts->m_iTicks );
|
||||
w.Finish();
|
||||
|
||||
ASSERT( !timing.m_ComboSegments.empty() );
|
||||
w.Init( "COMBOS" );
|
||||
FOREACH_CONST( ComboSegment, timing.m_ComboSegments, cs )
|
||||
w.Write( cs->m_iStartRow, cs->m_iCombo );
|
||||
w.Finish();
|
||||
|
||||
// Song Timing should only have the initial value.
|
||||
w.Init( "SPEEDS" );
|
||||
FOREACH_CONST( SpeedSegment, timing.m_SpeedSegments, ss )
|
||||
w.Write( ss->m_iStartRow, ss->m_fPercent, ss->m_fWait, ss->m_usMode );
|
||||
w.Finish();
|
||||
|
||||
if( !bIsSong )
|
||||
{
|
||||
w.Init( "FAKES" );
|
||||
FOREACH_CONST( FakeSegment, timing.m_FakeSegments, fs )
|
||||
w.Write( fs->m_iStartRow, fs->m_fLengthBeats );
|
||||
w.Finish();
|
||||
}
|
||||
|
||||
w.Init( "LABELS" );
|
||||
FOREACH_CONST( LabelSegment, timing.m_LabelSegments, ls )
|
||||
w.Write( ls->m_iStartRow, ls->m_sLabel.c_str() );
|
||||
w.Finish();
|
||||
}
|
||||
|
||||
static void WriteTimingTags( RageFile &f, const TimingData &timing, bool bIsSong = false )
|
||||
{
|
||||
|
||||
vector<RString> lines;
|
||||
|
||||
GetTimingTags( lines, timing, bIsSong );
|
||||
|
||||
f.PutLine( JoinLineList( lines ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write out the common tags for .SSC files.
|
||||
* @param f the file in question.
|
||||
* @param out the Song in question. */
|
||||
static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
@@ -74,7 +195,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.PutLine( "#INSTRUMENTTRACK:" + s + ";\n" );
|
||||
}
|
||||
}
|
||||
f.PutLine( ssprintf( "#OFFSET:%.6f;", out.m_Timing.m_fBeat0OffsetInSeconds ) );
|
||||
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 )
|
||||
@@ -106,105 +227,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
break;
|
||||
}
|
||||
|
||||
f.Write( "#BPMS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_BPMSegments.size(); i++ )
|
||||
{
|
||||
const BPMSegment &bs = out.m_Timing.m_BPMSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(bs.m_iStartRow), bs.GetBPM() ) );
|
||||
if( i != out.m_Timing.m_BPMSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#STOPS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_Timing.m_StopSegments[i];
|
||||
|
||||
if(!fs.m_bDelay)
|
||||
{
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
if( i != out.m_Timing.m_StopSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#DELAYS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_Timing.m_StopSegments[i];
|
||||
|
||||
if( fs.m_bDelay )
|
||||
{
|
||||
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fStopSeconds ) );
|
||||
if( i != out.m_Timing.m_StopSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
}
|
||||
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_fEndBeat ) );
|
||||
if( i != out.m_Timing.m_WarpSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
|
||||
ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() );
|
||||
f.Write( "#TIMESIGNATURES:" );
|
||||
FOREACH_CONST( TimeSignatureSegment, out.m_Timing.m_vTimeSignatureSegments, iter )
|
||||
{
|
||||
f.PutLine( ssprintf( "%.6f=%d=%d", NoteRowToBeat(iter->m_iStartRow), iter->m_iNumerator, iter->m_iDenominator ) );
|
||||
vector<TimeSignatureSegment>::const_iterator iter2 = iter;
|
||||
iter2++;
|
||||
if( iter2 != out.m_Timing.m_vTimeSignatureSegments.end() )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
ASSERT( !out.m_Timing.m_TickcountSegments.empty() );
|
||||
f.Write( "#TICKCOUNTS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_TickcountSegments.size(); i++ )
|
||||
{
|
||||
const TickcountSegment &ts = out.m_Timing.m_TickcountSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%d", NoteRowToBeat(ts.m_iStartRow), ts.m_iTicks ) );
|
||||
if( i != out.m_Timing.m_TickcountSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
|
||||
ASSERT( !out.m_Timing.m_ComboSegments.empty() );
|
||||
f.Write( "#COMBOS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_ComboSegments.size(); i++ )
|
||||
{
|
||||
const ComboSegment &cs = out.m_Timing.m_ComboSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%d", NoteRowToBeat(cs.m_iStartRow), cs.m_iCombo ) );
|
||||
if( i != out.m_Timing.m_ComboSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#LABELS:" );
|
||||
for( unsigned i=0; i<out.m_Timing.m_LabelSegments.size(); i++ )
|
||||
{
|
||||
const LabelSegment &ls = out.m_Timing.m_LabelSegments[i];
|
||||
|
||||
f.PutLine( ssprintf( "%.6f=%s", NoteRowToBeat(ls.m_iStartRow), ls.m_sLabel.c_str() ) );
|
||||
if( i != out.m_Timing.m_LabelSegments.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
WriteTimingTags( f, out.m_SongTiming, true );
|
||||
|
||||
FOREACH_BackgroundLayer( b )
|
||||
{
|
||||
@@ -258,23 +281,6 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn a vector of lines into a single line joined by newline characters.
|
||||
* @param lines the list of lines to join.
|
||||
* @return the joined lines. */
|
||||
static RString JoinLineList( vector<RString> &lines )
|
||||
{
|
||||
for( unsigned i = 0; i < lines.size(); ++i )
|
||||
TrimRight( lines[i] );
|
||||
|
||||
// Skip leading blanks.
|
||||
unsigned j = 0;
|
||||
while( j < lines.size() && lines.size() == 0 )
|
||||
++j;
|
||||
|
||||
return join( "\r\n", lines.begin()+j, lines.end() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the individual batches of NoteData.
|
||||
* @param song the Song in question.
|
||||
@@ -307,19 +313,12 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
|
||||
lines.push_back( ssprintf( "#CREDIT:%s;", SmEscape(in.GetCredit()).c_str() ) );
|
||||
|
||||
/*
|
||||
* TODO: Remove this block, transplant above code
|
||||
* below for Split Timing. -Wolfman2000 */
|
||||
lines.push_back( "#BPMS:;" );
|
||||
lines.push_back( "#STOPS:;" );
|
||||
lines.push_back( "#DELAYS:;" );
|
||||
lines.push_back( "#WARPS:;" );
|
||||
lines.push_back( "#LABELS:;" );
|
||||
lines.push_back( "#TIMESIGNATURES:;" );
|
||||
lines.push_back( "#TICKCOUNTS:;" );
|
||||
GetTimingTags( lines, in.m_Timing );
|
||||
|
||||
// For now, attacks are NOT in use for the step.
|
||||
lines.push_back( "#ATTACKS:;" );
|
||||
lines.push_back( "#COMBOS:;" );
|
||||
|
||||
lines.push_back( ssprintf( "#OFFSET:%.6f;", in.m_Timing.m_fBeat0OffsetInSeconds ) );
|
||||
|
||||
RString sNoteData;
|
||||
in.GetSMNoteData( sNoteData );
|
||||
|
||||
@@ -350,6 +349,7 @@ bool NotesWriterSSC::Write( RString sPath, const Song &out, const vector<Steps*>
|
||||
}
|
||||
|
||||
WriteGlobalTags( f, out );
|
||||
|
||||
if( bSavingCache )
|
||||
{
|
||||
f.PutLine( ssprintf( "// cache tags:" ) );
|
||||
|
||||
+44
-33
@@ -506,7 +506,7 @@ void Player::Load()
|
||||
|
||||
m_LastTapNoteScore = TNS_None;
|
||||
// The editor can start playing in the middle of the song.
|
||||
const int iNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat );
|
||||
const int iNoteRow = BeatToNoteRowNotRounded( m_pPlayerState->m_Position.m_fSongBeat );
|
||||
m_iFirstUncrossedRow = iNoteRow - 1;
|
||||
m_pJudgedRows->Reset( iNoteRow );
|
||||
|
||||
@@ -541,6 +541,8 @@ void Player::Load()
|
||||
if( GAMESTATE->m_pCurGame->m_bAllowHopos )
|
||||
NoteDataUtil::SetHopoPossibleFlags( pSong, m_NoteData );
|
||||
|
||||
m_Timing = &GAMESTATE->m_pCurSteps[pn]->m_Timing;
|
||||
|
||||
switch( GAMESTATE->m_PlayMode )
|
||||
{
|
||||
case PLAY_MODE_RAVE:
|
||||
@@ -693,7 +695,7 @@ void Player::Update( float fDeltaTime )
|
||||
return;
|
||||
}
|
||||
|
||||
const float fSongBeat = GAMESTATE->m_fSongBeat;
|
||||
const float fSongBeat = m_pPlayerState->m_Position.m_fSongBeat;
|
||||
const int iSongRow = BeatToNoteRow( fSongBeat );
|
||||
|
||||
// Optimization: Don't spend time processing the things below that won't show
|
||||
@@ -795,7 +797,7 @@ void Player::Update( float fDeltaTime )
|
||||
|
||||
// Check for a strum miss
|
||||
if( m_pPlayerState->m_fLastStrumMusicSeconds != -1 &&
|
||||
m_pPlayerState->m_fLastStrumMusicSeconds + g_fTimingWindowStrum < GAMESTATE->m_fMusicSeconds )
|
||||
m_pPlayerState->m_fLastStrumMusicSeconds + g_fTimingWindowStrum < m_pPlayerState->m_Position.m_fMusicSeconds )
|
||||
{
|
||||
DoStrumMiss();
|
||||
}
|
||||
@@ -917,12 +919,12 @@ void Player::Update( float fDeltaTime )
|
||||
/* We want to send the crossed row message exactly when we cross the row--not
|
||||
* .5 before the row. Use a very slow song (around 2 BPM) as a test case: without
|
||||
* rounding, autoplay steps early. -glenn */
|
||||
const int iRowNow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat );
|
||||
const int iRowNow = BeatToNoteRowNotRounded( m_pPlayerState->m_Position.m_fSongBeat );
|
||||
if( iRowNow >= 0 )
|
||||
{
|
||||
if( GAMESTATE->IsPlayerEnabled(m_pPlayerState) )
|
||||
{
|
||||
if(GAMESTATE->m_bDelay)
|
||||
if(m_pPlayerState->m_Position.m_bDelay)
|
||||
{
|
||||
if( !m_bDelay )
|
||||
m_bDelay = true;
|
||||
@@ -1528,7 +1530,7 @@ 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 ) )
|
||||
if( m_Timing->IsWarpAtRow( begin->first ) || m_Timing->IsFakeAtRow( begin->first ) )
|
||||
break;
|
||||
if( tn.type == TapNote::empty )
|
||||
break;
|
||||
@@ -1579,7 +1581,7 @@ int Player::GetClosestNonEmptyRowDirectional( int iStartRow, int iEndRow, bool b
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
|
||||
if( m_Timing->IsWarpAtRow( iter.Row() ) || m_Timing->IsFakeAtRow( iter.Row() ) )
|
||||
{
|
||||
++iter;
|
||||
continue;
|
||||
@@ -1650,7 +1652,7 @@ void Player::Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
|
||||
}
|
||||
|
||||
// Handle hammer-ons and pull-offs
|
||||
const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago();
|
||||
const float fPositionSeconds = m_pPlayerState->m_Position.m_fMusicSeconds - tm.Ago();
|
||||
int iHopoCol = -1;
|
||||
bool bDoHopo =
|
||||
m_pPlayerState->m_fLastHopoNoteMusicSeconds != -1 &&
|
||||
@@ -1703,7 +1705,7 @@ void Player::Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
|
||||
// Check if this fret breaks all active holds.
|
||||
if( !bRelease )
|
||||
{
|
||||
const float fSongBeat = GAMESTATE->m_fSongBeat;
|
||||
const float fSongBeat = m_pPlayerState->m_Position.m_fSongBeat;
|
||||
const int iSongRow = BeatToNoteRow( fSongBeat );
|
||||
|
||||
int iMaxHoldCol = -1;
|
||||
@@ -1745,7 +1747,7 @@ void Player::Strum( int col, int row, const RageTimer &tm, bool bHeld, bool bRel
|
||||
DoStrumMiss();
|
||||
}
|
||||
|
||||
m_pPlayerState->m_fLastStrumMusicSeconds = GAMESTATE->m_fMusicSeconds;
|
||||
m_pPlayerState->m_fLastStrumMusicSeconds = m_pPlayerState->m_Position.m_fMusicSeconds;
|
||||
|
||||
StepStrumHopo( col, row, tm, bHeld, bRelease, ButtonType_StrumFretsChanged );
|
||||
}
|
||||
@@ -1794,7 +1796,7 @@ void Player::ScoreAllActiveHoldsLetGo()
|
||||
{
|
||||
if( PENALIZE_TAP_SCORE_NONE )
|
||||
{
|
||||
const float fSongBeat = GAMESTATE->m_fSongBeat;
|
||||
const float fSongBeat = m_pPlayerState->m_Position.m_fSongBeat;
|
||||
const int iSongRow = BeatToNoteRow( fSongBeat );
|
||||
|
||||
// Score all active holds to NotHeld
|
||||
@@ -1857,8 +1859,8 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
|
||||
// Do everything that depends on a RageTimer here;
|
||||
// set your breakpoints somewhere after this block.
|
||||
const float fLastBeatUpdate = GAMESTATE->m_LastBeatUpdate.Ago();
|
||||
const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago();
|
||||
const float fLastBeatUpdate = m_pPlayerState->m_Position.m_LastBeatUpdate.Ago();
|
||||
const float fPositionSeconds = m_pPlayerState->m_Position.m_fMusicSeconds - tm.Ago();
|
||||
const float fTimeSinceStep = tm.Ago();
|
||||
|
||||
switch( pbt )
|
||||
@@ -1873,7 +1875,16 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
break;
|
||||
}
|
||||
|
||||
const float fSongBeat = GAMESTATE->m_pCurSong ? GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds ) : GAMESTATE->m_fSongBeat;
|
||||
float fSongBeat = m_pPlayerState->m_Position.m_fSongBeat;
|
||||
|
||||
if( GAMESTATE->m_pCurSong )
|
||||
{
|
||||
fSongBeat = GAMESTATE->m_pCurSong->m_SongTiming.GetBeatFromElapsedTime( fPositionSeconds );
|
||||
|
||||
if( GAMESTATE->m_pCurSteps[m_pPlayerState->m_PlayerNumber] )
|
||||
fSongBeat = m_Timing->GetBeatFromElapsedTime( fPositionSeconds );
|
||||
}
|
||||
|
||||
const int iSongRow = row == -1 ? BeatToNoteRow( fSongBeat ) : row;
|
||||
|
||||
if( col != -1 && !bRelease )
|
||||
@@ -2011,8 +2022,8 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
* "jack hammers." Hmm.
|
||||
*/
|
||||
const int iStepSearchRows = max(
|
||||
BeatToNoteRow( GAMESTATE->m_pCurSong->m_Timing.GetBeatFromElapsedTime( GAMESTATE->m_fMusicSeconds + StepSearchDistance ) ) - iSongRow,
|
||||
iSongRow - BeatToNoteRow( GAMESTATE->m_pCurSong->m_Timing.GetBeatFromElapsedTime( GAMESTATE->m_fMusicSeconds - StepSearchDistance ) )
|
||||
BeatToNoteRow( m_Timing->GetBeatFromElapsedTime( m_pPlayerState->m_Position.m_fMusicSeconds + StepSearchDistance ) ) - iSongRow,
|
||||
iSongRow - BeatToNoteRow( m_Timing->GetBeatFromElapsedTime( m_pPlayerState->m_Position.m_fMusicSeconds - StepSearchDistance ) )
|
||||
) + ROWS_PER_BEAT;
|
||||
int iRowOfOverlappingNoteOrRow = row;
|
||||
if( row == -1 )
|
||||
@@ -2039,7 +2050,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
float fNoteOffset = 0.0f;
|
||||
// we need this later if we are autosyncing
|
||||
const float fStepBeat = NoteRowToBeat( iRowOfOverlappingNoteOrRow );
|
||||
const float fStepSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat(fStepBeat);
|
||||
const float fStepSeconds = m_Timing->GetElapsedTimeFromBeat(fStepBeat);
|
||||
|
||||
if( row == -1 )
|
||||
{
|
||||
@@ -2048,7 +2059,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
|
||||
/* GAMESTATE->m_fMusicSeconds is the music time as of GAMESTATE->m_LastBeatUpdate. Figure
|
||||
* out what the music time is as of now. */
|
||||
const float fCurrentMusicSeconds = GAMESTATE->m_fMusicSeconds + (fLastBeatUpdate*GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate);
|
||||
const float fCurrentMusicSeconds = m_pPlayerState->m_Position.m_fMusicSeconds + (fLastBeatUpdate*GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate);
|
||||
|
||||
// ... which means it happened at this point in the music:
|
||||
const float fMusicSeconds = fCurrentMusicSeconds - fTimeSinceStep * GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
|
||||
@@ -2089,7 +2100,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
// Stepped too close to mine?
|
||||
if( !bRelease && ( REQUIRE_STEP_ON_MINES == !bHeld ) &&
|
||||
fSecondsFromExact <= GetWindowSeconds(TW_Mine) &&
|
||||
!GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow(iSongRow) )
|
||||
!m_Timing->IsWarpAtRow(iSongRow) && !m_Timing->IsFakeAtRow(iSongRow))
|
||||
score = TNS_HitMine;
|
||||
break;
|
||||
|
||||
@@ -2557,14 +2568,14 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
{
|
||||
//LOG->Trace( "Steps::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat );
|
||||
int iMissIfOlderThanThisRow;
|
||||
const float fEarliestTime = GAMESTATE->m_fMusicSeconds - fMissIfOlderThanSeconds;
|
||||
const float fEarliestTime = m_pPlayerState->m_Position.m_fMusicSeconds - fMissIfOlderThanSeconds;
|
||||
{
|
||||
bool bFreeze, bDelay;
|
||||
float fMissIfOlderThanThisBeat;
|
||||
float fThrowAway;
|
||||
int iWarpBeginRow;
|
||||
float fWarpLength;
|
||||
GAMESTATE->m_pCurSong->m_Timing.GetBeatAndBPSFromElapsedTime( fEarliestTime, fMissIfOlderThanThisBeat, fThrowAway, bFreeze, bDelay, iWarpBeginRow, fWarpLength );
|
||||
m_Timing->GetBeatAndBPSFromElapsedTime( fEarliestTime, fMissIfOlderThanThisBeat, fThrowAway, bFreeze, bDelay, iWarpBeginRow, fWarpLength );
|
||||
|
||||
iMissIfOlderThanThisRow = BeatToNoteRow( fMissIfOlderThanThisBeat );
|
||||
if( bFreeze || bDelay )
|
||||
@@ -2586,8 +2597,8 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
if( !NeedsTapJudging(tn) )
|
||||
continue;
|
||||
|
||||
// Ignore all notes that are skipped via WARPS.
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
|
||||
// Ignore all notes in WarpSegments or FakeSegments.
|
||||
if( m_Timing->IsWarpAtRow( iter.Row() ) || m_Timing->IsFakeAtRow( iter.Row() ) )
|
||||
continue;
|
||||
|
||||
if( tn.type == TapNote::mine )
|
||||
@@ -2610,7 +2621,7 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
|
||||
void Player::UpdateJudgedRows()
|
||||
{
|
||||
const int iEndRow = BeatToNoteRow( GAMESTATE->m_fSongBeat );
|
||||
const int iEndRow = BeatToNoteRow( m_pPlayerState->m_Position.m_fSongBeat );
|
||||
bool bAllJudged = true;
|
||||
const bool bSeparately = GAMESTATE->GetCurrentGame()->m_bCountNotesSeparately;
|
||||
|
||||
@@ -2621,8 +2632,8 @@ void Player::UpdateJudgedRows()
|
||||
{
|
||||
int iRow = iter.Row();
|
||||
|
||||
// If row is within a warp section, ignore it. -aj
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow(iRow) )
|
||||
// Do not judge arrows in WarpSegments or FakeSegments
|
||||
if( m_Timing->IsWarpAtRow(iRow) || m_Timing->IsFakeAtRow(iRow) )
|
||||
continue;
|
||||
|
||||
if( iLastSeenRow != iRow )
|
||||
@@ -2843,13 +2854,13 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
||||
int iCheckpointFrequencyRows = ROWS_PER_BEAT/2;
|
||||
if( CHECKPOINTS_USE_TICKCOUNTS )
|
||||
{
|
||||
int tickCurrent = GAMESTATE->m_pCurSong->m_Timing.GetTickcountAtRow( iLastRowCrossed );
|
||||
int tickCurrent = m_Timing->GetTickcountAtRow( iLastRowCrossed );
|
||||
// There are some charts that don't want tickcounts involved at all.
|
||||
iCheckpointFrequencyRows = (tickCurrent > 0 ? ROWS_PER_BEAT / tickCurrent : 0);
|
||||
}
|
||||
else if( CHECKPOINTS_USE_TIME_SIGNATURES )
|
||||
{
|
||||
TimeSignatureSegment tSignature = GAMESTATE->m_pCurSong->m_Timing.GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iLastRowCrossed ) );
|
||||
TimeSignatureSegment tSignature = m_Timing->GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iLastRowCrossed ) );
|
||||
|
||||
// Most songs are in 4/4 time. The frequency for checking tick counts should reflect that.
|
||||
iCheckpointFrequencyRows = ROWS_PER_BEAT * tSignature.m_iDenominator / (tSignature.m_iNumerator * 4);
|
||||
@@ -2956,8 +2967,8 @@ void Player::HandleTapRowScore( unsigned row )
|
||||
bNoCheating = false;
|
||||
#endif
|
||||
|
||||
// Warp hackery. -aj
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( row ) )
|
||||
// Do not score rows in WarpSegments or FakeSegments
|
||||
if( m_Timing->IsWarpAtRow( row ) || m_Timing->IsFakeAtRow( row ) )
|
||||
return;
|
||||
|
||||
if( GAMESTATE->m_bDemonstrationOrJukebox )
|
||||
@@ -3060,8 +3071,8 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH
|
||||
bNoCheating = false;
|
||||
#endif
|
||||
|
||||
// More warp hackery. -aj
|
||||
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iRow ) )
|
||||
// WarpSegments and FakeSegments aren't judged in any way.
|
||||
if( m_Timing->IsWarpAtRow( iRow ) || m_Timing->IsFakeAtRow( iRow ) )
|
||||
return;
|
||||
|
||||
// don't accumulate combo if AutoPlay is on.
|
||||
@@ -3257,7 +3268,7 @@ void Player::SetCombo( int iCombo, int iMisses )
|
||||
}
|
||||
else
|
||||
{
|
||||
bPastBeginning = GAMESTATE->m_fMusicSeconds > GAMESTATE->m_pCurSong->m_fMusicLengthSeconds * PERCENT_UNTIL_COLOR_COMBO;
|
||||
bPastBeginning = m_pPlayerState->m_Position.m_fMusicSeconds > GAMESTATE->m_pCurSong->m_fMusicLengthSeconds * PERCENT_UNTIL_COLOR_COMBO;
|
||||
}
|
||||
|
||||
if( m_bSendJudgmentAndComboMessages )
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "ScreenMessage.h"
|
||||
#include "ThemeMetric.h"
|
||||
#include "InputEventPlus.h"
|
||||
#include "TimingData.h"
|
||||
|
||||
class ScoreDisplay;
|
||||
class LifeMeter;
|
||||
@@ -147,6 +148,7 @@ protected:
|
||||
PlayerState *m_pPlayerState;
|
||||
/** @brief The player's present stage stats. */
|
||||
PlayerStageStats *m_pPlayerStageStats;
|
||||
TimingData *m_Timing;
|
||||
float m_fNoteFieldHeight;
|
||||
|
||||
bool m_bPaused;
|
||||
|
||||
@@ -680,7 +680,7 @@ bool PlayerOptions::operator==( const PlayerOptions &other ) const
|
||||
|
||||
bool PlayerOptions::IsEasierForSongAndSteps( Song* pSong, Steps* pSteps, PlayerNumber pn ) const
|
||||
{
|
||||
if( m_fTimeSpacing && pSong->HasSignificantBpmChangesOrStops() )
|
||||
if( m_fTimeSpacing && pSteps->HasSignificantTimingChanges() )
|
||||
return true;
|
||||
const RadarValues &rv = pSteps->GetRadarValues( pn );
|
||||
if( m_bTransforms[TRANSFORM_NOHOLDS] && rv[RadarCategory_Holds]>0 )
|
||||
|
||||
+2
-1
@@ -35,8 +35,9 @@ public:
|
||||
m_fPassmark(0), m_SpeedfPassmark(1.0f),
|
||||
m_fRandomSpeed(0), m_SpeedfRandomSpeed(1.0f),
|
||||
m_bMuteOnError(false), m_FailType(FAIL_IMMEDIATE),
|
||||
m_ScoreDisplay(SCORING_ADD), m_sNoteSkin("")
|
||||
m_ScoreDisplay(SCORING_ADD)
|
||||
{
|
||||
m_sNoteSkin = "";
|
||||
ZERO( m_fAccels ); ONE( m_SpeedfAccels );
|
||||
ZERO( m_fEffects ); ONE( m_SpeedfEffects );
|
||||
ZERO( m_fAppearances ); ONE( m_SpeedfAppearances );
|
||||
|
||||
+9
-3
@@ -69,8 +69,8 @@ void PlayerState::Update( float fDelta )
|
||||
|
||||
bool bCurrentlyEnabled =
|
||||
attack.bGlobal ||
|
||||
( attack.fStartSecond < GAMESTATE->m_fMusicSeconds &&
|
||||
GAMESTATE->m_fMusicSeconds < attack.fStartSecond+attack.fSecsRemaining );
|
||||
( attack.fStartSecond < m_Position.m_fMusicSeconds &&
|
||||
m_Position.m_fMusicSeconds < attack.fStartSecond+attack.fSecsRemaining );
|
||||
|
||||
if( m_ActiveAttacks[s].bOn == bCurrentlyEnabled )
|
||||
continue; // OK
|
||||
@@ -116,7 +116,7 @@ void PlayerState::LaunchAttack( const Attack& a )
|
||||
* so Player::Update knows to apply attack transforms correctly. (yuck) */
|
||||
m_ModsToApply.push_back( attack );
|
||||
if( attack.fStartSecond == -1 )
|
||||
attack.fStartSecond = GAMESTATE->m_fMusicSeconds;
|
||||
attack.fStartSecond = m_Position.m_fMusicSeconds;
|
||||
m_ActiveAttacks.push_back( attack );
|
||||
|
||||
RebuildPlayerOptionsFromActiveAttacks();
|
||||
@@ -197,6 +197,11 @@ class LunaPlayerState: public Luna<PlayerState>
|
||||
{
|
||||
public:
|
||||
DEFINE_METHOD( GetPlayerNumber, m_PlayerNumber );
|
||||
static int GetSongPosition( T* p, lua_State *L )
|
||||
{
|
||||
p->m_Position.PushSelf(L);
|
||||
return 1;
|
||||
}
|
||||
DEFINE_METHOD( GetMultiPlayerNumber, m_mp );
|
||||
DEFINE_METHOD( GetPlayerController, m_PlayerController );
|
||||
static int SetPlayerOptions( T* p, lua_State *L )
|
||||
@@ -246,6 +251,7 @@ public:
|
||||
ADD_METHOD( GetPlayerOptionsArray );
|
||||
ADD_METHOD( GetPlayerOptionsString );
|
||||
ADD_METHOD( GetCurrentPlayerOptions );
|
||||
ADD_METHOD( GetSongPosition );
|
||||
ADD_METHOD( GetHealthState );
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#ifndef PlayerState_H
|
||||
#define PlayerState_H
|
||||
|
||||
#include "SongPosition.h"
|
||||
#include "Attack.h"
|
||||
#include "ModsGroup.h"
|
||||
#include "PlayerNumber.h"
|
||||
@@ -38,6 +39,9 @@ public:
|
||||
*/
|
||||
MultiPlayer m_mp;
|
||||
|
||||
// Music statistics:
|
||||
SongPosition m_Position;
|
||||
|
||||
/**
|
||||
* @brief Change the PlayerOptions to their default.
|
||||
* @param l the level of mods to reset.
|
||||
|
||||
@@ -443,7 +443,7 @@ void ScoreKeeperNormal::HandleComboInternal( int iNumHitContinueCombo, int iNumH
|
||||
|
||||
if( iNumBreakCombo == 0 )
|
||||
{
|
||||
TimingData td = GAMESTATE->m_pCurSong->m_Timing;
|
||||
TimingData td = GAMESTATE->m_pCurSteps[m_pPlayerState->m_PlayerNumber]->m_Timing;
|
||||
int multiplier = ( iRow == -1 ? 1 : td.GetComboSegmentAtRow( iRow ).m_iCombo );
|
||||
m_pPlayerStageStats->m_iCurCombo += iNumHitContinueCombo * multiplier;
|
||||
}
|
||||
@@ -463,7 +463,7 @@ void ScoreKeeperNormal::HandleRowComboInternal( TapNoteScore tns, int iNumTapsIn
|
||||
if ( tns >= m_MinScoreToContinueCombo )
|
||||
{
|
||||
m_pPlayerStageStats->m_iCurMissCombo = 0;
|
||||
TimingData td = GAMESTATE->m_pCurSong->m_Timing;
|
||||
TimingData td = GAMESTATE->m_pCurSteps[m_pPlayerState->m_PlayerNumber]->m_Timing;
|
||||
int multiplier = ( iRow == -1 ? 1 : td.GetComboSegmentAtRow( iRow ).m_iCombo );
|
||||
m_pPlayerStageStats->m_iCurCombo += iNumTapsInRow * multiplier;
|
||||
}
|
||||
|
||||
+507
-289
File diff suppressed because it is too large
Load Diff
+43
-5
@@ -86,6 +86,7 @@ enum EditButton
|
||||
EDIT_BUTTON_SNAP_PREV,
|
||||
|
||||
EDIT_BUTTON_OPEN_EDIT_MENU,
|
||||
EDIT_BUTTON_OPEN_TIMING_MENU,
|
||||
EDIT_BUTTON_OPEN_AREA_MENU,
|
||||
EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU,
|
||||
EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU,
|
||||
@@ -137,6 +138,8 @@ enum EditButton
|
||||
EDIT_BUTTON_ADD_COURSE_MODS,
|
||||
|
||||
EDIT_BUTTON_SWITCH_PLAYERS, /**< Allow entering notes for a different Player. */
|
||||
|
||||
EDIT_BUTTON_SWITCH_TIMINGS, /**< Allow switching between Song and Step TimingData. */
|
||||
|
||||
NUM_EditButton, // leave this at the end
|
||||
EditButton_Invalid
|
||||
@@ -231,6 +234,9 @@ protected:
|
||||
float GetMaximumBeatForMoving() const; // don't allow Down key to go past this beat.
|
||||
|
||||
void DoHelp();
|
||||
|
||||
/** @brief Display the TimingData menu for editing song and step timing. */
|
||||
void DisplayTimingMenu();
|
||||
|
||||
EditState m_EditState;
|
||||
|
||||
@@ -275,14 +281,22 @@ protected:
|
||||
/** @brief Has the NoteData been changed such that a user should be prompted to save? */
|
||||
bool m_bDirty;
|
||||
|
||||
/** @brief The sound that is played when a note is added. */
|
||||
RageSound m_soundAddNote;
|
||||
/** @brief The sound that is played when a note is removed. */
|
||||
RageSound m_soundRemoveNote;
|
||||
RageSound m_soundChangeLine;
|
||||
RageSound m_soundChangeSnap;
|
||||
RageSound m_soundMarker;
|
||||
RageSound m_soundValueIncrease;
|
||||
RageSound m_soundValueDecrease;
|
||||
/** @brief The sound that is played when switching players for Routine. */
|
||||
RageSound m_soundSwitchPlayer;
|
||||
/** @brief The sound that is played when switching song/step timing. */
|
||||
RageSound m_soundSwitchTiming;
|
||||
/** @brief The sound that is played when switching to a different chart. */
|
||||
RageSound m_soundSwitchSteps;
|
||||
/** @brief The sound that is played when the chart is saved. */
|
||||
RageSound m_soundSave;
|
||||
|
||||
// used for reverting
|
||||
@@ -459,7 +473,6 @@ public:
|
||||
main_title_transliteration,
|
||||
sub_title_transliteration,
|
||||
artist_transliteration,
|
||||
beat_0_offset,
|
||||
last_beat_hint,
|
||||
preview_start,
|
||||
preview_length,
|
||||
@@ -472,16 +485,21 @@ public:
|
||||
|
||||
enum TimingDataInformationChoice
|
||||
{
|
||||
beat_0_offset,
|
||||
bpm,
|
||||
stop,
|
||||
delay,
|
||||
// time_signature,
|
||||
time_signature_numerator,
|
||||
time_signature_denominator,
|
||||
time_signature,
|
||||
label,
|
||||
tickcount,
|
||||
combo,
|
||||
label,
|
||||
warp,
|
||||
// speed,
|
||||
speed_percent,
|
||||
speed_wait,
|
||||
speed_mode,
|
||||
fake,
|
||||
erase_step_timing,
|
||||
NUM_TIMING_DATA_INFORMATION_CHOICES
|
||||
};
|
||||
|
||||
@@ -514,7 +532,19 @@ public:
|
||||
delete_change,
|
||||
NUM_BGCHANGE_CHOICES
|
||||
};
|
||||
|
||||
enum SpeedSegmentModes
|
||||
{
|
||||
SSMODE_Beats,
|
||||
SSMODE_Seconds
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Take care of any background changes that the user wants.
|
||||
*
|
||||
* It is important that this is only called in Song Timing mode.
|
||||
* @param c the Background Change style requested.
|
||||
* @param iAnswers the other settings involving the change. */
|
||||
void HandleBGChangeChoice( BGChangeChoice c, const vector<int> &iAnswers );
|
||||
|
||||
enum CourseAttackChoice
|
||||
@@ -544,6 +574,14 @@ public:
|
||||
|
||||
void MakeFilteredMenuDef( const MenuDef* pDef, MenuDef &menu );
|
||||
void EditMiniMenu( const MenuDef* pDef, ScreenMessage SM_SendOnOK = SM_None, ScreenMessage SM_SendOnCancel = SM_None );
|
||||
private:
|
||||
/**
|
||||
* @brief Retrieve the appropriate TimingData based on GAMESTATE.
|
||||
* @return the proper TimingData. */
|
||||
TimingData & GetAppropriateTiming() const;
|
||||
void SetBeat(float fBeat);
|
||||
float GetBeat();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -236,7 +236,7 @@ void ScreenEditMenu::MenuStart( const InputEventPlus &input )
|
||||
case EditMenuAction_Create:
|
||||
ASSERT( !pSteps );
|
||||
{
|
||||
pSteps = new Steps;
|
||||
pSteps = pSong->CreateSteps();
|
||||
|
||||
switch( m_Selector.EDIT_MODE )
|
||||
{
|
||||
|
||||
+17
-17
@@ -1325,7 +1325,7 @@ void ScreenGameplay::StartPlayingSong( float fMinTimeToNotes, float fMinTimeToMu
|
||||
|
||||
{
|
||||
const float fFirstBeat = GAMESTATE->m_pCurSong->m_fFirstBeat;
|
||||
const float fFirstSecond = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat( fFirstBeat );
|
||||
const float fFirstSecond = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( fFirstBeat );
|
||||
float fStartDelay = fMinTimeToNotes - fFirstSecond;
|
||||
fStartDelay = max( fStartDelay, fMinTimeToMusic );
|
||||
p.m_StartSecond = -fStartDelay;
|
||||
@@ -1347,10 +1347,10 @@ void ScreenGameplay::StartPlayingSong( float fMinTimeToNotes, float fMinTimeToMu
|
||||
m_pSoundMusic->Pause( true );
|
||||
|
||||
/* Make sure GAMESTATE->m_fMusicSeconds is set up. */
|
||||
GAMESTATE->m_fMusicSeconds = -5000;
|
||||
GAMESTATE->m_Position.m_fMusicSeconds = -5000;
|
||||
UpdateSongPosition(0);
|
||||
|
||||
ASSERT( GAMESTATE->m_fMusicSeconds > -4000 ); /* make sure the "fake timer" code doesn't trigger */
|
||||
ASSERT( GAMESTATE->m_Position.m_fMusicSeconds > -4000 ); /* make sure the "fake timer" code doesn't trigger */
|
||||
}
|
||||
|
||||
|
||||
@@ -1405,7 +1405,7 @@ void ScreenGameplay::PlayAnnouncer( RString type, float fSeconds )
|
||||
if( m_DancingState != STATE_DANCING )
|
||||
return;
|
||||
if( GAMESTATE->m_pCurSong == NULL || // this will be true on ScreenDemonstration sometimes
|
||||
GAMESTATE->m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat )
|
||||
GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat )
|
||||
return;
|
||||
|
||||
if( m_fTimeSinceLastDancingComment < fSeconds )
|
||||
@@ -1423,7 +1423,7 @@ void ScreenGameplay::UpdateSongPosition( float fDeltaTime )
|
||||
RageTimer tm;
|
||||
const float fSeconds = m_pSoundMusic->GetPositionSeconds( NULL, &tm );
|
||||
const float fAdjust = SOUND->GetFrameTimingAdjustment( fDeltaTime );
|
||||
GAMESTATE->UpdateSongPosition( fSeconds+fAdjust, GAMESTATE->m_pCurSong->m_Timing, tm+fAdjust );
|
||||
GAMESTATE->UpdateSongPosition( fSeconds+fAdjust, GAMESTATE->m_pCurSong->m_SongTiming, tm+fAdjust, true );
|
||||
}
|
||||
|
||||
void ScreenGameplay::BeginScreen()
|
||||
@@ -1480,7 +1480,7 @@ bool ScreenGameplay::AllAreFailing()
|
||||
|
||||
void ScreenGameplay::GetMusicEndTiming( float &fSecondsToStartFadingOutMusic, float &fSecondsToStartTransitioningOut )
|
||||
{
|
||||
float fLastStepSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat );
|
||||
float fLastStepSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat );
|
||||
fLastStepSeconds += Player::GetMaxStepDistanceSeconds();
|
||||
|
||||
float fTransitionLength;
|
||||
@@ -1687,7 +1687,7 @@ void ScreenGameplay::Update( float fDeltaTime )
|
||||
|
||||
// update fGameplaySeconds
|
||||
STATSMAN->m_CurStageStats.m_fGameplaySeconds += fUnscaledDeltaTime;
|
||||
if( GAMESTATE->m_fSongBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat && GAMESTATE->m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat )
|
||||
if( GAMESTATE->m_Position.m_fSongBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat && GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat )
|
||||
{
|
||||
STATSMAN->m_CurStageStats.m_fStepsSeconds += fUnscaledDeltaTime;
|
||||
|
||||
@@ -1707,7 +1707,7 @@ void ScreenGameplay::Update( float fDeltaTime )
|
||||
if( bAllReallyFailed )
|
||||
fSecondsToStartTransitioningOut += BEGIN_FAILED_DELAY;
|
||||
|
||||
if( GAMESTATE->m_fMusicSeconds >= fSecondsToStartTransitioningOut && !m_NextSong.IsTransitioning() )
|
||||
if( GAMESTATE->m_Position.m_fMusicSeconds >= fSecondsToStartTransitioningOut && !m_NextSong.IsTransitioning() )
|
||||
this->PostScreenMessage( SM_NotesEnded, 0 );
|
||||
}
|
||||
|
||||
@@ -1843,8 +1843,8 @@ void ScreenGameplay::Update( float fDeltaTime )
|
||||
|
||||
float ScreenGameplay::GetHasteRate()
|
||||
{
|
||||
if( GAMESTATE->m_fMusicSeconds < GAMESTATE->m_fLastHasteUpdateMusicSeconds || // new song
|
||||
GAMESTATE->m_fMusicSeconds > GAMESTATE->m_fLastHasteUpdateMusicSeconds + 4 )
|
||||
if( GAMESTATE->m_Position.m_fMusicSeconds < GAMESTATE->m_fLastHasteUpdateMusicSeconds || // new song
|
||||
GAMESTATE->m_Position.m_fMusicSeconds > GAMESTATE->m_fLastHasteUpdateMusicSeconds + 4 )
|
||||
{
|
||||
bool bAnyPlayerHitAllNotes = false;
|
||||
FOREACH_EnabledPlayerInfo( m_vPlayerInfo, pi )
|
||||
@@ -1865,7 +1865,7 @@ float ScreenGameplay::GetHasteRate()
|
||||
GAMESTATE->m_fHasteRate += 0.1f;
|
||||
CLAMP( GAMESTATE->m_fHasteRate, -1.0f, +1.0f );
|
||||
|
||||
GAMESTATE->m_fLastHasteUpdateMusicSeconds = GAMESTATE->m_fMusicSeconds;
|
||||
GAMESTATE->m_fLastHasteUpdateMusicSeconds = GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
}
|
||||
|
||||
/* If the life meter is less than half full, push the haste rate down to let
|
||||
@@ -1914,7 +1914,7 @@ void ScreenGameplay::UpdateLights()
|
||||
ZERO( bBlinkGameButton );
|
||||
bool bCrossedABeat = false;
|
||||
{
|
||||
const float fSongBeat = GAMESTATE->m_fLightSongBeat;
|
||||
const float fSongBeat = GAMESTATE->m_Position.m_fLightSongBeat;
|
||||
const int iSongRow = BeatToNoteRowNotRounded( fSongBeat );
|
||||
|
||||
static int iRowLastCrossed = 0;
|
||||
@@ -1968,7 +1968,7 @@ void ScreenGameplay::UpdateLights()
|
||||
}
|
||||
|
||||
// Before the first beat of the song, all cabinet lights solid on (except for menu buttons).
|
||||
bool bOverrideCabinetBlink = (GAMESTATE->m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat);
|
||||
bool bOverrideCabinetBlink = (GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat);
|
||||
FOREACH_CabinetLight( cl )
|
||||
bBlinkCabinetLight[cl] |= bOverrideCabinetBlink;
|
||||
|
||||
@@ -1994,8 +1994,8 @@ void ScreenGameplay::SendCrossedMessages()
|
||||
{
|
||||
static int iRowLastCrossed = 0;
|
||||
|
||||
float fPositionSeconds = GAMESTATE->m_fMusicSeconds;
|
||||
float fSongBeat = GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds );
|
||||
float fPositionSeconds = GAMESTATE->m_Position.m_fMusicSeconds;
|
||||
float fSongBeat = GAMESTATE->m_pCurSong->m_SongTiming.GetBeatFromElapsedTime( fPositionSeconds );
|
||||
|
||||
int iRowNow = BeatToNoteRowNotRounded( fSongBeat );
|
||||
iRowNow = max( 0, iRowNow );
|
||||
@@ -2032,8 +2032,8 @@ void ScreenGameplay::SendCrossedMessages()
|
||||
{
|
||||
float fNoteWillCrossInSeconds = MESSAGE_SPACING_SECONDS * i;
|
||||
|
||||
float fPositionSeconds = GAMESTATE->m_fMusicSeconds + fNoteWillCrossInSeconds;
|
||||
float fSongBeat = GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds );
|
||||
float fPositionSeconds = GAMESTATE->m_Position.m_fMusicSeconds + fNoteWillCrossInSeconds;
|
||||
float fSongBeat = GAMESTATE->m_pCurSong->m_SongTiming.GetBeatFromElapsedTime( fPositionSeconds );
|
||||
|
||||
int iRowNow = BeatToNoteRowNotRounded( fSongBeat );
|
||||
iRowNow = max( 0, iRowNow );
|
||||
|
||||
@@ -208,7 +208,7 @@ void ScreenHowToPlay::Step()
|
||||
#define ST_JUMPUD (ST_UP | ST_DOWN)
|
||||
|
||||
int iStep = 0;
|
||||
const int iNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat + 0.6f );
|
||||
const int iNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_Position.m_fSongBeat + 0.6f );
|
||||
// if we want to miss from here on out, don't process steps.
|
||||
if( m_iW2s < m_iNumW2s && m_NoteData.IsThereATapAtRow( iNoteRow ) )
|
||||
{
|
||||
@@ -229,10 +229,10 @@ void ScreenHowToPlay::Step()
|
||||
m_pmCharacter->PlayAnimation( "Step-JUMPLR", 1.8f );
|
||||
|
||||
m_pmCharacter->StopTweening();
|
||||
m_pmCharacter->BeginTweening( GAMESTATE->m_fCurBPS /8, TWEEN_LINEAR );
|
||||
m_pmCharacter->BeginTweening( GAMESTATE->m_Position.m_fCurBPS /8, TWEEN_LINEAR );
|
||||
m_pmCharacter->SetRotationY( 90 );
|
||||
m_pmCharacter->BeginTweening( (1/(GAMESTATE->m_fCurBPS * 2) ) ); //sleep between jump-frames
|
||||
m_pmCharacter->BeginTweening( GAMESTATE->m_fCurBPS /6, TWEEN_LINEAR );
|
||||
m_pmCharacter->BeginTweening( (1/(GAMESTATE->m_Position.m_fCurBPS * 2) ) ); //sleep between jump-frames
|
||||
m_pmCharacter->BeginTweening( GAMESTATE->m_Position.m_fCurBPS /6, TWEEN_LINEAR );
|
||||
m_pmCharacter->SetRotationY( 0 );
|
||||
break;
|
||||
}
|
||||
@@ -243,11 +243,11 @@ void ScreenHowToPlay::Update( float fDelta )
|
||||
{
|
||||
if( GAMESTATE->m_pCurSong != NULL )
|
||||
{
|
||||
GAMESTATE->UpdateSongPosition( m_fFakeSecondsIntoSong, GAMESTATE->m_pCurSong->m_Timing );
|
||||
GAMESTATE->UpdateSongPosition( m_fFakeSecondsIntoSong, GAMESTATE->m_pCurSong->m_SongTiming );
|
||||
m_fFakeSecondsIntoSong += fDelta;
|
||||
|
||||
static int iLastNoteRowCounted = 0;
|
||||
int iCurNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat );
|
||||
int iCurNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_Position.m_fSongBeat );
|
||||
|
||||
if( iCurNoteRow != iLastNoteRowCounted &&m_NoteData.IsThereATapAtRow(iCurNoteRow) )
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ void ScreenNameEntry::ScrollingText::Init( const RString &sName, const vector<fl
|
||||
|
||||
void ScreenNameEntry::ScrollingText::DrawPrimitives()
|
||||
{
|
||||
const float fFakeBeat = GAMESTATE->m_fSongBeat;
|
||||
const float fFakeBeat = GAMESTATE->m_Position.m_fSongBeat;
|
||||
const size_t iClosestIndex = lrintf( fFakeBeat ) % CHARS_CHOICES.size();
|
||||
const float fClosestYOffset = GetClosestCharYOffset( fFakeBeat );
|
||||
|
||||
@@ -325,7 +325,7 @@ void ScreenNameEntry::Update( float fDelta )
|
||||
SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo("name entry") );
|
||||
|
||||
m_fFakeBeat += fDelta * FAKE_BEATS_PER_SEC;
|
||||
GAMESTATE->m_fSongBeat = m_fFakeBeat;
|
||||
GAMESTATE->m_Position.m_fSongBeat = m_fFakeBeat;
|
||||
|
||||
ScreenWithMenuElements::Update(fDelta);
|
||||
}
|
||||
|
||||
@@ -1764,7 +1764,7 @@ void ScreenSelectMusic::AfterMusicChange()
|
||||
case SampleMusicPreviewMode_LastSong: // fall through
|
||||
// play the sample music
|
||||
m_sSampleMusicToPlay = pSong->GetMusicPath();
|
||||
m_pSampleMusicTimingData = &pSong->m_Timing;
|
||||
m_pSampleMusicTimingData = &pSong->m_SongTiming;
|
||||
m_fSampleStartSeconds = pSong->m_fMusicSampleStartSeconds;
|
||||
m_fSampleLengthSeconds = pSong->m_fMusicSampleLengthSeconds;
|
||||
break;
|
||||
|
||||
@@ -221,7 +221,7 @@ bool ScreenSyncOverlay::OverlayInput( const InputEventPlus &input )
|
||||
}
|
||||
if( GAMESTATE->m_pCurSong != NULL )
|
||||
{
|
||||
BPMSegment& seg = GAMESTATE->m_pCurSong->GetBPMSegmentAtBeat( GAMESTATE->m_fSongBeat );
|
||||
BPMSegment& seg = GAMESTATE->m_pCurSong->m_SongTiming.GetBPMSegmentAtBeat( GAMESTATE->m_Position.m_fSongBeat );
|
||||
seg.m_fBPS += fDelta;
|
||||
}
|
||||
}
|
||||
@@ -253,7 +253,7 @@ bool ScreenSyncOverlay::OverlayInput( const InputEventPlus &input )
|
||||
|
||||
case ChangeSongOffset:
|
||||
if( GAMESTATE->m_pCurSong != NULL )
|
||||
GAMESTATE->m_pCurSong->m_Timing.m_fBeat0OffsetInSeconds += fDelta;
|
||||
GAMESTATE->m_pCurSong->m_SongTiming.m_fBeat0OffsetInSeconds += fDelta;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+37
-54
@@ -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 = 171;
|
||||
const int FILE_CACHE_VERSION = 174;
|
||||
|
||||
/** @brief How long does a song sample last by default? */
|
||||
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
|
||||
@@ -145,6 +145,18 @@ void Song::AddLyricSegment( LyricSegment seg )
|
||||
m_LyricSegments.push_back( seg );
|
||||
}
|
||||
|
||||
Steps *Song::CreateSteps()
|
||||
{
|
||||
Steps *pSteps = new Steps;
|
||||
InitSteps( pSteps );
|
||||
return pSteps;
|
||||
}
|
||||
|
||||
void Song::InitSteps(Steps *pSteps)
|
||||
{
|
||||
pSteps->m_Timing = m_SongTiming;
|
||||
}
|
||||
|
||||
void Song::GetDisplayBpms( DisplayBpms &AddTo ) const
|
||||
{
|
||||
if( m_DisplayBPMType == DISPLAY_BPM_SPECIFIED )
|
||||
@@ -155,7 +167,7 @@ void Song::GetDisplayBpms( DisplayBpms &AddTo ) const
|
||||
else
|
||||
{
|
||||
float fMinBPM, fMaxBPM;
|
||||
m_Timing.GetActualBPM( fMinBPM, fMaxBPM );
|
||||
m_SongTiming.GetActualBPM( fMinBPM, fMaxBPM );
|
||||
AddTo.Add( fMinBPM );
|
||||
AddTo.Add( fMaxBPM );
|
||||
}
|
||||
@@ -460,6 +472,13 @@ void Song::TidyUpData()
|
||||
m_fMusicLengthSeconds = 0;
|
||||
}
|
||||
|
||||
m_SongTiming.TidyUpData();
|
||||
|
||||
FOREACH( Steps *, m_vpSteps, s )
|
||||
{
|
||||
(*s)->m_Timing.TidyUpData();
|
||||
}
|
||||
|
||||
/* Generate these before we autogen notes, so the new notes can inherit
|
||||
* their source's values. */
|
||||
ReCalculateRadarValuesAndLastBeat();
|
||||
@@ -476,29 +495,18 @@ void Song::TidyUpData()
|
||||
m_sArtist = "Unknown artist";
|
||||
TranslateTitles();
|
||||
|
||||
if( m_Timing.m_BPMSegments.empty() )
|
||||
{
|
||||
LOG->UserLog( "Song file", m_sSongDir + m_sSongFileName, "has no BPM segments, default provided." );
|
||||
|
||||
m_Timing.AddBPMSegment( BPMSegment(0, 60) );
|
||||
}
|
||||
|
||||
// Make sure the first BPM segment starts at beat 0.
|
||||
if( m_Timing.m_BPMSegments[0].m_iStartRow != 0 )
|
||||
m_Timing.m_BPMSegments[0].m_iStartRow = 0;
|
||||
|
||||
|
||||
if( m_fMusicSampleStartSeconds == -1 ||
|
||||
m_fMusicSampleStartSeconds == 0 ||
|
||||
m_fMusicSampleStartSeconds+m_fMusicSampleLengthSeconds > this->m_fMusicLengthSeconds )
|
||||
{
|
||||
m_fMusicSampleStartSeconds = this->GetElapsedTimeFromBeat( 100 );
|
||||
const TimingData &timing = this->m_SongTiming;
|
||||
m_fMusicSampleStartSeconds = timing.GetElapsedTimeFromBeat( 100 );
|
||||
|
||||
if( m_fMusicSampleStartSeconds+m_fMusicSampleLengthSeconds > this->m_fMusicLengthSeconds )
|
||||
{
|
||||
int iBeat = lrintf( m_fLastBeat/2 );
|
||||
iBeat -= iBeat%4;
|
||||
m_fMusicSampleStartSeconds = this->GetElapsedTimeFromBeat( (float)iBeat );
|
||||
m_fMusicSampleStartSeconds = timing.GetElapsedTimeFromBeat( (float)iBeat );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,38 +785,6 @@ void Song::TidyUpData()
|
||||
m_sSongFileName += ".ssc";
|
||||
} while(0);
|
||||
}
|
||||
|
||||
// If no time signature specified, assume 4/4 time for the whole song.
|
||||
if( m_Timing.m_vTimeSignatureSegments.empty() )
|
||||
{
|
||||
TimeSignatureSegment seg(0, 4, 4);
|
||||
m_Timing.m_vTimeSignatureSegments.push_back( seg );
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, if no tickcount signature is specified, assume 2 ticks
|
||||
* per beat for the entire song. The default of 2 is chosen more
|
||||
* for compatibility with the Pump Pro series than anything else.
|
||||
*/
|
||||
if( m_Timing.m_TickcountSegments.empty() )
|
||||
{
|
||||
TickcountSegment seg(0, 2);
|
||||
m_Timing.m_TickcountSegments.push_back( seg );
|
||||
}
|
||||
|
||||
// Have a default combo segment of one just in case.
|
||||
if( m_Timing.m_ComboSegments.empty() )
|
||||
{
|
||||
ComboSegment seg(0, 1);
|
||||
m_Timing.m_ComboSegments.push_back( seg );
|
||||
}
|
||||
|
||||
// Have a default label segment just in case.
|
||||
if( m_Timing.m_LabelSegments.empty() )
|
||||
{
|
||||
LabelSegment seg(0, "Song Start");
|
||||
m_Timing.m_LabelSegments.push_back( seg );
|
||||
}
|
||||
}
|
||||
|
||||
void Song::TranslateTitles()
|
||||
@@ -857,8 +833,8 @@ void Song::ReCalculateRadarValuesAndLastBeat()
|
||||
if( tempNoteData.GetLastRow() == 0 )
|
||||
continue;
|
||||
|
||||
fFirstBeat = min( fFirstBeat, tempNoteData.GetFirstBeat() );
|
||||
fLastBeat = max( fLastBeat, tempNoteData.GetLastBeat() );
|
||||
fFirstBeat = min( fFirstBeat, m_SongTiming.GetBeatFromElapsedTime(pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat())) );
|
||||
fLastBeat = max( fLastBeat, m_SongTiming.GetBeatFromElapsedTime(pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat())) );
|
||||
}
|
||||
|
||||
m_fFirstBeat = fFirstBeat;
|
||||
@@ -887,6 +863,12 @@ void Song::Save()
|
||||
|
||||
ReCalculateRadarValuesAndLastBeat();
|
||||
TranslateTitles();
|
||||
|
||||
// TODO: Figure out a better way to save to Song's timing data.
|
||||
if( m_vpSteps.size() == 1 )
|
||||
{
|
||||
m_SongTiming = m_vpSteps[0]->m_Timing;
|
||||
}
|
||||
|
||||
// Save the new files. These calls make backups on their own.
|
||||
if( !SaveToSSCFile(GetSongFilePath(), false) )
|
||||
@@ -1453,7 +1435,7 @@ bool Song::IsEditAlreadyLoaded( Steps* pSteps ) const
|
||||
|
||||
bool Song::HasSignificantBpmChangesOrStops() const
|
||||
{
|
||||
if( m_Timing.HasStops() )
|
||||
if( m_SongTiming.HasStops() )
|
||||
return true;
|
||||
|
||||
// Don't consider BPM changes that only are only for maintaining sync as
|
||||
@@ -1463,7 +1445,7 @@ bool Song::HasSignificantBpmChangesOrStops() const
|
||||
if( m_fSpecifiedBPMMin != m_fSpecifiedBPMMax )
|
||||
return true;
|
||||
}
|
||||
else if( m_Timing.HasBpmChanges() )
|
||||
else if( m_SongTiming.HasBpmChanges() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1473,7 +1455,8 @@ bool Song::HasSignificantBpmChangesOrStops() const
|
||||
|
||||
float Song::GetStepsSeconds() const
|
||||
{
|
||||
return GetElapsedTimeFromBeat( m_fLastBeat ) - GetElapsedTimeFromBeat( m_fFirstBeat );
|
||||
const TimingData &timing = this->m_SongTiming;
|
||||
return timing.GetElapsedTimeFromBeat( m_fLastBeat ) - timing.GetElapsedTimeFromBeat( m_fFirstBeat );
|
||||
}
|
||||
|
||||
bool Song::IsLong() const
|
||||
@@ -1556,7 +1539,7 @@ public:
|
||||
}
|
||||
static int GetTimingData( T* p, lua_State *L )
|
||||
{
|
||||
p->m_Timing.PushSelf(L);
|
||||
p->m_SongTiming.PushSelf(L);
|
||||
return 1;
|
||||
}
|
||||
// has functions
|
||||
|
||||
+37
-3
@@ -8,16 +8,16 @@
|
||||
#include "RageUtil_AutoPtr.h"
|
||||
#include "RageUtil_CachedObject.h"
|
||||
#include "RageTypes.h"
|
||||
#include "Steps.h"
|
||||
#include <set>
|
||||
|
||||
class Steps;
|
||||
class Style;
|
||||
class StepsID;
|
||||
struct lua_State;
|
||||
struct BackgroundChange;
|
||||
|
||||
/** @brief The version of the .ssc file format. */
|
||||
const static float STEPFILE_VERSION_NUMBER = 0.59f;
|
||||
const static float STEPFILE_VERSION_NUMBER = 0.7f;
|
||||
|
||||
/** @brief How many edits for this song can each profile have? */
|
||||
const int MAX_EDITS_PER_SONG_PER_PROFILE = 5;
|
||||
@@ -285,7 +285,10 @@ public:
|
||||
bool Matches(RString sGroup, RString sSong) const;
|
||||
|
||||
/** @brief The Song's TimingData. */
|
||||
TimingData m_Timing;
|
||||
TimingData m_SongTiming;
|
||||
|
||||
/** @brief The initial offset of a song. */
|
||||
float m_fBeat0OffsetInSeconds;
|
||||
|
||||
typedef vector<BackgroundChange> VBackgroundChange;
|
||||
private:
|
||||
@@ -317,9 +320,11 @@ public:
|
||||
* This must be sorted before gameplay. */
|
||||
vector<LyricSegment> m_LyricSegments;
|
||||
|
||||
/* [splittiming]
|
||||
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 AddBackgroundChange( BackgroundLayer blLayer, BackgroundChange seg );
|
||||
void AddForegroundChange( BackgroundChange seg );
|
||||
void AddLyricSegment( LyricSegment seg );
|
||||
@@ -327,18 +332,47 @@ public:
|
||||
void GetDisplayBpms( DisplayBpms &AddTo ) const;
|
||||
const BackgroundChange &GetBackgroundAtBeat( BackgroundLayer iLayer, float fBeat ) const;
|
||||
|
||||
/* [splittiming]
|
||||
float GetBPMAtBeat( float fBeat ) const { return m_Timing.GetBPMAtBeat( fBeat ); }
|
||||
void SetBPMAtBeat( float fBeat, float fBPM ) { m_Timing.SetBPMAtBeat( fBeat, fBPM ); }
|
||||
BPMSegment& GetBPMSegmentAtBeat( float fBeat ) { return m_Timing.GetBPMSegmentAtBeat( fBeat ); }
|
||||
*/
|
||||
|
||||
Steps *CreateSteps();
|
||||
void InitSteps(Steps *pSteps);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the beat based on the specified time.
|
||||
* @param fElapsedTime the amount of time since the Song started.
|
||||
* @return the appropriate beat. */
|
||||
/* [splittiming]
|
||||
float SongGetBeatFromElapsedTime( float fElapsedTime ) const
|
||||
{
|
||||
return m_SongTiming.GetBeatFromElapsedTime( fElapsedTime );
|
||||
}
|
||||
float StepsGetBeatFromElapsedTime( float fElapsedTime, const Steps &steps ) const
|
||||
{
|
||||
return steps.m_Timing.GetBeatFromElapsedTime( fElapsedTime );
|
||||
}
|
||||
|
||||
float SongGetElapsedTimeFromBeat( float fBeat ) const
|
||||
{
|
||||
return m_SongTiming.GetElapsedTimeFromBeat( fBeat );
|
||||
}
|
||||
float StepsGetElapsedTimeFromBeat( float fBeat, const Steps &steps ) const
|
||||
{
|
||||
return steps.m_Timing.GetElapsedTimeFromBeat( fBeat );
|
||||
}
|
||||
*/
|
||||
|
||||
/* [splittiming]
|
||||
float GetBeatFromElapsedTime( float fElapsedTime ) const
|
||||
{
|
||||
return m_Timing.GetBeatFromElapsedTime( fElapsedTime );
|
||||
}
|
||||
float GetElapsedTimeFromBeat( float fBeat ) const { return m_Timing.GetElapsedTimeFromBeat( fBeat ); }
|
||||
*/
|
||||
|
||||
bool HasSignificantBpmChangesOrStops() const;
|
||||
float GetStepsSeconds() const;
|
||||
bool IsLong() const;
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "global.h"
|
||||
#include "SongPosition.h"
|
||||
|
||||
static Preference<float> g_fVisualDelaySeconds( "VisualDelaySeconds", 0.0f );
|
||||
|
||||
void SongPosition::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp )
|
||||
{
|
||||
|
||||
if( !timestamp.IsZero() )
|
||||
m_LastBeatUpdate = timestamp;
|
||||
else
|
||||
m_LastBeatUpdate.Touch();
|
||||
|
||||
// xxx testing: only do this on monotune survivor
|
||||
/*
|
||||
if( m_pCurSong && m_pCurSong->GetDisplayFullTitle() == "monotune survivor" )
|
||||
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_fWarpDestination );
|
||||
|
||||
// "Crash reason : -243478.890625 -48695.773438"
|
||||
ASSERT_M( m_fSongBeat > -2000, ssprintf("Song beat %f at %f seconds is less than -2000!", m_fSongBeat, fPositionSeconds) );
|
||||
|
||||
m_fMusicSeconds = fPositionSeconds;
|
||||
|
||||
m_fLightSongBeat = timing.GetBeatFromElapsedTime( fPositionSeconds + g_fLightsAheadSeconds );
|
||||
|
||||
m_fSongBeatNoOffset = timing.GetBeatFromElapsedTimeNoOffset( fPositionSeconds );
|
||||
|
||||
/*
|
||||
// xxx testing: only do this on monotune survivor
|
||||
if( m_pCurSong && m_pCurSong->GetDisplayFullTitle() == "monotune survivor" )
|
||||
{
|
||||
// and only do it in the known negative bpm region. HACKITY HACK
|
||||
if(m_fSongBeat >= 445.490f && m_fSongBeat <= 453.72f)
|
||||
{
|
||||
LOG->Trace( ssprintf("fPositionSeconds = %f",fPositionSeconds) );
|
||||
LOG->Trace( ssprintf("Song beat: %f (%f seconds), BPS = %f (%f BPM)",m_fSongBeat,m_fMusicSecondsVisible,m_fCurBPS,m_fCurBPS*60.0f) );
|
||||
//LOG->Trace( ssprintf("Music seconds visible %f = fPositionSeconds %f - g_fVisualDelaySeconds %f", m_fMusicSecondsVisible,fPositionSeconds,g_fVisualDelaySeconds.Get()) );
|
||||
}
|
||||
else if(m_fSongBeat == 445.500f)
|
||||
{
|
||||
LOG->Trace( ssprintf("[beat 445.500] fPositionSeconds = %f",fPositionSeconds) );
|
||||
LOG->Trace( ssprintf("Song beat: %f (%f seconds), BPS = %f (%f BPM)",m_fSongBeat,m_fMusicSecondsVisible,m_fCurBPS,m_fCurBPS*60.0f) );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
m_fMusicSecondsVisible = fPositionSeconds - g_fVisualDelaySeconds.Get();
|
||||
float fThrowAway, fThrowAway2;
|
||||
bool bThrowAway;
|
||||
int iThrowAway;
|
||||
timing.GetBeatAndBPSFromElapsedTime( m_fMusicSecondsVisible, m_fSongBeatVisible, fThrowAway, bThrowAway, bThrowAway, iThrowAway, fThrowAway2 );
|
||||
|
||||
}
|
||||
|
||||
void SongPosition::Reset()
|
||||
{
|
||||
|
||||
m_fMusicSecondsVisible = 0;
|
||||
m_fSongBeatVisible = 0;
|
||||
|
||||
m_fMusicSeconds = 0; // MUSIC_SECONDS_INVALID;
|
||||
// todo: move me to FOREACH_EnabledPlayer( p ) after [NUM_PLAYERS]ing
|
||||
m_fSongBeat = 0;
|
||||
m_fSongBeatNoOffset = 0;
|
||||
m_fCurBPS = 10;
|
||||
//m_bStop = false;
|
||||
m_bFreeze = false;
|
||||
m_bDelay = false;
|
||||
m_iWarpBeginRow = -1; // Set to -1 because some song may want to warp to row 0. -aj
|
||||
m_fWarpDestination = -1; // Set when a warp is encountered. also see above. -aj
|
||||
|
||||
}
|
||||
|
||||
//lua start
|
||||
#include "LuaBinding.h"
|
||||
class LunaSongPosition: public Luna<SongPosition>
|
||||
{
|
||||
public:
|
||||
DEFINE_METHOD( GetMusicSecondsVisible, m_fMusicSecondsVisible );
|
||||
DEFINE_METHOD( GetSongBeatVisible, m_fSongBeatVisible );
|
||||
DEFINE_METHOD( GetMusicSeconds, m_fMusicSeconds );
|
||||
DEFINE_METHOD( GetSongBeat, m_fSongBeat );
|
||||
DEFINE_METHOD( GetSongBeatNoOffset, m_fSongBeatNoOffset );
|
||||
DEFINE_METHOD( GetCurBPS, m_fCurBPS );
|
||||
DEFINE_METHOD( GetFreeze, m_bFreeze );
|
||||
DEFINE_METHOD( GetDelay, m_bDelay );
|
||||
DEFINE_METHOD( GetWarpBeginRow, m_iWarpBeginRow );
|
||||
DEFINE_METHOD( GetWarpDestination, m_fWarpDestination );
|
||||
|
||||
LunaSongPosition()
|
||||
{
|
||||
ADD_METHOD( GetMusicSecondsVisible );
|
||||
ADD_METHOD( GetSongBeatVisible );
|
||||
ADD_METHOD( GetMusicSeconds );
|
||||
ADD_METHOD( GetSongBeat );
|
||||
ADD_METHOD( GetSongBeatNoOffset );
|
||||
ADD_METHOD( GetCurBPS );
|
||||
ADD_METHOD( GetFreeze );
|
||||
ADD_METHOD( GetDelay );
|
||||
ADD_METHOD( GetWarpBeginRow );
|
||||
ADD_METHOD( GetWarpDestination );
|
||||
}
|
||||
};
|
||||
|
||||
LUA_REGISTER_CLASS( SongPosition );
|
||||
/**
|
||||
* @file
|
||||
* @author Thai Pangsakulyanont (c) 2011
|
||||
* @section LICENSE
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef SONGPOSITION_H
|
||||
#define SONGPOSITION_H
|
||||
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "RageTimer.h"
|
||||
#include "LightsManager.h"
|
||||
#include "MessageManager.h"
|
||||
#include "TimingData.h"
|
||||
//XXX: where does this come from? might need another include
|
||||
struct lua_State;
|
||||
|
||||
class SongPosition
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// Arcade - the current stage (one song).
|
||||
// Oni/Endless - a single song in a course.
|
||||
// Let a lot of classes access this info here so they don't have to keep their own copies.
|
||||
float m_fMusicSeconds; // time into the current song, not scaled by music rate
|
||||
float m_fSongBeat;
|
||||
float m_fSongBeatNoOffset;
|
||||
float m_fCurBPS;
|
||||
float m_fLightSongBeat; // g_fLightsFalloffSeconds ahead
|
||||
//bool m_bStop; // in the middle of a stop (freeze or delay)
|
||||
/** @brief A flag to determine if we're in the middle of a freeze/stop. */
|
||||
bool m_bFreeze;
|
||||
/** @brief A flag to determine if we're in the middle of a delay (Pump style stop). */
|
||||
bool m_bDelay;
|
||||
/** @brief The row used to start a warp. */
|
||||
int m_iWarpBeginRow;
|
||||
/** @brief The beat to warp to afterwards. */
|
||||
float m_fWarpDestination;
|
||||
RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
|
||||
float m_fMusicSecondsVisible;
|
||||
float m_fSongBeatVisible;
|
||||
|
||||
void Reset();
|
||||
void UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp = RageZeroTimer );
|
||||
|
||||
// Lua
|
||||
void PushSelf( lua_State *L );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @author Thai Pangsakulyanont (c) 2011
|
||||
* @section LICENSE
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -44,7 +44,7 @@ void SoundEffectControl::Update( float fDeltaTime )
|
||||
|
||||
if( LOCK_TO_HOLD )
|
||||
{
|
||||
int iRow = BeatToNoteRow( GAMESTATE->m_fSongBeat );
|
||||
int iRow = BeatToNoteRow( GAMESTATE->m_Position.m_fSongBeat );
|
||||
int iHoldsHeld, iHoldsLetGo;
|
||||
HoldsBeingHeld( iRow, iHoldsHeld, iHoldsLetGo );
|
||||
|
||||
|
||||
@@ -1444,6 +1444,14 @@
|
||||
RelativePath="SongOptions.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\SongPosition.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\SongPosition.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SongUtil.cpp"
|
||||
>
|
||||
|
||||
@@ -339,6 +339,7 @@ void Steps::AutogenFrom( const Steps *parent_, StepsType ntTo )
|
||||
{
|
||||
parent = parent_;
|
||||
m_StepsType = ntTo;
|
||||
m_Timing = parent->m_Timing;
|
||||
}
|
||||
|
||||
void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds ) // pSource does not have to be of the same StepsType
|
||||
@@ -348,6 +349,7 @@ void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds
|
||||
pSource->GetNoteData( noteData );
|
||||
noteData.SetNumTracks( GAMEMAN->GetStepsTypeInfo(ntTo).iNumTracks );
|
||||
parent = NULL;
|
||||
m_Timing = pSource->m_Timing;
|
||||
this->SetNoteData( noteData );
|
||||
this->SetDescription( pSource->GetDescription() );
|
||||
this->SetDifficulty( pSource->GetDifficulty() );
|
||||
@@ -400,6 +402,21 @@ void Steps::SetMeter( int meter )
|
||||
m_iMeter = meter;
|
||||
}
|
||||
|
||||
bool Steps::HasSignificantTimingChanges() const
|
||||
{
|
||||
if( m_Timing.HasStops() )
|
||||
return true;
|
||||
|
||||
/* TODO: Deal with DisplayBPM here...if possible?
|
||||
* Song's version may still be useful. */
|
||||
|
||||
else if( m_Timing.HasBpmChanges() || m_Timing.HasWarps() || m_Timing.HasSpeedChanges() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Steps::SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] )
|
||||
{
|
||||
DeAutogen();
|
||||
@@ -424,6 +441,8 @@ public:
|
||||
DEFINE_METHOD( IsAnEdit, IsAnEdit() )
|
||||
DEFINE_METHOD( IsAPlayerEdit, IsAPlayerEdit() )
|
||||
|
||||
static int HasSignificantTimingChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasSignificantTimingChanges()); return 1; }
|
||||
|
||||
static int GetRadarValues( T* p, lua_State *L )
|
||||
{
|
||||
PlayerNumber pn = Enum::Check<PlayerNumber>(L, 1);
|
||||
@@ -432,6 +451,12 @@ public:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetTimingData( T* p, lua_State *L )
|
||||
{
|
||||
p->m_Timing.PushSelf(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetHash( T* p, lua_State *L ) { lua_pushnumber( L, p->GetHash() ); return 1; }
|
||||
|
||||
// untested
|
||||
@@ -442,6 +467,7 @@ public:
|
||||
lua_pushstring( L, out );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
LunaSteps()
|
||||
{
|
||||
@@ -452,7 +478,9 @@ public:
|
||||
ADD_METHOD( GetFilename );
|
||||
ADD_METHOD( GetHash );
|
||||
ADD_METHOD( GetMeter );
|
||||
ADD_METHOD( HasSignificantTimingChanges );
|
||||
ADD_METHOD( GetRadarValues );
|
||||
ADD_METHOD( GetTimingData );
|
||||
//ADD_METHOD( GetSMNoteData );
|
||||
ADD_METHOD( GetStepsType );
|
||||
ADD_METHOD( IsAnEdit );
|
||||
|
||||
+10
@@ -8,6 +8,8 @@
|
||||
#include "Difficulty.h"
|
||||
#include "RageUtil_AutoPtr.h"
|
||||
#include "RageUtil_CachedObject.h"
|
||||
#include "TimingData.h"
|
||||
|
||||
class Profile;
|
||||
class NoteData;
|
||||
struct lua_State;
|
||||
@@ -116,6 +118,14 @@ public:
|
||||
void TidyUpData();
|
||||
void CalculateRadarValues( float fMusicLengthSeconds );
|
||||
|
||||
/** @brief Timing data */
|
||||
TimingData m_Timing;
|
||||
|
||||
/**
|
||||
* @brief Determine if the Steps have any major timing changes during gameplay.
|
||||
* @return true if it does, or false otherwise. */
|
||||
bool HasSignificantTimingChanges() const;
|
||||
|
||||
// Lua
|
||||
void PushSelf( lua_State *L );
|
||||
|
||||
|
||||
+354
-27
@@ -8,10 +8,16 @@
|
||||
#include <float.h>
|
||||
|
||||
|
||||
TimingData::TimingData()
|
||||
TimingData::TimingData() :
|
||||
m_fBeat0OffsetInSeconds(0),
|
||||
m_bHasNegativeBpms(false)
|
||||
{
|
||||
m_fBeat0OffsetInSeconds = 0;
|
||||
m_bHasNegativeBpms = false;
|
||||
}
|
||||
|
||||
TimingData::TimingData(float fOffset) :
|
||||
m_fBeat0OffsetInSeconds(fOffset),
|
||||
m_bHasNegativeBpms(false)
|
||||
{
|
||||
}
|
||||
|
||||
void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut ) const
|
||||
@@ -62,6 +68,16 @@ void TimingData::AddLabelSegment( const LabelSegment &seg )
|
||||
m_LabelSegments.insert( upper_bound(m_LabelSegments.begin(), m_LabelSegments.end(), seg), seg );
|
||||
}
|
||||
|
||||
void TimingData::AddSpeedSegment( const SpeedSegment &seg )
|
||||
{
|
||||
m_SpeedSegments.insert( upper_bound(m_SpeedSegments.begin(), m_SpeedSegments.end(), seg), seg );
|
||||
}
|
||||
|
||||
void TimingData::AddFakeSegment( const FakeSegment &seg )
|
||||
{
|
||||
m_FakeSegments.insert( upper_bound(m_FakeSegments.begin(), m_FakeSegments.end(), seg), seg );
|
||||
}
|
||||
|
||||
/* Change an existing BPM segment, merge identical segments together or insert a new one. */
|
||||
void TimingData::SetBPMAtRow( int iNoteRow, float fBPM )
|
||||
{
|
||||
@@ -160,7 +176,7 @@ void TimingData::SetWarpAtRow( int iRow, float fNew )
|
||||
for( i=0; i<m_WarpSegments.size(); i++ )
|
||||
if( m_WarpSegments[i].m_iStartRow == iRow )
|
||||
break;
|
||||
bool valid = iRow > 0 && NoteRowToBeat(iRow) < fNew;
|
||||
bool valid = iRow > 0 && fNew > 0;
|
||||
if( i == m_WarpSegments.size() )
|
||||
{
|
||||
if( valid )
|
||||
@@ -172,7 +188,7 @@ void TimingData::SetWarpAtRow( int iRow, float fNew )
|
||||
{
|
||||
if( valid )
|
||||
{
|
||||
m_WarpSegments[i].m_fEndBeat = fNew;
|
||||
m_WarpSegments[i].m_fLengthBeats = fNew;
|
||||
}
|
||||
else
|
||||
m_WarpSegments.erase( m_WarpSegments.begin()+i, m_WarpSegments.begin()+i+1 );
|
||||
@@ -244,6 +260,86 @@ void TimingData::SetLabelAtRow( int iRow, const RString sLabel )
|
||||
}
|
||||
}
|
||||
|
||||
void TimingData::SetSpeedAtRow( int iRow, float fPercent, float fWait, unsigned short usMode )
|
||||
{
|
||||
unsigned i;
|
||||
for( i = 0; i < m_SpeedSegments.size(); i++ )
|
||||
{
|
||||
if( m_SpeedSegments[i].m_iStartRow >= iRow)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( i == m_SpeedSegments.size() || m_SpeedSegments[i].m_iStartRow != iRow )
|
||||
{
|
||||
// the core mod itself matters the most for comparisons.
|
||||
if( i == 0 || m_SpeedSegments[i-1].m_fPercent != fPercent )
|
||||
AddSpeedSegment( SpeedSegment(iRow, fPercent, fWait, usMode) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// The others aren't compared: only the mod itself matters.
|
||||
if( i > 0 && m_SpeedSegments[i-1].m_fPercent == fPercent )
|
||||
m_SpeedSegments.erase( m_SpeedSegments.begin()+i,
|
||||
m_SpeedSegments.begin()+i+1 );
|
||||
else
|
||||
{
|
||||
m_SpeedSegments[i].m_fPercent = fPercent;
|
||||
m_SpeedSegments[i].m_fWait = fWait;
|
||||
m_SpeedSegments[i].m_usMode = usMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimingData::SetFakeAtRow( int iRow, float fNew )
|
||||
{
|
||||
unsigned i;
|
||||
for( i=0; i<m_FakeSegments.size(); i++ )
|
||||
if( m_FakeSegments[i].m_iStartRow == iRow )
|
||||
break;
|
||||
bool valid = iRow > 0 && fNew > 0;
|
||||
if( i == m_FakeSegments.size() )
|
||||
{
|
||||
if( valid )
|
||||
{
|
||||
AddFakeSegment( FakeSegment(iRow, fNew) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( valid )
|
||||
{
|
||||
m_FakeSegments[i].m_fLengthBeats = fNew;
|
||||
}
|
||||
else
|
||||
m_FakeSegments.erase( m_FakeSegments.begin()+i, m_FakeSegments.begin()+i+1 );
|
||||
}
|
||||
}
|
||||
|
||||
void TimingData::SetSpeedPercentAtRow( int iRow, float fPercent )
|
||||
{
|
||||
SetSpeedAtRow( iRow,
|
||||
fPercent,
|
||||
GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fWait,
|
||||
GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_usMode);
|
||||
}
|
||||
|
||||
void TimingData::SetSpeedWaitAtRow( int iRow, float fWait )
|
||||
{
|
||||
SetSpeedAtRow( iRow,
|
||||
GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fPercent,
|
||||
fWait,
|
||||
GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_usMode);
|
||||
}
|
||||
|
||||
void TimingData::SetSpeedModeAtRow( int iRow, unsigned short usMode )
|
||||
{
|
||||
SetSpeedAtRow( iRow,
|
||||
GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fPercent,
|
||||
GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fWait,
|
||||
usMode );
|
||||
}
|
||||
|
||||
|
||||
float TimingData::GetStopAtRow( int iNoteRow, bool bDelay ) const
|
||||
{
|
||||
for( unsigned i=0; i<m_StopSegments.size(); i++ )
|
||||
@@ -283,7 +379,34 @@ float TimingData::GetWarpAtRow( int iWarpRow ) const
|
||||
{
|
||||
if( m_WarpSegments[i].m_iStartRow == iWarpRow )
|
||||
{
|
||||
return m_WarpSegments[i].m_fEndBeat;
|
||||
return m_WarpSegments[i].m_fLengthBeats;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float TimingData::GetSpeedPercentAtRow( int iRow )
|
||||
{
|
||||
return GetSpeedSegmentAtRow( iRow ).m_fPercent;
|
||||
}
|
||||
|
||||
float TimingData::GetSpeedWaitAtRow( int iRow )
|
||||
{
|
||||
return GetSpeedSegmentAtRow( iRow ).m_fWait;
|
||||
}
|
||||
|
||||
unsigned short TimingData::GetSpeedModeAtRow( int iRow )
|
||||
{
|
||||
return GetSpeedSegmentAtRow( iRow ).m_usMode;
|
||||
}
|
||||
|
||||
float TimingData::GetFakeAtRow( int iFakeRow ) const
|
||||
{
|
||||
for( unsigned i=0; i<m_FakeSegments.size(); i++ )
|
||||
{
|
||||
if( m_FakeSegments[i].m_iStartRow == iFakeRow )
|
||||
{
|
||||
return m_FakeSegments[i].m_fLengthBeats;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -371,6 +494,18 @@ int TimingData::GetWarpSegmentIndexAtRow( int iNoteRow ) const
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
int TimingData::GetFakeSegmentIndexAtRow( int iNoteRow ) const
|
||||
{
|
||||
unsigned i;
|
||||
for( i=0; i<m_FakeSegments.size()-1; i++ )
|
||||
{
|
||||
const FakeSegment& s = m_FakeSegments[i+1];
|
||||
if( s.m_iStartRow > iNoteRow )
|
||||
break;
|
||||
}
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
bool TimingData::IsWarpAtRow( int iNoteRow ) const
|
||||
{
|
||||
if( m_WarpSegments.empty() )
|
||||
@@ -378,7 +513,7 @@ bool TimingData::IsWarpAtRow( int iNoteRow ) const
|
||||
|
||||
int i = GetWarpSegmentIndexAtRow( iNoteRow );
|
||||
const WarpSegment& s = m_WarpSegments[i];
|
||||
if( s.m_iStartRow <= iNoteRow && iNoteRow < BeatToNoteRow(s.m_fEndBeat) )
|
||||
if( s.m_iStartRow <= iNoteRow && iNoteRow < (s.m_iStartRow + BeatToNoteRow(s.m_fLengthBeats) ) )
|
||||
{
|
||||
if( m_StopSegments.empty() )
|
||||
{
|
||||
@@ -393,6 +528,20 @@ bool TimingData::IsWarpAtRow( int iNoteRow ) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimingData::IsFakeAtRow( int iNoteRow ) const
|
||||
{
|
||||
if( m_FakeSegments.empty() )
|
||||
return false;
|
||||
|
||||
int i = GetFakeSegmentIndexAtRow( iNoteRow );
|
||||
const FakeSegment& s = m_FakeSegments[i];
|
||||
if( s.m_iStartRow <= iNoteRow && iNoteRow < ( s.m_iStartRow + BeatToNoteRow(s.m_fLengthBeats) ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TimingData::GetTimeSignatureSegmentIndexAtRow( int iRow ) const
|
||||
{
|
||||
unsigned i;
|
||||
@@ -426,6 +575,15 @@ int TimingData::GetLabelSegmentIndexAtRow( int iRow ) const
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
int TimingData::GetSpeedSegmentIndexAtRow( int iRow ) const
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i < m_SpeedSegments.size() - 1; i++ )
|
||||
if( m_SpeedSegments[i+1].m_iStartRow > iRow )
|
||||
break;
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow )
|
||||
{
|
||||
static BPMSegment empty;
|
||||
@@ -445,6 +603,15 @@ TimeSignatureSegment& TimingData::GetTimeSignatureSegmentAtRow( int iRow )
|
||||
return m_vTimeSignatureSegments[i];
|
||||
}
|
||||
|
||||
SpeedSegment& TimingData::GetSpeedSegmentAtRow( int iRow )
|
||||
{
|
||||
unsigned i;
|
||||
for( i=0; i<m_SpeedSegments.size()-1; i++ )
|
||||
if( m_SpeedSegments[i+1].m_iStartRow > iRow )
|
||||
break;
|
||||
return m_SpeedSegments[i];
|
||||
}
|
||||
|
||||
int TimingData::GetTimeSignatureNumeratorAtRow( int iRow )
|
||||
{
|
||||
return GetTimeSignatureSegmentAtRow( iRow ).m_iNumerator;
|
||||
@@ -493,6 +660,16 @@ WarpSegment& TimingData::GetWarpSegmentAtRow( int iRow )
|
||||
return m_WarpSegments[i];
|
||||
}
|
||||
|
||||
FakeSegment& TimingData::GetFakeSegmentAtRow( int iRow )
|
||||
{
|
||||
static FakeSegment empty;
|
||||
if( m_FakeSegments.empty() )
|
||||
return empty;
|
||||
|
||||
int i = GetFakeSegmentIndexAtRow( iRow );
|
||||
return m_FakeSegments[i];
|
||||
}
|
||||
|
||||
int TimingData::GetTickcountSegmentIndexAtRow( int iRow ) const
|
||||
{
|
||||
unsigned i;
|
||||
@@ -652,15 +829,18 @@ void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float
|
||||
}
|
||||
break;
|
||||
case FOUND_WARP:
|
||||
bIsWarping = true;
|
||||
if( itWS->m_fEndBeat > fWarpDestination )
|
||||
{
|
||||
fWarpDestination = itWS->m_fEndBeat;
|
||||
bIsWarping = true;
|
||||
float fWarpSum = itWS->m_fLengthBeats + NoteRowToBeat( itWS->m_iStartRow );
|
||||
if( fWarpSum > fWarpDestination )
|
||||
{
|
||||
fWarpDestination = fWarpSum;
|
||||
}
|
||||
iWarpBeginOut = iEventRow;
|
||||
fWarpDestinationOut = fWarpDestination;
|
||||
itWS ++;
|
||||
break;
|
||||
}
|
||||
iWarpBeginOut = iEventRow;
|
||||
fWarpDestinationOut = fWarpDestination;
|
||||
itWS ++;
|
||||
break;
|
||||
}
|
||||
iLastRow = iEventRow;
|
||||
}
|
||||
@@ -744,13 +924,16 @@ float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const
|
||||
case FOUND_MARKER:
|
||||
return fLastTime;
|
||||
case FOUND_WARP:
|
||||
bIsWarping = true;
|
||||
if( itWS->m_fEndBeat > fWarpDestination )
|
||||
{
|
||||
fWarpDestination = itWS->m_fEndBeat;
|
||||
bIsWarping = true;
|
||||
float fWarpSum = itWS->m_fLengthBeats + NoteRowToBeat( itWS->m_iStartRow );
|
||||
if( fWarpSum > fWarpDestination )
|
||||
{
|
||||
fWarpDestination = fWarpSum;
|
||||
}
|
||||
itWS ++;
|
||||
break;
|
||||
}
|
||||
itWS ++;
|
||||
break;
|
||||
}
|
||||
iLastRow = iEventRow;
|
||||
}
|
||||
@@ -790,13 +973,13 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
|
||||
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 );
|
||||
const int iSegEndRow = iSegStartRow + BeatToNoteRow( m_WarpSegments[i].m_fLengthBeats );
|
||||
if( iSegEndRow >= iStartIndex )
|
||||
{
|
||||
if( iSegEndRow > iEndIndex )
|
||||
m_WarpSegments[i].m_fEndBeat += NoteRowToBeat(lrintf((iEndIndex - iStartIndex) * (fScale - 1)));
|
||||
m_WarpSegments[i].m_fLengthBeats += NoteRowToBeat(lrintf((iEndIndex - iStartIndex) * (fScale - 1)));
|
||||
else
|
||||
m_WarpSegments[i].m_fEndBeat = NoteRowToBeat(lrintf((iSegEndRow - iStartIndex) * fScale) + iStartIndex);
|
||||
m_WarpSegments[i].m_fLengthBeats = NoteRowToBeat(lrintf((iSegEndRow - iStartIndex) * fScale));
|
||||
}
|
||||
if( iSegStartRow < iStartIndex )
|
||||
continue;
|
||||
@@ -839,6 +1022,36 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
|
||||
m_LabelSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex;
|
||||
}
|
||||
|
||||
for ( unsigned i = 0; i < m_SpeedSegments.size(); i++ )
|
||||
{
|
||||
const int iSegStart = m_SpeedSegments[i].m_iStartRow;
|
||||
if( iSegStart < iStartIndex )
|
||||
continue;
|
||||
else if( iSegStart > iEndIndex )
|
||||
m_SpeedSegments[i].m_iStartRow += lrintf( (iEndIndex - iStartIndex) * (fScale - 1) );
|
||||
else
|
||||
m_SpeedSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_FakeSegments.size(); i++ )
|
||||
{
|
||||
const int iSegStartRow = m_FakeSegments[i].m_iStartRow;
|
||||
const int iSegEndRow = iSegStartRow + BeatToNoteRow( m_FakeSegments[i].m_fLengthBeats );
|
||||
if( iSegEndRow >= iStartIndex )
|
||||
{
|
||||
if( iSegEndRow > iEndIndex )
|
||||
m_FakeSegments[i].m_fLengthBeats += NoteRowToBeat(lrintf((iEndIndex - iStartIndex) * (fScale - 1)));
|
||||
else
|
||||
m_FakeSegments[i].m_fLengthBeats = NoteRowToBeat(lrintf((iSegEndRow - iStartIndex) * fScale));
|
||||
}
|
||||
if( iSegStartRow < iStartIndex )
|
||||
continue;
|
||||
else if( iSegStartRow > iEndIndex )
|
||||
m_FakeSegments[i].m_iStartRow += lrintf((iEndIndex - iStartIndex) * (fScale - 1));
|
||||
else
|
||||
m_FakeSegments[i].m_iStartRow = lrintf((iSegStartRow - iStartIndex) * fScale) + iStartIndex;
|
||||
}
|
||||
|
||||
// adjust BPM changes to preserve timing
|
||||
if( bAdjustBPM )
|
||||
{
|
||||
@@ -886,8 +1099,6 @@ void TimingData::InsertRows( int iStartRow, int 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;
|
||||
@@ -923,6 +1134,22 @@ void TimingData::InsertRows( int iStartRow, int iRowsToAdd )
|
||||
continue;
|
||||
labl.m_iStartRow += iRowsToAdd;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_SpeedSegments.size(); i++ )
|
||||
{
|
||||
SpeedSegment &sped = m_SpeedSegments[i];
|
||||
if( sped.m_iStartRow < iStartRow )
|
||||
continue;
|
||||
sped.m_iStartRow += iRowsToAdd;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_FakeSegments.size(); i++ )
|
||||
{
|
||||
FakeSegment &fake = m_FakeSegments[i];
|
||||
if( fake.m_iStartRow < iStartRow )
|
||||
continue;
|
||||
fake.m_iStartRow += iRowsToAdd;
|
||||
}
|
||||
|
||||
if( iStartRow == 0 )
|
||||
{
|
||||
@@ -985,9 +1212,6 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete )
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -1080,10 +1304,99 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete )
|
||||
}
|
||||
labl.m_iStartRow -= iRowsToDelete;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_SpeedSegments.size(); i++ )
|
||||
{
|
||||
SpeedSegment &sped = m_SpeedSegments[i];
|
||||
|
||||
if( sped.m_iStartRow < iStartRow )
|
||||
continue;
|
||||
|
||||
if( sped.m_iStartRow < iStartRow+iRowsToDelete )
|
||||
{
|
||||
m_SpeedSegments.erase( m_SpeedSegments.begin()+i, m_SpeedSegments.begin()+i+1 );
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
sped.m_iStartRow -= iRowsToDelete;
|
||||
}
|
||||
|
||||
for( unsigned i = 0; i < m_FakeSegments.size(); i++ )
|
||||
{
|
||||
FakeSegment &fake = m_FakeSegments[i];
|
||||
|
||||
if( fake.m_iStartRow < iStartRow )
|
||||
continue;
|
||||
|
||||
if( fake.m_iStartRow < iStartRow+iRowsToDelete )
|
||||
{
|
||||
m_FakeSegments.erase( m_FakeSegments.begin()+i, m_FakeSegments.begin()+i+1 );
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
fake.m_iStartRow -= iRowsToDelete;
|
||||
}
|
||||
|
||||
this->SetBPMAtRow( iStartRow, fNewBPM );
|
||||
}
|
||||
|
||||
void TimingData::TidyUpData()
|
||||
{
|
||||
// If there are no BPM segments, provide a default.
|
||||
if( m_BPMSegments.empty() )
|
||||
{
|
||||
LOG->UserLog( "Song file", m_sFile, "has no BPM segments, default provided." );
|
||||
|
||||
AddBPMSegment( BPMSegment(0, 60) );
|
||||
}
|
||||
|
||||
// Make sure the first BPM segment starts at beat 0.
|
||||
if( m_BPMSegments[0].m_iStartRow != 0 )
|
||||
m_BPMSegments[0].m_iStartRow = 0;
|
||||
|
||||
// If no time signature specified, assume 4/4 time for the whole song.
|
||||
if( m_vTimeSignatureSegments.empty() )
|
||||
{
|
||||
TimeSignatureSegment seg(0, 4, 4);
|
||||
m_vTimeSignatureSegments.push_back( seg );
|
||||
}
|
||||
|
||||
// Likewise, if no tickcount signature is specified, assume 2 ticks
|
||||
//per beat for the entire song. The default of 2 is chosen more
|
||||
//for compatibility with the Pump Pro series than anything else.
|
||||
if( m_TickcountSegments.empty() )
|
||||
{
|
||||
TickcountSegment seg(0, 2);
|
||||
m_TickcountSegments.push_back( seg );
|
||||
}
|
||||
|
||||
// Have a default combo segment of one just in case.
|
||||
if( m_ComboSegments.empty() )
|
||||
{
|
||||
ComboSegment seg(0, 1);
|
||||
m_ComboSegments.push_back( seg );
|
||||
}
|
||||
|
||||
// Have a default label segment just in case.
|
||||
if( m_LabelSegments.empty() )
|
||||
{
|
||||
LabelSegment seg(0, "Song Start");
|
||||
m_LabelSegments.push_back( seg );
|
||||
}
|
||||
|
||||
// Always be sure there is a starting speed.
|
||||
if( m_SpeedSegments.empty() )
|
||||
{
|
||||
SpeedSegment seg(0, 1, 0);
|
||||
m_SpeedSegments.push_back( seg );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool TimingData::HasBpmChanges() const
|
||||
{
|
||||
return m_BPMSegments.size()>1;
|
||||
@@ -1099,6 +1412,16 @@ bool TimingData::HasWarps() const
|
||||
return m_WarpSegments.size()>0;
|
||||
}
|
||||
|
||||
bool TimingData::HasFakes() const
|
||||
{
|
||||
return m_FakeSegments.size()>0;
|
||||
}
|
||||
|
||||
bool TimingData::HasSpeedChanges() const
|
||||
{
|
||||
return m_SpeedSegments.size()>1;
|
||||
}
|
||||
|
||||
void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const
|
||||
{
|
||||
iMeasureIndexOut = 0;
|
||||
@@ -1145,6 +1468,8 @@ 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 HasFakes( T* p, lua_State *L ) { lua_pushboolean(L, p->HasFakes()); return 1; }
|
||||
static int HasSpeedChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasSpeedChanges()); return 1; }
|
||||
static int GetStops( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vStops;
|
||||
@@ -1232,6 +1557,8 @@ public:
|
||||
ADD_METHOD( HasStops );
|
||||
ADD_METHOD( HasBPMChanges );
|
||||
ADD_METHOD( HasWarps );
|
||||
ADD_METHOD( HasFakes );
|
||||
ADD_METHOD( HasSpeedChanges );
|
||||
ADD_METHOD( GetStops );
|
||||
ADD_METHOD( GetDelays );
|
||||
ADD_METHOD( GetBPMs );
|
||||
|
||||
+470
-18
@@ -313,8 +313,8 @@ struct TimeSignatureSegment
|
||||
*
|
||||
* A warp segment is used to replicate the effects of Negative BPMs without
|
||||
* abusing negative BPMs. Negative BPMs should be converted to warp segments.
|
||||
* WarpAt=WarpTo is the format, where both are in beats. (Technically they're
|
||||
* both rows though.) */
|
||||
* WarpAt=WarpToRelative is the format, where both are in beats.
|
||||
* (Technically they're both rows though.) */
|
||||
struct WarpSegment
|
||||
{
|
||||
/**
|
||||
@@ -322,45 +322,45 @@ struct WarpSegment
|
||||
*
|
||||
* It is best to override the values as soon as possible.
|
||||
*/
|
||||
WarpSegment() : m_iStartRow(-1), m_fEndBeat(-1) { }
|
||||
WarpSegment() : m_iStartRow(-1), m_fLengthBeats(-1) { }
|
||||
/**
|
||||
* @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.
|
||||
* @param r the number of rows to jump ahead.
|
||||
*/
|
||||
WarpSegment( int s, int r ): m_iStartRow(max(0, (s < r ? s : r))),
|
||||
m_fEndBeat(max(0, NoteRowToBeat((r > s ? r : s)))) {}
|
||||
WarpSegment( int s, int r ): m_iStartRow(s),
|
||||
m_fLengthBeats(NoteRowToBeat(r)) {}
|
||||
/**
|
||||
* @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.
|
||||
* @param b the number of beats to jump ahead.
|
||||
*/
|
||||
WarpSegment( int s, float b ): m_iStartRow(max(0, s)),
|
||||
m_fEndBeat(max(0, b)) {}
|
||||
m_fLengthBeats(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.
|
||||
* @param r the number of rows to jump ahead.
|
||||
*/
|
||||
WarpSegment( float s, int r ):
|
||||
m_iStartRow(max(0, BeatToNoteRow(s))),
|
||||
m_fEndBeat(max(0, NoteRowToBeat(r))) {}
|
||||
m_fLengthBeats(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.
|
||||
* @param b the number of beats to jump ahead.
|
||||
*/
|
||||
WarpSegment( float s, float b ):
|
||||
m_iStartRow(max(0, BeatToNoteRow((s < b ? s : b)))),
|
||||
m_fEndBeat(max(0, (b > s ? b : s))) {}
|
||||
m_iStartRow(BeatToNoteRow(s)),
|
||||
m_fLengthBeats(b) {}
|
||||
/**
|
||||
* @brief The row in which the WarpSegment activates.
|
||||
*/
|
||||
int m_iStartRow;
|
||||
/**
|
||||
* @brief The beat to warp to.
|
||||
* @brief The number of beats to warp ahead by.
|
||||
*/
|
||||
float m_fEndBeat;
|
||||
float m_fLengthBeats;
|
||||
/**
|
||||
* @brief Compares two WarpSegments to see if they are equal to each other.
|
||||
* @param other the other WarpSegment to compare to.
|
||||
@@ -369,7 +369,7 @@ struct WarpSegment
|
||||
bool operator==( const WarpSegment &other ) const
|
||||
{
|
||||
COMPARE( m_iStartRow );
|
||||
COMPARE( m_fEndBeat );
|
||||
COMPARE( m_fLengthBeats );
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
@@ -386,7 +386,7 @@ struct WarpSegment
|
||||
bool operator<( const WarpSegment &other ) const
|
||||
{
|
||||
return m_iStartRow < other.m_iStartRow ||
|
||||
( m_iStartRow == other.m_iStartRow && m_fEndBeat < other.m_fEndBeat );
|
||||
( m_iStartRow == other.m_iStartRow && m_fLengthBeats < other.m_fLengthBeats );
|
||||
}
|
||||
/**
|
||||
* @brief Compares two WarpSegments to see if one is less than or equal to the other.
|
||||
@@ -508,7 +508,7 @@ struct ComboSegment
|
||||
* @param t the amount the combo increases on a succesful hit.
|
||||
*/
|
||||
ComboSegment( int s, int t ): m_iStartRow(max(0, s)),
|
||||
m_iCombo(max(1,t)) {}
|
||||
m_iCombo(max(0,t)) {}
|
||||
/**
|
||||
* @brief The row in which the ComboSegment activates.
|
||||
*/
|
||||
@@ -647,6 +647,243 @@ struct LabelSegment
|
||||
bool operator>=( const LabelSegment &other ) const { return !operator<(other); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Identifies when the arrow scroll changes.
|
||||
*
|
||||
* SpeedSegments take a Player's scrolling BPM (Step's BPM * speed mod),
|
||||
* and then multiplies it with the percentage value. No matter the player's
|
||||
* speed mod, the ratio will be the same. Unlike forced attacks, these
|
||||
* cannot be turned off at a set time: reset it by setting the precentage
|
||||
* back to 1.
|
||||
*
|
||||
* These were inspired by the Pump It Up series. */
|
||||
struct SpeedSegment
|
||||
{
|
||||
/** @brief Sets up the SpeedSegment with default values. */
|
||||
SpeedSegment(): m_iStartRow(0),
|
||||
m_fPercent(1), m_fWait(0), m_usMode(0) {}
|
||||
|
||||
/**
|
||||
* @brief Sets up the SpeedSegment with specified values.
|
||||
* @param i The row this activates.
|
||||
* @param p The percentage to use. */
|
||||
SpeedSegment(int i, float p): m_iStartRow(0),
|
||||
m_fPercent(p), m_fWait(0), m_usMode(0) {}
|
||||
|
||||
/**
|
||||
* @brief Sets up the SpeedSegment with specified values.
|
||||
* @param r The beat this activates.
|
||||
* @param p The percentage to use. */
|
||||
SpeedSegment(float r, float p): m_iStartRow(BeatToNoteRow(r)),
|
||||
m_fPercent(p), m_fWait(0), m_usMode(0) {}
|
||||
|
||||
/**
|
||||
* @brief Sets up the SpeedSegment with specified values.
|
||||
* @param i The row this activates.
|
||||
* @param p The percentage to use.
|
||||
* @param w The number of beats to wait. */
|
||||
SpeedSegment(int i, float p, float w): m_iStartRow(i),
|
||||
m_fPercent(p), m_fWait(w), m_usMode(0) {}
|
||||
|
||||
/**
|
||||
* @brief Sets up the SpeedSegment with specified values.
|
||||
* @param r The beat this activates.
|
||||
* @param p The percentage to use.
|
||||
* @param w The number of beats to wait. */
|
||||
SpeedSegment(float r, float p, float w): m_iStartRow(BeatToNoteRow(r)),
|
||||
m_fPercent(p), m_fWait(w), m_usMode(0) {}
|
||||
|
||||
/**
|
||||
* @brief Sets up the SpeedSegment with specified values.
|
||||
* @param i The row this activates.
|
||||
* @param p The percentage to use.
|
||||
* @param w The number of beats/seconds to wait.
|
||||
* @param k The mode used for the wait variable. */
|
||||
SpeedSegment(int i, float p, float w, unsigned short k): m_iStartRow(i),
|
||||
m_fPercent(p), m_fWait(w), m_usMode(k) {}
|
||||
|
||||
/**
|
||||
* @brief Sets up the SpeedSegment with specified values.
|
||||
* @param r The beat this activates.
|
||||
* @param p The percentage to use.
|
||||
* @param w The number of beats/seconds to wait.
|
||||
* @param k The mode used for the wait variable.*/
|
||||
SpeedSegment(float r, float p, float w, unsigned short k): m_iStartRow(BeatToNoteRow(r)),
|
||||
m_fPercent(p), m_fWait(w), m_usMode(k) {}
|
||||
|
||||
/** @brief The row in which the ComboSegment activates. */
|
||||
int m_iStartRow;
|
||||
/** @brief The percentage to use when multiplying the Player's BPM. */
|
||||
float m_fPercent;
|
||||
/**
|
||||
* @brief The number of beats or seconds to wait for the change to take place.
|
||||
*
|
||||
* A value of 0 means this is immediate. */
|
||||
float m_fWait;
|
||||
/**
|
||||
* @brief The mode that this segment uses for the math.
|
||||
*
|
||||
* 0: beats
|
||||
* 1: seconds
|
||||
* other
|
||||
*/
|
||||
unsigned short m_usMode;
|
||||
|
||||
/**
|
||||
* @brief Compares two SpeedSegments to see if they are equal to each other.
|
||||
* @param other the other SpeedSegment to compare to.
|
||||
* @return the equality of the two segments.
|
||||
*/
|
||||
bool operator==( const SpeedSegment &other ) const
|
||||
{
|
||||
COMPARE( m_iStartRow );
|
||||
COMPARE( m_fPercent );
|
||||
COMPARE( m_usMode );
|
||||
COMPARE( m_fWait );
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief Compares two SpeedSegments to see if they are not equal to each other.
|
||||
* @param other the other SpeedSegment to compare to.
|
||||
* @return the inequality of the two segments.
|
||||
*/
|
||||
bool operator!=( const SpeedSegment &other ) const { return !operator==(other); }
|
||||
/**
|
||||
* @brief Compares two SpeedSegments to see if one is less than the other.
|
||||
* @param other the other SpeedSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is less than the second.
|
||||
*/
|
||||
bool operator<( const SpeedSegment &other ) const { return m_iStartRow < other.m_iStartRow; }
|
||||
/**
|
||||
* @brief Compares two SpeedSegments to see if one is less than or equal to the other.
|
||||
* @param other the other SpeedSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is less or equal to than the second.
|
||||
*/
|
||||
bool operator<=( const SpeedSegment &other ) const
|
||||
{
|
||||
return ( operator<(other) || operator==(other) );
|
||||
}
|
||||
/**
|
||||
* @brief Compares two SpeedSegments to see if one is greater than the other.
|
||||
* @param other the other SpeedSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is greater than the second.
|
||||
*/
|
||||
bool operator>( const SpeedSegment &other ) const { return !operator<=(other); }
|
||||
/**
|
||||
* @brief Compares two SpeedSegments to see if one is greater than or equal to the other.
|
||||
* @param other the other SpeedSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is greater than or equal to the second.
|
||||
*/
|
||||
bool operator>=( const SpeedSegment &other ) const { return !operator<(other); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Identifies when a whole region of arrows is to be ignored.
|
||||
*
|
||||
* FakeSegments are similar to the Fake Tap Notes in that the contents
|
||||
* inside are neither for nor against the player. They can be useful for
|
||||
* mission modes, in conjunction with WarpSegments, or perhaps other
|
||||
* uses not thought up at the time of this comment. Unlike the Warp
|
||||
* Segments, these are not magically jumped over: instead, these are
|
||||
* drawn normally.
|
||||
*
|
||||
* These were inspired by the Pump It Up series. */
|
||||
struct FakeSegment
|
||||
{
|
||||
/**
|
||||
* @brief Create a simple Fake Segment with default values.
|
||||
*
|
||||
* It is best to override the values as soon as possible.
|
||||
*/
|
||||
FakeSegment() : m_iStartRow(-1), m_fLengthBeats(-1) { }
|
||||
/**
|
||||
* @brief Create a Fake Segment with the specified values.
|
||||
* @param s the starting row of this segment.
|
||||
* @param r the number of rows this segment lasts.
|
||||
*/
|
||||
FakeSegment( int s, int r ): m_iStartRow(s),
|
||||
m_fLengthBeats(NoteRowToBeat(r)) {}
|
||||
/**
|
||||
* @brief Creates a Fake Segment with the specified values.
|
||||
* @param s the starting row of this segment.
|
||||
* @param b the number of beats this segment lasts.
|
||||
*/
|
||||
FakeSegment( int s, float b ): m_iStartRow(max(0, s)),
|
||||
m_fLengthBeats(max(0, b)) {}
|
||||
/**
|
||||
* @brief Create a Fake Segment with the specified values.
|
||||
* @param s the starting beat in this segment.
|
||||
* @param r the number of rows this segment lasts.
|
||||
*/
|
||||
FakeSegment( float s, int r ):
|
||||
m_iStartRow(max(0, BeatToNoteRow(s))),
|
||||
m_fLengthBeats(max(0, NoteRowToBeat(r))) {}
|
||||
/**
|
||||
* @brief Creates a Fake Segment with the specified values.
|
||||
* @param s the starting beat of this segment.
|
||||
* @param b the number of beats this segment lasts.
|
||||
*/
|
||||
FakeSegment( float s, float b ):
|
||||
m_iStartRow(BeatToNoteRow(s)),
|
||||
m_fLengthBeats(b) {}
|
||||
/**
|
||||
* @brief The row in which the FakeSegment activates.
|
||||
*/
|
||||
int m_iStartRow;
|
||||
/**
|
||||
* @brief The number of beats the FakeSegment is alive for.
|
||||
*/
|
||||
float m_fLengthBeats;
|
||||
/**
|
||||
* @brief Compares two FakeSegments to see if they are equal to each other.
|
||||
* @param other the other FakeSegment to compare to.
|
||||
* @return the equality of the two segments.
|
||||
*/
|
||||
bool operator==( const FakeSegment &other ) const
|
||||
{
|
||||
COMPARE( m_iStartRow );
|
||||
COMPARE( m_fLengthBeats );
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief Compares two FakeSegments to see if they are not equal to each other.
|
||||
* @param other the other FakeSegment to compare to.
|
||||
* @return the inequality of the two segments.
|
||||
*/
|
||||
bool operator!=( const FakeSegment &other ) const { return !operator==(other); }
|
||||
/**
|
||||
* @brief Compares two FakeSegments to see if one is less than the other.
|
||||
* @param other the other FakeSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is less than the second.
|
||||
*/
|
||||
bool operator<( const FakeSegment &other ) const
|
||||
{
|
||||
return m_iStartRow < other.m_iStartRow ||
|
||||
( m_iStartRow == other.m_iStartRow && m_fLengthBeats < other.m_fLengthBeats );
|
||||
}
|
||||
/**
|
||||
* @brief Compares two FakeSegments to see if one is less than or equal to the other.
|
||||
* @param other the other FakeSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is less or equal to than the second.
|
||||
*/
|
||||
bool operator<=( const FakeSegment &other ) const
|
||||
{
|
||||
return ( operator<(other) || operator==(other) );
|
||||
}
|
||||
/**
|
||||
* @brief Compares two FakeSegments to see if one is greater than the other.
|
||||
* @param other the other FakeSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is greater than the second.
|
||||
*/
|
||||
bool operator>( const FakeSegment &other ) const { return !operator<=(other); }
|
||||
/**
|
||||
* @brief Compares two FakeSegments to see if one is greater than or equal to the other.
|
||||
* @param other the other FakeSegment to compare to.
|
||||
* @return the truth/falsehood of if the first is greater than or equal to the second.
|
||||
*/
|
||||
bool operator>=( const FakeSegment &other ) const { return !operator<(other); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Holds data for translating beats<->seconds.
|
||||
@@ -658,6 +895,10 @@ public:
|
||||
* @brief Sets up initial timing data.
|
||||
*/
|
||||
TimingData();
|
||||
/**
|
||||
* @brief Sets up initial timing data with a defined offset.
|
||||
* @param fOffset the offset from the 0th beat. */
|
||||
TimingData(float fOffset);
|
||||
/**
|
||||
* @brief Gets the actual BPM of the song.
|
||||
* @param fMinBPMOut the minimium specified BPM.
|
||||
@@ -1234,6 +1475,193 @@ public:
|
||||
*/
|
||||
float GetNextLabelSegmentBeatAtBeat( float fBeat ) const { return GetNextLabelSegmentBeatAtRow( BeatToNoteRow(fBeat) ); }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the Speed's percent at the given row.
|
||||
* @param iNoteRow the row in question.
|
||||
* @return the percent.
|
||||
*/
|
||||
float GetSpeedPercentAtRow( int iNoteRow );
|
||||
/**
|
||||
* @brief Retrieve the Speed's percent at the given beat.
|
||||
* @param fBeat the beat in question.
|
||||
* @return the percent.
|
||||
*/
|
||||
float GetSpeedPercentAtBeat( float fBeat ) { return GetSpeedPercentAtRow( BeatToNoteRow(fBeat) ); }
|
||||
/**
|
||||
* @brief Retrieve the Speed's wait at the given row.
|
||||
* @param iNoteRow the row in question.
|
||||
* @return the wait.
|
||||
*/
|
||||
float GetSpeedWaitAtRow( int iNoteRow );
|
||||
/**
|
||||
* @brief Retrieve the Speed's wait at the given beat.
|
||||
* @param fBeat the beat in question.
|
||||
* @return the wait.
|
||||
*/
|
||||
float GetSpeedWaitAtBeat( float fBeat ) { return GetSpeedWaitAtRow( BeatToNoteRow(fBeat) ); }
|
||||
/**
|
||||
* @brief Retrieve the Speed's mode at the given row.
|
||||
* @param iNoteRow the row in question.
|
||||
* @return the mode.
|
||||
*/
|
||||
unsigned short GetSpeedModeAtRow( int iNoteRow );
|
||||
/**
|
||||
* @brief Retrieve the Speed's mode at the given beat.
|
||||
* @param fBeat the beat in question.
|
||||
* @return the mode.
|
||||
*/
|
||||
unsigned short GetSpeedModeAtBeat( float fBeat ) { return GetSpeedModeAtRow( BeatToNoteRow(fBeat) ); }
|
||||
/**
|
||||
* @brief Set the row to have the new Speed.
|
||||
* @param iNoteRow the row to have the new Speed.
|
||||
* @param fPercent the percent.
|
||||
* @param fWait the wait.
|
||||
* @param usMode the mode.
|
||||
*/
|
||||
void SetSpeedAtRow( int iNoteRow, float fPercent, float fWait, unsigned short usMode );
|
||||
/**
|
||||
* @brief Set the beat to have the new Speed.
|
||||
* @param fBeat the beat to have the new Speed.
|
||||
* @param fPercent the percent.
|
||||
* @param fWait the wait.
|
||||
* @param usMode the mode.
|
||||
*/
|
||||
void SetSpeedAtBeat( float fBeat, float fPercent, float fWait, unsigned short usMode ) { SetSpeedAtRow( BeatToNoteRow(fBeat), fPercent, fWait, usMode ); }
|
||||
/**
|
||||
* @brief Set the row to have the new Speed percent.
|
||||
* @param iNoteRow the row to have the new Speed percent.
|
||||
* @param fPercent the percent.
|
||||
*/
|
||||
void SetSpeedPercentAtRow( int iNoteRow, float fPercent );
|
||||
/**
|
||||
* @brief Set the beat to have the new Speed percent.
|
||||
* @param fBeat the beat to have the new Speed percent.
|
||||
* @param fPercent the percent.
|
||||
*/
|
||||
void SetSpeedPercentAtBeat( float fBeat, float fPercent ) { SetSpeedPercentAtRow( BeatToNoteRow(fBeat), fPercent); }
|
||||
/**
|
||||
* @brief Set the row to have the new Speed wait.
|
||||
* @param iNoteRow the row to have the new Speed wait.
|
||||
* @param fWait the wait.
|
||||
*/
|
||||
void SetSpeedWaitAtRow( int iNoteRow, float fWait );
|
||||
/**
|
||||
* @brief Set the beat to have the new Speed wait.
|
||||
* @param fBeat the beat to have the new Speed wait.
|
||||
* @param fWait the wait.
|
||||
*/
|
||||
void SetSpeedWaitAtBeat( float fBeat, float fWait ) { SetSpeedWaitAtRow( BeatToNoteRow(fBeat), fWait); }
|
||||
/**
|
||||
* @brief Set the row to have the new Speed mode.
|
||||
* @param iNoteRow the row to have the new Speed mode.
|
||||
* @param usMode the mode.
|
||||
*/
|
||||
void SetSpeedModeAtRow( int iNoteRow, unsigned short usMode );
|
||||
/**
|
||||
* @brief Set the beat to have the new Speed mode.
|
||||
* @param fBeat the beat to have the new Speed mode.
|
||||
* @param usMode the mode.
|
||||
*/
|
||||
void SetSpeedModeAtBeat( float fBeat, unsigned short usMode ) { SetSpeedModeAtRow( BeatToNoteRow(fBeat), usMode); }
|
||||
/**
|
||||
* @brief Retrieve the SpeedSegment at the specified row.
|
||||
* @param iNoteRow the row that has a SpeedSegment.
|
||||
* @return the SpeedSegment in question.
|
||||
*/
|
||||
SpeedSegment& GetSpeedSegmentAtRow( int iNoteRow );
|
||||
/**
|
||||
* @brief Retrieve the SpeedSegment at the specified beat.
|
||||
* @param fBeat the beat that has a SpeedSegment.
|
||||
* @return the SpeedSegment in question.
|
||||
*/
|
||||
SpeedSegment& GetSpeedSegmentAtBeat( float fBeat ) { return GetSpeedSegmentAtRow( BeatToNoteRow(fBeat) ); }
|
||||
/**
|
||||
* @brief Retrieve the index of the SpeedSegments at the specified row.
|
||||
* @param iNoteRow the row that has a SpeedSegment.
|
||||
* @return the SpeedSegment's index in question.
|
||||
*/
|
||||
int GetSpeedSegmentIndexAtRow( int iNoteRow ) const;
|
||||
/**
|
||||
* @brief Retrieve the index of the SpeedSegments at the specified beat.
|
||||
* @param fBeat the beat that has a SpeedSegment.
|
||||
* @return the SpeedSegment's index in question.
|
||||
*/
|
||||
int GetSpeedSegmentIndexAtBeat( float fBeat ) const { return GetSpeedSegmentIndexAtRow( BeatToNoteRow(fBeat) ); }
|
||||
/**
|
||||
* @brief Add the SpeedSegment to the TimingData.
|
||||
* @param seg the new SpeedSegment.
|
||||
*/
|
||||
void AddSpeedSegment( const SpeedSegment &seg );
|
||||
|
||||
/**
|
||||
* @brief Determine when the fakes end.
|
||||
* @param iRow The row you start on.
|
||||
* @return the time when the fakes end.
|
||||
*/
|
||||
float GetFakeAtRow( int iRow ) const;
|
||||
/**
|
||||
* @brief Determine when the fakes end.
|
||||
* @param fBeat The beat you start on.
|
||||
* @return the time when the fakes end.
|
||||
*/
|
||||
float GetFakeAtBeat( float fBeat ) const { return GetFakeAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Set the beat to indicate when the FakeSegment ends.
|
||||
* @param iRow The row to start on.
|
||||
* @param fNew The destination beat.
|
||||
*/
|
||||
void SetFakeAtRow( int iRow, float fNew );
|
||||
/**
|
||||
* @brief Set the beat to indicate when the FakeSegment ends.
|
||||
* @param fBeat The beat to start on.
|
||||
* @param fNew The destination beat.
|
||||
*/
|
||||
void SetFakeAtBeat( float fBeat, float fNew ) { SetFakeAtRow( BeatToNoteRow( fBeat ), fNew ); }
|
||||
/**
|
||||
* @brief Retrieve the FakeSegment at the specified row.
|
||||
* @param iRow the row to focus on.
|
||||
* @return the FakeSegment in question.
|
||||
*/
|
||||
FakeSegment& GetFakeSegmentAtRow( int iRow );
|
||||
/**
|
||||
* @brief Retrieve the FakeSegment at the specified beat.
|
||||
* @param fBeat the beat to focus on.
|
||||
* @return the FakeSegment in question.
|
||||
*/
|
||||
FakeSegment& GetFakeSegmentAtBeat( float fBeat ) { return GetFakeSegmentAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Retrieve the index of the FakeSegment at the specified row.
|
||||
* @param iRow the row to focus on.
|
||||
* @return the index in question.
|
||||
*/
|
||||
int GetFakeSegmentIndexAtRow( int iRow ) const;
|
||||
/**
|
||||
* @brief Retrieve the index of the FakeSegment at the specified beat.
|
||||
* @param fBeat the beat to focus on.
|
||||
* @return the index in question.
|
||||
*/
|
||||
int GetFakeSegmentIndexAtBeat( float fBeat ) const { return GetFakeSegmentIndexAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Checks if the row is inside a fake.
|
||||
* @param iRow the row to focus on.
|
||||
* @return true if the row is inside a fake, false otherwise.
|
||||
*/
|
||||
bool IsFakeAtRow( int iRow ) const;
|
||||
/**
|
||||
* @brief Checks if the beat is inside a fake.
|
||||
* @param fBeat the beat to focus on.
|
||||
* @return true if the row is inside a fake, false otherwise.
|
||||
*/
|
||||
bool IsFakeAtBeat( float fBeat ) const { return IsFakeAtRow( BeatToNoteRow( fBeat ) ); }
|
||||
/**
|
||||
* @brief Add the FakeSegment to the TimingData.
|
||||
* @param seg the new FakeSegment.
|
||||
*/
|
||||
void AddFakeSegment( const FakeSegment &seg );
|
||||
|
||||
|
||||
|
||||
void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor );
|
||||
|
||||
void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const;
|
||||
@@ -1276,6 +1704,14 @@ public:
|
||||
* @return true if there is at least one warp, false otherwise.
|
||||
*/
|
||||
bool HasWarps() const;
|
||||
/**
|
||||
* @brief View the TimingData to see if there is at least one fake segment involved.
|
||||
* @return true if there is at least one fake segment, false otherwise. */
|
||||
bool HasFakes() const;
|
||||
/**
|
||||
* @brief View the TimingData to see if a song changes its speed scrolling at any point.
|
||||
* @return true if there is at least one change, false otherwise. */
|
||||
bool HasSpeedChanges() const;
|
||||
/**
|
||||
* @brief Compare two sets of timing data to see if they are equal.
|
||||
* @param other the other TimingData.
|
||||
@@ -1301,8 +1737,15 @@ public:
|
||||
COMPARE( m_ComboSegments.size() );
|
||||
for( unsigned i=0; i<m_ComboSegments.size(); i++ )
|
||||
COMPARE( m_ComboSegments[i] );
|
||||
COMPARE( m_LabelSegments.size() );
|
||||
for( unsigned i=0; i<m_LabelSegments.size(); i++ )
|
||||
COMPARE( m_LabelSegments[i] );
|
||||
COMPARE( m_SpeedSegments.size() );
|
||||
for( unsigned i=0; i<m_SpeedSegments.size(); i++ )
|
||||
COMPARE( m_SpeedSegments[i] );
|
||||
COMPARE( m_FakeSegments.size() );
|
||||
for( unsigned i=0; i<m_FakeSegments.size(); i++ )
|
||||
COMPARE( m_FakeSegments[i] );
|
||||
COMPARE( m_fBeat0OffsetInSeconds );
|
||||
return true;
|
||||
}
|
||||
@@ -1317,6 +1760,11 @@ public:
|
||||
void InsertRows( int iStartRow, int iRowsToAdd );
|
||||
void DeleteRows( int iStartRow, int iRowsToDelete );
|
||||
|
||||
/**
|
||||
* @brief Tidy up the timing data, e.g. provide default BPMs, labels, tickcounts.
|
||||
*/
|
||||
void TidyUpData();
|
||||
|
||||
// Lua
|
||||
void PushSelf( lua_State *L );
|
||||
/**
|
||||
@@ -1354,6 +1802,10 @@ public:
|
||||
* @brief The collection of LabelSegments.
|
||||
*/
|
||||
vector<LabelSegment> m_LabelSegments;
|
||||
/** @brief The collection of SpeedSegments. */
|
||||
vector<SpeedSegment> m_SpeedSegments;
|
||||
/** @brief The collection of FakeSegments. */
|
||||
vector<FakeSegment> m_FakeSegments;
|
||||
/**
|
||||
* @brief The initial offset of a song.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user