[defaul -> Xcode4] We may need this sooner.
This commit is contained in:
+28
-3
@@ -18,6 +18,8 @@
|
||||
|
||||
static Preference<bool> g_bShowMasks("ShowMasks", false);
|
||||
|
||||
PlayerNumber Actor::m_ActivePlayerNumber = PLAYER_1;
|
||||
|
||||
/**
|
||||
* @brief Set up a hidden Actor that won't be drawn.
|
||||
*
|
||||
@@ -34,6 +36,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 +72,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 +160,7 @@ Actor::Actor()
|
||||
lua_setfield( L, -2, "ctx" );
|
||||
lua_pop( L, 1 );
|
||||
LUA->Release( L );
|
||||
|
||||
|
||||
|
||||
m_size = RageVector2( 1, 1 );
|
||||
InitState();
|
||||
m_pParent = NULL;
|
||||
@@ -302,6 +311,11 @@ void Actor::BeginDraw() // set the world matrix and calculate actor properties
|
||||
m_pTempState = &tempState;
|
||||
tempState = m_current;
|
||||
|
||||
// XXX HACK! We can't really determine the active player outside Draw() so
|
||||
// figure it out just for this clock type here.
|
||||
if( m_EffectClock == CLOCK_BGM_BEAT_PLAYER_ACTIVE )
|
||||
m_fSecsIntoEffect = g_vfCurrentBGMBeatPlayerNoOffset[m_ActivePlayerNumber];
|
||||
|
||||
const float fTotalPeriod = GetEffectPeriod();
|
||||
ASSERT( fTotalPeriod > 0 );
|
||||
const float fTimeIntoEffect = fmodfp( m_fSecsIntoEffect+m_fEffectOffset, fTotalPeriod );
|
||||
@@ -671,11 +685,22 @@ void Actor::UpdateInternal( float fDeltaTime )
|
||||
break;
|
||||
}
|
||||
|
||||
case CLOCK_BGM_BEAT_PLAYER_ACTIVE:
|
||||
case CLOCK_BGM_BEAT:
|
||||
m_fEffectDelta = g_fCurrentBGMBeat - m_fSecsIntoEffect;
|
||||
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;
|
||||
@@ -830,7 +855,7 @@ void Actor::SetEffectClockString( const RString &s )
|
||||
{
|
||||
if (s.EqualsNoCase("timer")) this->SetEffectClock( CLOCK_TIMER );
|
||||
if (s.EqualsNoCase("timerglobal")) this->SetEffectClock( CLOCK_TIMER_GLOBAL );
|
||||
else if(s.EqualsNoCase("beat")) this->SetEffectClock( CLOCK_BGM_BEAT );
|
||||
else if(s.EqualsNoCase("beat")) this->SetEffectClock( CLOCK_BGM_BEAT_PLAYER_ACTIVE );
|
||||
else if(s.EqualsNoCase("music")) this->SetEffectClock( CLOCK_BGM_TIME );
|
||||
else if(s.EqualsNoCase("bgm")) this->SetEffectClock( CLOCK_BGM_BEAT ); // compat, deprecated
|
||||
else if(s.EqualsNoCase("musicnooffset"))this->SetEffectClock( CLOCK_BGM_TIME_NO_OFFSET );
|
||||
|
||||
+14
@@ -1,6 +1,7 @@
|
||||
#ifndef ACTOR_H
|
||||
#define ACTOR_H
|
||||
|
||||
#include "PlayerNumber.h"
|
||||
#include "RageTypes.h"
|
||||
#include "RageUtil_AutoPtr.h"
|
||||
#include "LuaReference.h"
|
||||
@@ -109,8 +110,16 @@ 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 );
|
||||
|
||||
/**
|
||||
* @brief The actively-drawing player number. This is used as a hack
|
||||
* so that we don't need to tell each actor which player it belongs to.
|
||||
* This is used to figure out the right player for "beat" effect clock.
|
||||
*/
|
||||
static PlayerNumber m_ActivePlayerNumber;
|
||||
|
||||
/**
|
||||
* @brief The list of the different effects.
|
||||
*
|
||||
@@ -131,6 +140,9 @@ public:
|
||||
CLOCK_BGM_BEAT,
|
||||
CLOCK_BGM_TIME_NO_OFFSET,
|
||||
CLOCK_BGM_BEAT_NO_OFFSET,
|
||||
CLOCK_BGM_BEAT_PLAYER1,
|
||||
CLOCK_BGM_BEAT_PLAYER2,
|
||||
CLOCK_BGM_BEAT_PLAYER_ACTIVE,
|
||||
CLOCK_LIGHT_1 = 1000,
|
||||
CLOCK_LIGHT_LAST = 1100,
|
||||
NUM_CLOCKS
|
||||
@@ -713,6 +725,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
|
||||
|
||||
+3
-1
@@ -116,7 +116,9 @@ Actor* ActorUtil::LoadFromNode( const XNode* pNode, Actor *pParentActor )
|
||||
}
|
||||
|
||||
RString sClass;
|
||||
pNode->GetAttrValue( "Class", sClass );
|
||||
bool bHasClass = pNode->GetAttrValue( "Class", sClass );
|
||||
if( !bHasClass )
|
||||
bHasClass = pNode->GetAttrValue( "Type", sClass );
|
||||
|
||||
map<RString,CreateActorFn>::iterator iter = g_pmapRegistrees->find( sClass );
|
||||
if( iter == g_pmapRegistrees->end() )
|
||||
|
||||
+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 )
|
||||
|
||||
@@ -37,7 +37,7 @@ void AnnouncerManager::GetAnnouncerNames( vector<RString>& AddTo )
|
||||
|
||||
// strip out the empty announcer folder
|
||||
for( int i=AddTo.size()-1; i>=0; i-- )
|
||||
if( !stricmp( AddTo[i], EMPTY_ANNOUNCER_NAME ) )
|
||||
if( !AddTo[i].EqualsNoCase( EMPTY_ANNOUNCER_NAME ) )
|
||||
AddTo.erase(AddTo.begin()+i, AddTo.begin()+i+1 );
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ bool AnnouncerManager::DoesAnnouncerExist( RString sAnnouncerName )
|
||||
vector<RString> asAnnouncerNames;
|
||||
GetAnnouncerNames( asAnnouncerNames );
|
||||
for( unsigned i=0; i<asAnnouncerNames.size(); i++ )
|
||||
if( 0==stricmp(sAnnouncerName, asAnnouncerNames[i]) )
|
||||
if( sAnnouncerName.EqualsNoCase(asAnnouncerNames[i]) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -88,6 +88,18 @@ static const char *aliases[][2] = {
|
||||
{ "ScreenSelectStyle comment solo", "select style comment solo" },
|
||||
{ "ScreenSelectStyle comment versus", "select style comment versus" },
|
||||
|
||||
/* Combo compatibility: */
|
||||
{ "gameplay combo 100", "gameplay 100 combo" },
|
||||
{ "gameplay combo 200", "gameplay 200 combo" },
|
||||
{ "gameplay combo 300", "gameplay 300 combo" },
|
||||
{ "gameplay combo 400", "gameplay 400 combo" },
|
||||
{ "gameplay combo 500", "gameplay 500 combo" },
|
||||
{ "gameplay combo 600", "gameplay 600 combo" },
|
||||
{ "gameplay combo 700", "gameplay 700 combo" },
|
||||
{ "gameplay combo 800", "gameplay 800 combo" },
|
||||
{ "gameplay combo 900", "gameplay 900 combo" },
|
||||
{ "gameplay combo 1000", "gameplay 1000 combo" },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
+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 )
|
||||
|
||||
+22
-12
@@ -6,6 +6,7 @@
|
||||
#include "ActorUtil.h"
|
||||
#include "Foreach.h"
|
||||
#include "LuaManager.h"
|
||||
#include "PrefsManager.h"
|
||||
|
||||
REGISTER_ACTOR_CLASS(BGAnimation);
|
||||
|
||||
@@ -102,18 +103,27 @@ void BGAnimation::LoadFromAniDir( const RString &_sAniDir )
|
||||
|
||||
if( DoesFileExist(sPathToIni) )
|
||||
{
|
||||
// This is a 3.9-style BGAnimation (using .ini)
|
||||
IniFile ini;
|
||||
ini.ReadFile( sPathToIni );
|
||||
|
||||
AddLayersFromAniDir( sAniDir, &ini ); // TODO: Check for circular load
|
||||
|
||||
XNode* pBGAnimation = ini.GetChild( "BGAnimation" );
|
||||
XNode dummy( "BGAnimation" );
|
||||
if( pBGAnimation == NULL )
|
||||
pBGAnimation = &dummy;
|
||||
|
||||
LoadFromNode( pBGAnimation );
|
||||
if( PREFSMAN->m_bQuirksMode )
|
||||
{
|
||||
// This is a 3.9-style BGAnimation (using .ini)
|
||||
IniFile ini;
|
||||
ini.ReadFile( sPathToIni );
|
||||
|
||||
AddLayersFromAniDir( sAniDir, &ini ); // TODO: Check for circular load
|
||||
|
||||
XNode* pBGAnimation = ini.GetChild( "BGAnimation" );
|
||||
XNode dummy( "BGAnimation" );
|
||||
if( pBGAnimation == NULL )
|
||||
pBGAnimation = &dummy;
|
||||
|
||||
LoadFromNode( pBGAnimation );
|
||||
}
|
||||
else // We don't officially support .ini files anymore.
|
||||
{
|
||||
XNode dummy( "BGAnimation" );
|
||||
XNode *pBG = &dummy;
|
||||
LoadFromNode( pBG );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -388,30 +388,30 @@ void BGAnimationLayer::LoadFromNode( const XNode* pNode )
|
||||
pNode->GetAttrValue( "Stretch", bStretch );
|
||||
|
||||
// Check for string match first, then do integer match.
|
||||
// "if(atoi(type)==0)" was matching against all string matches.
|
||||
// "if(StringType(type)==0)" was matching against all string matches.
|
||||
// -Chris
|
||||
if( stricmp(type,"sprite")==0 )
|
||||
if( type.EqualsNoCase("sprite") )
|
||||
{
|
||||
m_Type = TYPE_SPRITE;
|
||||
}
|
||||
else if( stricmp(type,"particles")==0 )
|
||||
else if( type.EqualsNoCase("particles") )
|
||||
{
|
||||
m_Type = TYPE_PARTICLES;
|
||||
}
|
||||
else if( stricmp(type,"tiles")==0 )
|
||||
else if( type.EqualsNoCase("tiles") )
|
||||
{
|
||||
m_Type = TYPE_TILES;
|
||||
}
|
||||
else if( atoi(type) == 1 )
|
||||
else if( StringToInt(type) == 1 )
|
||||
{
|
||||
m_Type = TYPE_SPRITE;
|
||||
bStretch = true;
|
||||
}
|
||||
else if( atoi(type) == 2 )
|
||||
else if( StringToInt(type) == 2 )
|
||||
{
|
||||
m_Type = TYPE_PARTICLES;
|
||||
}
|
||||
else if( atoi(type) == 3 )
|
||||
else if( StringToInt(type) == 3 )
|
||||
{
|
||||
m_Type = TYPE_TILES;
|
||||
}
|
||||
|
||||
+17
-3
@@ -32,6 +32,7 @@ void BPMDisplay::Load()
|
||||
SET_EXTRA_COMMAND.Load( m_sName, "SetExtraCommand" );
|
||||
CYCLE.Load( m_sName, "Cycle" );
|
||||
RANDOM_CYCLE_SPEED.Load( m_sName, "RandomCycleSpeed" );
|
||||
COURSE_CYCLE_SPEED.Load( m_sName, "CourseCycleSpeed" );
|
||||
SEPARATOR.Load( m_sName, "Separator" );
|
||||
SHOW_QMARKS.Load( m_sName, "ShowQMarksInRandomCycle" );
|
||||
NO_BPM_TEXT.Load( m_sName, "NoBpmText" );
|
||||
@@ -204,9 +205,11 @@ void BPMDisplay::SetBpmFromCourse( const Course* pCourse )
|
||||
|
||||
StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType;
|
||||
Trail *pTrail = pCourse->GetTrail( st );
|
||||
ASSERT( pTrail );
|
||||
// GetTranslitFullTitle because "Crashinfo.txt is garbled because of the ANSI output as usual." -f
|
||||
ASSERT_M( pTrail, ssprintf("Course '%s' has no trail for StepsType '%s'", pCourse->GetTranslitFullTitle().c_str(), StringConversion::ToString(st).c_str() ) );
|
||||
|
||||
m_fCycleTime = 0.2f;
|
||||
// todo: let themers define this. -aj
|
||||
m_fCycleTime = (float)COURSE_CYCLE_SPEED;
|
||||
|
||||
if( (int)pTrail->m_vEntries.size() > CommonMetrics::MAX_COURSE_ENTRIES_BEFORE_VARIOUS )
|
||||
{
|
||||
@@ -276,7 +279,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;
|
||||
@@ -304,12 +307,23 @@ public:
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int SetFromCourse( T* p, lua_State *L )
|
||||
{
|
||||
if( lua_isnil(L,1) ) { p->NoBPM(); }
|
||||
else
|
||||
{
|
||||
const Course* pCourse = Luna<Course>::check( L, 1, true );
|
||||
p->SetBpmFromCourse(pCourse);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int GetText( T* p, lua_State *L ) { lua_pushstring( L, p->GetText() ); return 1; }
|
||||
|
||||
LunaBPMDisplay()
|
||||
{
|
||||
ADD_METHOD( SetFromGameState );
|
||||
ADD_METHOD( SetFromSong );
|
||||
ADD_METHOD( SetFromCourse );
|
||||
ADD_METHOD( GetText );
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,6 +82,7 @@ protected:
|
||||
ThemeMetric<bool> SHOW_QMARKS;
|
||||
/** @brief How often the random BPMs cycle themselves. */
|
||||
ThemeMetric<float> RANDOM_CYCLE_SPEED;
|
||||
ThemeMetric<float> COURSE_CYCLE_SPEED;
|
||||
/** @brief The text used to separate the low and high BPMs. */
|
||||
ThemeMetric<RString> SEPARATOR;
|
||||
/** @brief The text used when there is no BPM. */
|
||||
|
||||
+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()
|
||||
|
||||
@@ -133,7 +133,8 @@ void BackgroundUtil::GetBackgroundTransitions( const RString &_sName, vector<RSt
|
||||
sName = "*";
|
||||
|
||||
vsPathsOut.clear();
|
||||
GetDirListing( BACKGROUND_TRANSITIONS_DIR+sName+".xml", vsPathsOut, false, true );
|
||||
if( true )
|
||||
GetDirListing( BACKGROUND_TRANSITIONS_DIR+sName+".xml", vsPathsOut, false, true );
|
||||
GetDirListing( BACKGROUND_TRANSITIONS_DIR+sName+".lua", vsPathsOut, false, true );
|
||||
|
||||
vsNamesOut.clear();
|
||||
@@ -241,7 +242,8 @@ void BackgroundUtil::GetGlobalBGAnimations( const Song *pSong, const RString &sM
|
||||
{
|
||||
vsPathsOut.clear();
|
||||
GetDirListing( BG_ANIMS_DIR+sMatch+"*", vsPathsOut, true, true );
|
||||
GetDirListing( BG_ANIMS_DIR+sMatch+"*.xml", vsPathsOut, false, true );
|
||||
if( true )
|
||||
GetDirListing( BG_ANIMS_DIR+sMatch+"*.xml", vsPathsOut, false, true );
|
||||
|
||||
vsNamesOut.clear();
|
||||
FOREACH_CONST( RString, vsPathsOut, s )
|
||||
|
||||
+4
-4
@@ -17,8 +17,8 @@ REGISTER_ACTOR_CLASS( Banner );
|
||||
|
||||
ThemeMetric<bool> SCROLL_RANDOM ("Banner","ScrollRandom");
|
||||
ThemeMetric<bool> SCROLL_ROULETTE ("Banner","ScrollRoulette");
|
||||
//ThemeMetric<bool> SCROLL_MODE ("Banner","ScrollMode");
|
||||
//ThemeMetric<bool> SCROLL_SORT_ORDER ("Banner","ScrollSortOrder");
|
||||
ThemeMetric<bool> SCROLL_MODE ("Banner","ScrollMode");
|
||||
ThemeMetric<bool> SCROLL_SORT_ORDER ("Banner","ScrollSortOrder");
|
||||
ThemeMetric<float> SCROLL_SPEED_DIVISOR ("Banner","ScrollSpeedDivisor");
|
||||
|
||||
Banner::Banner()
|
||||
@@ -119,7 +119,7 @@ void Banner::LoadFromSong( Song* pSong ) // NULL means no song
|
||||
void Banner::LoadMode()
|
||||
{
|
||||
Load( THEME->GetPathG("Banner","Mode") );
|
||||
m_bScrolling = false;
|
||||
m_bScrolling = (bool)SCROLL_MODE;
|
||||
}
|
||||
|
||||
void Banner::LoadFromSongGroup( RString sSongGroup )
|
||||
@@ -229,7 +229,7 @@ void Banner::LoadFromSortOrder( SortOrder so )
|
||||
if( so != SORT_GROUP && so != SORT_RECENT )
|
||||
Load( THEME->GetPathG("Banner",ssprintf("%s",SortOrderToString(so).c_str())) );
|
||||
}
|
||||
m_bScrolling = false;
|
||||
m_bScrolling = (bool)SCROLL_SORT_ORDER;
|
||||
}
|
||||
|
||||
// lua start
|
||||
|
||||
@@ -293,23 +293,14 @@ RageTextureID BannerCache::LoadCachedBanner( RString sBannerPath )
|
||||
ASSERT( pImage );
|
||||
|
||||
int iSourceWidth = 0, iSourceHeight = 0;
|
||||
bool bWasRotatedBanner = false;
|
||||
BannerData.GetValue( sBannerPath, "Width", iSourceWidth );
|
||||
BannerData.GetValue( sBannerPath, "Height", iSourceHeight );
|
||||
BannerData.GetValue( sBannerPath, "Rotated", bWasRotatedBanner );
|
||||
if( iSourceWidth == 0 || iSourceHeight == 0 )
|
||||
{
|
||||
LOG->UserLog( "Cache file", sBannerPath, "couldn't be loaded." );
|
||||
return ID;
|
||||
}
|
||||
|
||||
if( bWasRotatedBanner )
|
||||
{
|
||||
/* We need to tell Sprite that this was originally a rotated
|
||||
* sprite. */
|
||||
ID.filename += "(was rotated)";
|
||||
}
|
||||
|
||||
/* Is the banner already in a texture? */
|
||||
if( TEXTUREMAN->IsTextureRegistered(ID) )
|
||||
return ID; /* It's all set. */
|
||||
@@ -383,50 +374,6 @@ void BannerCache::CacheBannerInternal( RString sBannerPath )
|
||||
return;
|
||||
}
|
||||
|
||||
bool bWasRotatedBanner = false;
|
||||
|
||||
if( Sprite::IsDiagonalBanner(pImage->w , pImage->h) )
|
||||
{
|
||||
/* Ack. It's a diagonal banner. Problem: if we resize a diagonal banner, we
|
||||
* get ugly checker patterns. We need to un-rotate it.
|
||||
*
|
||||
* If we spin the banner by hand, we need to do a linear filter, or the
|
||||
* fade to the full resolution banner is misaligned, which looks strange.
|
||||
*
|
||||
* To do a linear filter, we need to lose the palette. Oh well.
|
||||
*
|
||||
* This also makes the banner take less memory, though that could also be
|
||||
* done by RLEing the surface.
|
||||
*/
|
||||
//RageSurfaceUtils::ApplyHotPinkColorKey( pImage );
|
||||
|
||||
RageSurfaceUtils::ConvertSurface(pImage, pImage->w, pImage->h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
|
||||
|
||||
RageSurface *dst = CreateSurface(
|
||||
256, 64, pImage->format->BitsPerPixel,
|
||||
pImage->format->Rmask, pImage->format->Gmask, pImage->format->Bmask, pImage->format->Amask );
|
||||
|
||||
if( pImage->format->BitsPerPixel == 8 )
|
||||
{
|
||||
ASSERT( pImage->format->palette );
|
||||
dst->fmt.palette = pImage->fmt.palette;
|
||||
}
|
||||
|
||||
const float fCustomImageCoords[8] = {
|
||||
0.02f, 0.78f, // top left
|
||||
0.22f, 0.98f, // bottom left
|
||||
0.98f, 0.22f, // bottom right
|
||||
0.78f, 0.02f, // top right
|
||||
};
|
||||
|
||||
RageSurfaceUtils::BlitTransform( pImage, dst, fCustomImageCoords );
|
||||
|
||||
delete pImage;
|
||||
pImage = dst;
|
||||
|
||||
bWasRotatedBanner = true;
|
||||
}
|
||||
|
||||
const int iSourceWidth = pImage->w, iSourceHeight = pImage->h;
|
||||
|
||||
int iWidth = pImage->w / 2, iHeight = pImage->h / 2;
|
||||
@@ -504,8 +451,6 @@ void BannerCache::CacheBannerInternal( RString sBannerPath )
|
||||
BannerData.SetValue( sBannerPath, "Width", iSourceWidth );
|
||||
BannerData.SetValue( sBannerPath, "Height", iSourceHeight );
|
||||
BannerData.SetValue( sBannerPath, "FullHash", GetHashForFile( sBannerPath ) );
|
||||
/* Remember this, so we can hint Sprite. */
|
||||
BannerData.SetValue( sBannerPath, "Rotated", bWasRotatedBanner );
|
||||
BannerData.WriteFile( BANNER_CACHE_INDEX );
|
||||
}
|
||||
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
+2
-2
@@ -90,8 +90,8 @@ int CourseEntry::GetNumModChanges() const
|
||||
|
||||
Course::Course(): m_bIsAutogen(false), m_sPath(""), m_sMainTitle(""),
|
||||
m_sMainTitleTranslit(""), m_sSubTitle(""), m_sSubTitleTranslit(""),
|
||||
m_sBannerPath(""), m_sBackgroundPath(""), m_sCDTitlePath(""),
|
||||
m_sGroupName(""), m_sScripter(""), m_bRepeat(false), m_fGoalSeconds(0),
|
||||
m_sScripter(""), m_sBannerPath(""), m_sBackgroundPath(""),
|
||||
m_sCDTitlePath(""), m_sGroupName(""), m_bRepeat(false), m_fGoalSeconds(0),
|
||||
m_bShuffle(false), m_iLives(-1), m_bSortByMeter(false),
|
||||
m_bIncomplete(false), m_vEntries(), m_SortOrder_TotalDifficulty(0),
|
||||
m_SortOrder_Ranking(0), m_LoadedFromProfile(ProfileSlot_Invalid),
|
||||
|
||||
+25
-25
@@ -61,13 +61,13 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
const MsdFile::value_t &sParams = msd.GetValue(i);
|
||||
|
||||
// handle the data
|
||||
if( 0 == stricmp(sValueName, "COURSE") )
|
||||
if( sValueName.EqualsNoCase("COURSE") )
|
||||
out.m_sMainTitle = sParams[1];
|
||||
else if( 0 == stricmp(sValueName, "COURSETRANSLIT") )
|
||||
else if( sValueName.EqualsNoCase("COURSETRANSLIT") )
|
||||
out.m_sMainTitleTranslit = sParams[1];
|
||||
else if( 0 == stricmp(sValueName, "SCRIPTER") )
|
||||
else if( sValueName.EqualsNoCase("SCRIPTER") )
|
||||
out.m_sScripter = sParams[1];
|
||||
else if( 0 == stricmp(sValueName, "REPEAT") )
|
||||
else if( sValueName.EqualsNoCase("REPEAT") )
|
||||
{
|
||||
RString str = sParams[1];
|
||||
str.MakeLower();
|
||||
@@ -75,27 +75,27 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
out.m_bRepeat = true;
|
||||
}
|
||||
|
||||
else if( 0 == stricmp(sValueName, "BANNER") )
|
||||
else if( sValueName.EqualsNoCase("BANNER") )
|
||||
{
|
||||
out.m_sBannerPath = sParams[1];
|
||||
}
|
||||
else if( 0 == stricmp(sValueName, "BACKGROUND") )
|
||||
else if( sValueName.EqualsNoCase("BACKGROUND") )
|
||||
{
|
||||
out.m_sBackgroundPath = sParams[1];
|
||||
}
|
||||
else if( 0 == stricmp(sValueName, "LIVES") )
|
||||
else if( sValueName.EqualsNoCase("LIVES") )
|
||||
{
|
||||
out.m_iLives = max( atoi(sParams[1]), 0 );
|
||||
out.m_iLives = max( StringToInt(sParams[1]), 0 );
|
||||
}
|
||||
else if( 0 == stricmp(sValueName, "GAINSECONDS") )
|
||||
else if( sValueName.EqualsNoCase("GAINSECONDS") )
|
||||
{
|
||||
fGainSeconds = StringToFloat( sParams[1] );
|
||||
}
|
||||
else if( 0 == stricmp(sValueName, "METER") )
|
||||
else if( sValueName.EqualsNoCase("METER") )
|
||||
{
|
||||
if( sParams.params.size() == 2 )
|
||||
{
|
||||
out.m_iCustomMeter[Difficulty_Medium] = max( atoi(sParams[1]), 0 ); /* compat */
|
||||
out.m_iCustomMeter[Difficulty_Medium] = max( StringToInt(sParams[1]), 0 ); /* compat */
|
||||
}
|
||||
else if( sParams.params.size() == 3 )
|
||||
{
|
||||
@@ -105,12 +105,12 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
LOG->UserLog( "Course file", sPath, "contains an invalid #METER string: \"%s\"", sParams[1].c_str() );
|
||||
continue;
|
||||
}
|
||||
out.m_iCustomMeter[cd] = max( atoi(sParams[2]), 0 );
|
||||
out.m_iCustomMeter[cd] = max( StringToInt(sParams[2]), 0 );
|
||||
}
|
||||
}
|
||||
// todo: add COMBO and COMBOMODE from DWI CRS files? -aj
|
||||
|
||||
else if( 0 == stricmp(sValueName, "MODS") )
|
||||
else if( sValueName.EqualsNoCase("MODS") )
|
||||
{
|
||||
Attack attack;
|
||||
float end = -9999;
|
||||
@@ -156,7 +156,7 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
}
|
||||
|
||||
}
|
||||
else if( 0 == stricmp(sValueName, "SONG") )
|
||||
else if( sValueName.EqualsNoCase("SONG") )
|
||||
{
|
||||
CourseEntry new_entry;
|
||||
|
||||
@@ -167,28 +167,28 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
// most played
|
||||
if( sParams[1].Left(strlen("BEST")) == "BEST" )
|
||||
{
|
||||
new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("BEST")) ) - 1;
|
||||
new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("BEST")) ) - 1;
|
||||
CLAMP( new_entry.iChooseIndex, 0, 500 );
|
||||
new_entry.songSort = SongSort_MostPlays;
|
||||
}
|
||||
// least played
|
||||
else if( sParams[1].Left(strlen("WORST")) == "WORST" )
|
||||
{
|
||||
new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("WORST")) ) - 1;
|
||||
new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("WORST")) ) - 1;
|
||||
CLAMP( new_entry.iChooseIndex, 0, 500 );
|
||||
new_entry.songSort = SongSort_FewestPlays;
|
||||
}
|
||||
// best grades
|
||||
else if( sParams[1].Left(strlen("GRADEBEST")) == "GRADEBEST" )
|
||||
{
|
||||
new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("GRADEBEST")) ) - 1;
|
||||
new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("GRADEBEST")) ) - 1;
|
||||
CLAMP( new_entry.iChooseIndex, 0, 500 );
|
||||
new_entry.songSort = SongSort_TopGrades;
|
||||
}
|
||||
// worst grades
|
||||
else if( sParams[1].Left(strlen("GRADEWORST")) == "GRADEWORST" )
|
||||
{
|
||||
new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("GRADEWORST")) ) - 1;
|
||||
new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("GRADEWORST")) ) - 1;
|
||||
CLAMP( new_entry.iChooseIndex, 0, 500 );
|
||||
new_entry.songSort = SongSort_LowestGrades;
|
||||
}
|
||||
@@ -284,7 +284,7 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
else if( !sMod.CompareNoCase("nodifficult") )
|
||||
new_entry.bNoDifficult = true;
|
||||
else if( sMod.length() > 5 && !sMod.Left(5).CompareNoCase("award") )
|
||||
new_entry.iGainLives = atoi( sMod.substr(5).c_str() );
|
||||
new_entry.iGainLives = StringToInt( sMod.substr(5) );
|
||||
else
|
||||
continue;
|
||||
mods.erase( mods.begin() + j );
|
||||
@@ -298,22 +298,22 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
|
||||
out.m_vEntries.push_back( new_entry );
|
||||
}
|
||||
else if( !stricmp(sValueName, "DISPLAYCOURSE") || !stricmp(sValueName, "COMBO") ||
|
||||
!stricmp(sValueName, "COMBOMODE") )
|
||||
else if( !sValueName.EqualsNoCase("DISPLAYCOURSE") || !sValueName.EqualsNoCase("COMBO") ||
|
||||
!sValueName.EqualsNoCase("COMBOMODE") )
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
else if( bFromCache && !stricmp(sValueName, "RADAR") )
|
||||
else if( bFromCache && !sValueName.EqualsNoCase("RADAR") )
|
||||
{
|
||||
StepsType st = (StepsType) atoi(sParams[1]);
|
||||
CourseDifficulty cd = (CourseDifficulty) atoi( sParams[2] );
|
||||
StepsType st = (StepsType) StringToInt(sParams[1]);
|
||||
CourseDifficulty cd = (CourseDifficulty) StringToInt( sParams[2] );
|
||||
|
||||
RadarValues rv;
|
||||
rv.FromString( sParams[3] );
|
||||
out.m_RadarCache[Course::CacheEntry(st, cd)] = rv;
|
||||
}
|
||||
else if( 0 == stricmp(sValueName, "STYLE") )
|
||||
else if( sValueName.EqualsNoCase("STYLE") )
|
||||
{
|
||||
RString sStyles = sParams[1];
|
||||
vector<RString> asStyles;
|
||||
|
||||
@@ -404,6 +404,23 @@ RString CryptManager::GetSHA1ForString( RString sData )
|
||||
return RString( (const char *) digest, sizeof(digest) );
|
||||
}
|
||||
|
||||
RString CryptManager::GetSHA1ForFile( RString fn )
|
||||
{
|
||||
RageFile file;
|
||||
if( !file.Open( fn, RageFile::READ ) )
|
||||
{
|
||||
LOG->Warn( "GetSHA1: Failed to open file '%s'", fn.c_str() );
|
||||
return RString();
|
||||
}
|
||||
int iHash = register_hash( &sha1_desc );
|
||||
ASSERT( iHash >= 0 );
|
||||
|
||||
unsigned char digest[20];
|
||||
HashFile( file, digest, iHash );
|
||||
|
||||
return RString( (const char *) digest, sizeof(digest) );
|
||||
}
|
||||
|
||||
RString CryptManager::GetPublicKeyFileName()
|
||||
{
|
||||
return PUBLIC_KEY_PATH;
|
||||
@@ -455,12 +472,20 @@ public:
|
||||
lua_pushstring( L, sha1out );
|
||||
return 1;
|
||||
}
|
||||
static int SHA1File( T* p, lua_State *L )
|
||||
{
|
||||
RString sha1fout;
|
||||
sha1fout = p->GetSHA1ForFile(SArg(1));
|
||||
lua_pushstring( L, sha1fout );
|
||||
return 1;
|
||||
}
|
||||
|
||||
LunaCryptManager()
|
||||
{
|
||||
ADD_METHOD( MD5String );
|
||||
ADD_METHOD( MD5File );
|
||||
ADD_METHOD( SHA1String );
|
||||
ADD_METHOD( SHA1File );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
static RString GetMD5ForFile( RString fn ); // in binary
|
||||
static RString GetMD5ForString( RString sData ); // in binary
|
||||
static RString GetSHA1ForString( RString sData ); // in binary
|
||||
static RString GetSHA1ForFile( RString fn ); // in binary
|
||||
|
||||
static RString GetPublicKeyFileName();
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -21,35 +21,6 @@ XToString( Difficulty );
|
||||
StringToX( Difficulty );
|
||||
LuaXType( Difficulty );
|
||||
|
||||
/* We prefer the above names; recognize a number of others, too. (They'll get
|
||||
* normalized when written to SMs, etc.) TODO: Format specific hacks should be
|
||||
* moved into the file loader for that format. We don't want to carry these
|
||||
* hacks forward to file formats that don't need them. */
|
||||
Difficulty DwiCompatibleStringToDifficulty( const RString& sDC )
|
||||
{
|
||||
RString s2 = sDC;
|
||||
s2.MakeLower();
|
||||
if( s2 == "beginner" ) return Difficulty_Beginner;
|
||||
else if( s2 == "easy" ) return Difficulty_Easy;
|
||||
else if( s2 == "basic" ) return Difficulty_Easy;
|
||||
else if( s2 == "light" ) return Difficulty_Easy;
|
||||
else if( s2 == "medium" ) return Difficulty_Medium;
|
||||
else if( s2 == "another" ) return Difficulty_Medium;
|
||||
else if( s2 == "trick" ) return Difficulty_Medium;
|
||||
else if( s2 == "standard" ) return Difficulty_Medium;
|
||||
else if( s2 == "difficult") return Difficulty_Medium;
|
||||
else if( s2 == "hard" ) return Difficulty_Hard;
|
||||
else if( s2 == "ssr" ) return Difficulty_Hard;
|
||||
else if( s2 == "maniac" ) return Difficulty_Hard;
|
||||
else if( s2 == "heavy" ) return Difficulty_Hard;
|
||||
else if( s2 == "smaniac" ) return Difficulty_Challenge;
|
||||
else if( s2 == "challenge" ) return Difficulty_Challenge;
|
||||
else if( s2 == "expert" ) return Difficulty_Challenge;
|
||||
else if( s2 == "oni" ) return Difficulty_Challenge;
|
||||
else if( s2 == "edit" ) return Difficulty_Edit;
|
||||
else return Difficulty_Invalid;
|
||||
}
|
||||
|
||||
const RString &CourseDifficultyToLocalizedString( CourseDifficulty x )
|
||||
{
|
||||
static auto_ptr<LocalizedString> g_CourseDifficultyName[NUM_Difficulty];
|
||||
|
||||
@@ -22,8 +22,6 @@ const RString& DifficultyToString( Difficulty dc );
|
||||
Difficulty StringToDifficulty( const RString& sDC );
|
||||
LuaDeclareType( Difficulty );
|
||||
|
||||
Difficulty DwiCompatibleStringToDifficulty( const RString& sDC );
|
||||
|
||||
typedef Difficulty CourseDifficulty;
|
||||
const int NUM_CourseDifficulty = NUM_Difficulty;
|
||||
/** @brief Loop through the shown course difficulties. */
|
||||
|
||||
+1
-1
@@ -386,7 +386,7 @@ void EditMenu::OnRowValueChanged( EditMenuRow row )
|
||||
dcOld = GetSelectedDifficulty();
|
||||
|
||||
m_vpSteps.clear();
|
||||
|
||||
|
||||
FOREACH_ENUM( Difficulty, dc )
|
||||
{
|
||||
if( dc == Difficulty_Edit )
|
||||
|
||||
@@ -272,14 +272,14 @@ void FileTransfer::HTTPUpdate()
|
||||
m_sResponseName = "Malformed response.";
|
||||
return;
|
||||
}
|
||||
m_iResponseCode = atoi(m_sBUFFER.substr(i+1,j-i).c_str());
|
||||
m_iResponseCode = StringToInt(m_sBUFFER.substr(i+1,j-i));
|
||||
m_sResponseName = m_sBUFFER.substr( j+1, k-j );
|
||||
|
||||
i = m_sBUFFER.find("Content-Length:");
|
||||
j = m_sBUFFER.find("\n", i+1 );
|
||||
|
||||
if( i != string::npos )
|
||||
m_iTotalBytes = atoi(m_sBUFFER.substr(i+16,j-i).c_str());
|
||||
m_iTotalBytes = StringToInt(m_sBUFFER.substr(i+16,j-i));
|
||||
else
|
||||
m_iTotalBytes = -1; // We don't know, so go until disconnect
|
||||
|
||||
@@ -350,7 +350,7 @@ bool FileTransfer::ParseHTTPAddress( const RString &URL, RString &sProto, RStrin
|
||||
sServer = asMatches[1];
|
||||
if( asMatches[3] != "" )
|
||||
{
|
||||
iPort = atoi(asMatches[3]);
|
||||
iPort = StringToInt(asMatches[3]);
|
||||
if( iPort == 0 )
|
||||
return false;
|
||||
}
|
||||
|
||||
+2
-2
@@ -414,7 +414,7 @@ void Font::LoadFontPageSettings( FontPageSettings &cfg, IniFile &ini, const RStr
|
||||
// If val is an integer, it's a width, eg. "10=27".
|
||||
if( IsAnInt(sName) )
|
||||
{
|
||||
cfg.m_mapGlyphWidths[atoi(sName)] = pValue->GetValue<int>();
|
||||
cfg.m_mapGlyphWidths[StringToInt(sName)] = pValue->GetValue<int>();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -516,7 +516,7 @@ void Font::LoadFontPageSettings( FontPageSettings &cfg, IniFile &ini, const RStr
|
||||
TrimLeft( sRowStr );
|
||||
|
||||
ASSERT( IsAnInt(sRowStr) );
|
||||
const int iRow = atoi( sRowStr.c_str() );
|
||||
const int iRow = StringToInt( sRowStr );
|
||||
const int iFirstFrame = iRow * iNumFramesWide;
|
||||
|
||||
if( iRow > iNumFramesHigh )
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+2
-2
@@ -335,12 +335,12 @@ void GameCommand::LoadOne( const Command& cmd )
|
||||
|
||||
else if( sName == "weight" )
|
||||
{
|
||||
m_iWeightPounds = atoi( sValue );
|
||||
m_iWeightPounds = StringToInt( sValue );
|
||||
}
|
||||
|
||||
else if( sName == "goalcalories" )
|
||||
{
|
||||
m_iGoalCalories = atoi( sValue );
|
||||
m_iGoalCalories = StringToInt( sValue );
|
||||
}
|
||||
|
||||
else if( sName == "goaltype" )
|
||||
|
||||
@@ -87,8 +87,10 @@ enum StepsType
|
||||
StepsType_para_single,
|
||||
StepsType_ds3ddx_single,
|
||||
StepsType_beat_single5,
|
||||
StepsType_beat_versus5,
|
||||
StepsType_beat_double5,
|
||||
StepsType_beat_single7,
|
||||
StepsType_beat_versus7,
|
||||
StepsType_beat_double7,
|
||||
StepsType_maniax_single,
|
||||
StepsType_maniax_double,
|
||||
|
||||
+91
-2
@@ -69,8 +69,10 @@ static const StepsTypeInfo g_StepsTypeInfos[] = {
|
||||
{ "ds3ddx-single", 8, true, StepsTypeCategory_Single },
|
||||
// beatmania
|
||||
{ "bm-single5", 6, true, StepsTypeCategory_Single }, // called "bm" for backward compat
|
||||
{ "bm-versus5", 6, true, StepsTypeCategory_Single }, // called "bm" for backward compat
|
||||
{ "bm-double5", 12, true, StepsTypeCategory_Double }, // called "bm" for backward compat
|
||||
{ "bm-single7", 8, true, StepsTypeCategory_Single }, // called "bm" for backward compat
|
||||
{ "bm-versus7", 8, true, StepsTypeCategory_Single }, // called "bm" for backward compat
|
||||
{ "bm-double7", 16, true, StepsTypeCategory_Double }, // called "bm" for backward compat
|
||||
// dance maniax
|
||||
{ "maniax-single", 4, true, StepsTypeCategory_Single },
|
||||
@@ -1584,7 +1586,47 @@ static const Style g_Style_Beat_Single5 =
|
||||
false, // m_bLockDifficulties
|
||||
};
|
||||
|
||||
static const Style g_Style_Beat_Double =
|
||||
static const Style g_Style_Beat_Versus5 =
|
||||
{ // STYLE_BEAT_VERSUS
|
||||
true, // m_bUsedForGameplay
|
||||
false, // m_bUsedForEdit
|
||||
true, // m_bUsedForDemonstration
|
||||
false, // m_bUsedForHowToPlay
|
||||
"versus", // m_szName
|
||||
StepsType_beat_versus5, // m_StepsType
|
||||
StyleType_TwoPlayersTwoSides, // m_StyleType
|
||||
6, // m_iColsPerPlayer
|
||||
{ // m_ColumnInfo[NUM_PLAYERS][MAX_COLS_PER_PLAYER];
|
||||
{ // PLAYER_1
|
||||
{ TRACK_1, -BEAT_COL_SPACING*2.5f, NULL },
|
||||
{ TRACK_2, -BEAT_COL_SPACING*1.5f, NULL },
|
||||
{ TRACK_3, -BEAT_COL_SPACING*0.5f, NULL },
|
||||
{ TRACK_4, +BEAT_COL_SPACING*0.5f, NULL },
|
||||
{ TRACK_5, +BEAT_COL_SPACING*1.5f, NULL },
|
||||
{ TRACK_6, +BEAT_COL_SPACING*3.0f, "scratch" },
|
||||
},
|
||||
{ // PLAYER_2
|
||||
{ TRACK_1, -BEAT_COL_SPACING*2.5f, NULL },
|
||||
{ TRACK_2, -BEAT_COL_SPACING*1.5f, NULL },
|
||||
{ TRACK_3, -BEAT_COL_SPACING*0.5f, NULL },
|
||||
{ TRACK_4, +BEAT_COL_SPACING*0.5f, NULL },
|
||||
{ TRACK_5, +BEAT_COL_SPACING*1.5f, NULL },
|
||||
{ TRACK_6, +BEAT_COL_SPACING*3.0f, "scratch" },
|
||||
},
|
||||
},
|
||||
{ // m_iInputColumn[NUM_GameController][NUM_GameButton]
|
||||
{ 0, 1, 2, 3, 4, Style::NO_MAPPING, Style::NO_MAPPING, 5, 5, Style::END_MAPPING },
|
||||
{ 0, 1, 2, 3, 4, Style::NO_MAPPING, Style::NO_MAPPING, 5, 5, Style::END_MAPPING }
|
||||
},
|
||||
{ // m_iColumnDrawOrder[MAX_COLS_PER_PLAYER];
|
||||
0,1,2,3,4,5
|
||||
},
|
||||
false, // m_bNeedsZoomOutWith2Players
|
||||
false, // m_bCanUseBeginnerHelper
|
||||
false, // m_bLockDifficulties
|
||||
};
|
||||
|
||||
static const Style g_Style_Beat_Double5 =
|
||||
{ // STYLE_BEAT_DOUBLE
|
||||
true, // m_bUsedForGameplay
|
||||
true, // m_bUsedForEdit
|
||||
@@ -1680,6 +1722,51 @@ static const Style g_Style_Beat_Single7 =
|
||||
false, // m_bLockDifficulties
|
||||
};
|
||||
|
||||
static const Style g_Style_Beat_Versus7 =
|
||||
{ // STYLE_BEAT_VERSUS7
|
||||
true, // m_bUsedForGameplay
|
||||
true, // m_bUsedForEdit
|
||||
false, // m_bUsedForDemonstration
|
||||
false, // m_bUsedForHowToPlay
|
||||
"single7", // m_szName
|
||||
StepsType_beat_versus7, // m_StepsType
|
||||
StyleType_TwoPlayersTwoSides, // m_StyleType
|
||||
8, // m_iColsPerPlayer
|
||||
{ // m_ColumnInfo[NUM_PLAYERS][MAX_COLS_PER_PLAYER];
|
||||
{ // PLAYER_1
|
||||
{ TRACK_8, -BEAT_COL_SPACING*3.5f, "scratch" },
|
||||
{ TRACK_1, -BEAT_COL_SPACING*2.0f, NULL },
|
||||
{ TRACK_2, -BEAT_COL_SPACING*1.0f, NULL },
|
||||
{ TRACK_3, -BEAT_COL_SPACING*0.0f, NULL },
|
||||
{ TRACK_4, +BEAT_COL_SPACING*1.0f, NULL },
|
||||
{ TRACK_5, +BEAT_COL_SPACING*2.0f, NULL },
|
||||
{ TRACK_6, +BEAT_COL_SPACING*3.0f, NULL },
|
||||
{ TRACK_7, +BEAT_COL_SPACING*4.0f, NULL },
|
||||
},
|
||||
{ // PLAYER_2
|
||||
{ TRACK_1, -BEAT_COL_SPACING*3.5f, NULL },
|
||||
{ TRACK_2, -BEAT_COL_SPACING*2.5f, NULL },
|
||||
{ TRACK_3, -BEAT_COL_SPACING*1.5f, NULL },
|
||||
{ TRACK_4, -BEAT_COL_SPACING*0.5f, NULL },
|
||||
{ TRACK_5, +BEAT_COL_SPACING*0.5f, NULL },
|
||||
{ TRACK_6, +BEAT_COL_SPACING*1.5f, NULL },
|
||||
{ TRACK_7, +BEAT_COL_SPACING*2.5f, NULL },
|
||||
{ TRACK_8, +BEAT_COL_SPACING*4.0f, "scratch" },
|
||||
},
|
||||
},
|
||||
{ // m_iInputColumn[NUM_GameController][NUM_GameButton]
|
||||
{ 1, 2, 3, 4, 5, 6, 7, 0, 0, Style::END_MAPPING },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 7, Style::END_MAPPING },
|
||||
},
|
||||
{ // m_iColumnDrawOrder[MAX_COLS_PER_PLAYER];
|
||||
0,1,2,3,4,5,6,7
|
||||
},
|
||||
false, // m_bNeedsZoomOutWith2Players
|
||||
false, // m_bCanUseBeginnerHelper
|
||||
false, // m_bLockDifficulties
|
||||
};
|
||||
|
||||
|
||||
static const Style g_Style_Beat_Double7 =
|
||||
{ // STYLE_BEAT_DOUBLE7
|
||||
true, // m_bUsedForGameplay
|
||||
@@ -1743,8 +1830,10 @@ static const Style g_Style_Beat_Double7 =
|
||||
static const Style *g_apGame_Beat_Styles[] =
|
||||
{
|
||||
&g_Style_Beat_Single5,
|
||||
&g_Style_Beat_Double,
|
||||
&g_Style_Beat_Versus5,
|
||||
&g_Style_Beat_Double5,
|
||||
&g_Style_Beat_Single7,
|
||||
&g_Style_Beat_Versus7,
|
||||
&g_Style_Beat_Double7,
|
||||
NULL
|
||||
};
|
||||
|
||||
+16
-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;
|
||||
@@ -806,12 +806,14 @@ public:
|
||||
return 0;
|
||||
}
|
||||
static int PlayOnce( T* p, lua_State *L ) { RString sPath = SArg(1); p->PlayOnce( sPath ); return 0; }
|
||||
static int PlayAnnouncer( T* p, lua_State *L ) { RString sPath = SArg(1); p->PlayOnceFromAnnouncer( sPath ); return 0; }
|
||||
static int GetPlayerBalance( T* p, lua_State *L ) { PlayerNumber pn = Enum::Check<PlayerNumber>(L, 1); lua_pushnumber( L, p->GetPlayerBalance(pn) ); return 1; }
|
||||
|
||||
LunaGameSoundManager()
|
||||
{
|
||||
ADD_METHOD( DimMusic );
|
||||
ADD_METHOD( PlayOnce );
|
||||
ADD_METHOD( PlayAnnouncer );
|
||||
ADD_METHOD( GetPlayerBalance );
|
||||
}
|
||||
};
|
||||
|
||||
+34
-67
@@ -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;
|
||||
|
||||
@@ -209,7 +211,7 @@ void GameState::ApplyCmdline()
|
||||
RString sPlayer;
|
||||
for( int i = 0; GetCommandlineArgument( "player", &sPlayer, i ); ++i )
|
||||
{
|
||||
int pn = atoi( sPlayer )-1;
|
||||
int pn = StringToInt( sPlayer )-1;
|
||||
if( !IsAnInt( sPlayer ) || pn < 0 || pn >= NUM_PLAYERS )
|
||||
RageException::Throw( "Invalid argument \"--player=%s\".", sPlayer.c_str() );
|
||||
|
||||
@@ -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
@@ -948,7 +948,7 @@ MultiPlayer InputMapper::InputDeviceToMultiPlayer( InputDevice id )
|
||||
GameButton InputScheme::ButtonNameToIndex( const RString &sButtonName ) const
|
||||
{
|
||||
for( GameButton gb=(GameButton) 0; gb<m_iButtonsPerController; gb=(GameButton)(gb+1) )
|
||||
if( stricmp(GetGameButtonName(gb), sButtonName) == 0 )
|
||||
if( sButtonName.EqualsNoCase(GetGameButtonName(gb)) )
|
||||
return gb;
|
||||
|
||||
return GameButton_Invalid;
|
||||
|
||||
+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);
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "arch/Dialog/Dialog.h"
|
||||
#include "../extern/jsoncpp/include/json/reader.h"
|
||||
#include "../extern/jsoncpp/include/json/writer.h"
|
||||
#include "json/reader.h"
|
||||
#include "json/writer.h"
|
||||
|
||||
bool JsonUtil::LoadFromString(Json::Value &root, RString sData, RString &sErrorOut)
|
||||
{
|
||||
|
||||
+184
-2
@@ -1,9 +1,10 @@
|
||||
/** @brief Utilities for handling JSON data. */
|
||||
#ifndef JsonUtil_H
|
||||
#define JsonUtil_H
|
||||
|
||||
class RageFileBasic;
|
||||
#include "../extern/jsoncpp/include/json/value.h"
|
||||
/** @brief Utilities for handling JSON data. */
|
||||
#include "json/value.h"
|
||||
|
||||
namespace JsonUtil
|
||||
{
|
||||
bool LoadFromString( Json::Value &root, RString sData, RString &sErrorOut );
|
||||
@@ -31,6 +32,86 @@ namespace JsonUtil
|
||||
fn(*v[i], root[i]);
|
||||
}
|
||||
|
||||
template<typename V, typename T>
|
||||
static void SerializeArray(const V &v, void fn(const T &, Json::Value &), Json::Value &root)
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
root.resize( v.size() );
|
||||
int i=0;
|
||||
for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ )
|
||||
fn( *iter, root[i++] );
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
static void SerializeArrayValues(const V &v, Json::Value &root)
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
root.resize( v.size() );
|
||||
int i=0;
|
||||
for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ )
|
||||
root[i++] = *iter;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
static void SerializeArrayObjects(const V &v, Json::Value &root)
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
root.resize( v.size() );
|
||||
int i=0;
|
||||
for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ )
|
||||
iter->Serialize( root[i++] );
|
||||
}
|
||||
|
||||
template <typename M, typename E, typename F>
|
||||
static void SerializeStringToObjectMap(const M &m, F fnEnumToString(E e), Json::Value &root)
|
||||
{
|
||||
for( typename M::const_iterator iter=m.begin(); iter!=m.end(); iter++ )
|
||||
iter->second.Serialize( root[ fnEnumToString(iter->first) ] );
|
||||
}
|
||||
|
||||
template <typename M, typename E, typename F>
|
||||
static void SerializeStringToValueMap(const M &m, F fnToString(E e), Json::Value &root)
|
||||
{
|
||||
for( typename M::const_iterator iter=m.begin(); iter!=m.end(); iter++ )
|
||||
root[ fnToString(iter->first) ] = iter->second;
|
||||
}
|
||||
|
||||
template <typename M>
|
||||
static void SerializeValueToValueMap(const M &m, Json::Value &root)
|
||||
{
|
||||
for( typename M::const_iterator iter=m.begin(); iter!=m.end(); iter++ )
|
||||
root[ (iter->first) ] = iter->second;
|
||||
}
|
||||
|
||||
// Serialize a map that has a non-string key type
|
||||
template <typename V>
|
||||
static void SerializeObjectToObjectMapAsArray(const V &v, const RString &sKeyName, const RString &sValueName, Json::Value &root)
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
root.resize( v.size() );
|
||||
int i=0;
|
||||
for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ )
|
||||
{
|
||||
Json::Value &vv = root[i++];
|
||||
iter->first.Serialize( vv[sKeyName] );
|
||||
iter->second.Serialize( vv[sValueName] );
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
static void SerializeObjectToValueMapAsArray(const V &v, const RString &sKeyName, const RString &sValueName, Json::Value &root)
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
root.resize( v.size() );
|
||||
int i=0;
|
||||
for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ )
|
||||
{
|
||||
Json::Value &vv = root[i++];
|
||||
iter->first.Serialize( vv[sKeyName] );
|
||||
vv[sValueName] = iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void SerializeVectorValues(const vector<T> &v, Json::Value &root)
|
||||
{
|
||||
@@ -48,6 +129,14 @@ namespace JsonUtil
|
||||
fn(v[i], root[i]);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
static void DeserializeArrayObjects( V &v, const Json::Value &root)
|
||||
{
|
||||
v.resize( root.size() );
|
||||
for( unsigned i=0; i<v.size(); i++ )
|
||||
v[i].Deserialize( root[i] );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void DeserializeVectorPointers(vector<T*> &v, void fn(T &, const Json::Value &), const Json::Value &root)
|
||||
{
|
||||
@@ -61,6 +150,99 @@ namespace JsonUtil
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void DeserializeArrayValues(vector<T> &v, const Json::Value &root)
|
||||
{
|
||||
v.clear();
|
||||
for( unsigned i=0; i<root.size(); i++ )
|
||||
{
|
||||
T t;
|
||||
if( root[i].TryGet( t ) )
|
||||
v.push_back( t );
|
||||
}
|
||||
}
|
||||
|
||||
// don't pull in the set header here
|
||||
template<typename S, typename T>
|
||||
static void DeserializeArrayValuesIntoSet(S &s, const Json::Value &root)
|
||||
{
|
||||
s.clear();
|
||||
for( unsigned i=0; i<root.size(); i++ )
|
||||
{
|
||||
T t;
|
||||
if( root[i].TryGet( t ) )
|
||||
s.insert( t );
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void DeserializeArrayValuesIntoVector(vector<T> &v, const Json::Value &root)
|
||||
{
|
||||
v.clear();
|
||||
for( unsigned i=0; i<root.size(); i++ )
|
||||
{
|
||||
T t;
|
||||
if( root[i].TryGet( t ) )
|
||||
v.push_back( t );
|
||||
}
|
||||
}
|
||||
|
||||
template <typename M>
|
||||
static void DeserializeValueToValueMap(M &m, const Json::Value &root)
|
||||
{
|
||||
for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ )
|
||||
(*iter).TryGet( m[ iter.memberName() ] );
|
||||
}
|
||||
|
||||
template <typename M, typename E, typename F>
|
||||
static void DeserializeStringToValueMap(M &m, F fnToValue(E e), const Json::Value &root)
|
||||
{
|
||||
for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ )
|
||||
(*iter).TryGet( m[ fnToValue(iter.memberName()) ] );
|
||||
}
|
||||
|
||||
template <typename M, typename E, typename F>
|
||||
static void DeserializeStringToObjectMap(M &m, F fnToValue(E e), const Json::Value &root)
|
||||
{
|
||||
for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ )
|
||||
m[ fnToValue(iter.memberName()) ].Deserialize( *iter );
|
||||
}
|
||||
|
||||
// Serialize a map that has a non-string key type
|
||||
template <typename K, typename V>
|
||||
static void DeserializeObjectToObjectMapAsArray(map<K,V> &m, const RString &sKeyName, const RString &sValueName, const Json::Value &root)
|
||||
{
|
||||
m.clear();
|
||||
ASSERT( root.type() == Json::arrayValue );
|
||||
for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ )
|
||||
{
|
||||
ASSERT( (*iter).type() == Json::objectValue );
|
||||
K k;
|
||||
if( !k.Deserialize( (*iter)[sKeyName] ) )
|
||||
continue;
|
||||
V v;
|
||||
if( !v.Deserialize( (*iter)[sValueName] ) )
|
||||
continue;
|
||||
m[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
static void DeserializeObjectToValueMapAsArray(map<K,V> &m, const RString &sKeyName, const RString &sValueName, const Json::Value &root)
|
||||
{
|
||||
for( unsigned i=0; i<root.size(); i++ )
|
||||
{
|
||||
const Json::Value &root2 = root[i];
|
||||
K k;
|
||||
if( !k.Deserialize( root2[sKeyName] ) )
|
||||
continue;
|
||||
V v;
|
||||
if( !root2[sValueName].TryGet(v) )
|
||||
continue;
|
||||
m[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void DeserializeVectorValues(vector<T> &v, const Json::Value &root)
|
||||
{
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
#include "Command.h"
|
||||
#include "RageTypes.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <sstream> // conversion for lua functions.
|
||||
#include <csetjmp>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -63,7 +63,7 @@ bool LyricsLoader::LoadFromLRCFile(const RString& sPath, Song& out)
|
||||
StripCrnl(sValueData);
|
||||
|
||||
// handle the data
|
||||
if( 0==stricmp(sValueName,"COLOUR") || 0==stricmp(sValueName,"COLOR") )
|
||||
if( sValueName.EqualsNoCase("COLOUR") || sValueName.EqualsNoCase("COLOR") )
|
||||
{
|
||||
// set color var here for this segment
|
||||
int r, g, b;
|
||||
|
||||
+49
-18
@@ -42,6 +42,19 @@ increment_version:
|
||||
ver.cpp:
|
||||
$(MAKE) increment_version
|
||||
|
||||
PNG = \
|
||||
../extern/libpng/include/png.c ../extern/libpng/include/.png.h \
|
||||
../extern/libpng/include/pngconf.h ../extern/libpng/include/pngdebug.h \
|
||||
../extern/libpng/include/pngerror.c ../extern/libpng/include/pngget.c \
|
||||
../extern/libpng/include/pnginfo.h ../extern/libpng/include/scripts/pnglibconf.h \
|
||||
../extern/libpng/include/pngmem.c ../extern/libpng/include/pngpread.c \
|
||||
../extern/libpng/include/pngpriv.h ../extern/libpng/include/pngread.c \
|
||||
../extern/libpng/include/pngrio.c ../extern/libpng/include/pngrtran.c \
|
||||
../extern/libpng/include/pngrutil.c ../extern/libpng/include/pngset.c \
|
||||
../extern/libpng/include/pngstruct.h ../extern/libpng/include/pngtrans.c \
|
||||
../extern/libpng/include/pngwio.c ../extern/libpng/include/pngwrite.c \
|
||||
../extern/libpng/include/pngwtran.c ../extern/libpng/include/pngwutil.c
|
||||
|
||||
Screens = \
|
||||
Screen.cpp Screen.h ScreenAttract.cpp ScreenAttract.h \
|
||||
ScreenBookkeeping.cpp ScreenBookkeeping.h \
|
||||
@@ -145,10 +158,12 @@ ModsGroup.cpp ModsGroup.h \
|
||||
NoteData.cpp NoteData.h NoteDataUtil.cpp NoteDataUtil.h NoteDataWithScoring.cpp NoteDataWithScoring.h \
|
||||
NoteTypes.cpp NoteTypes.h NotesLoader.cpp NotesLoader.h \
|
||||
NotesLoaderBMS.cpp NotesLoaderBMS.h NotesLoaderDWI.cpp NotesLoaderDWI.h \
|
||||
NotesLoaderJson.cpp NotesLoaderJson.h \
|
||||
NotesLoaderKSF.cpp NotesLoaderKSF.h NotesLoaderMidi.cpp NotesLoaderMidi.h \
|
||||
NotesLoaderPMS.cpp NotesLoaderPMS.h NotesLoaderSM.cpp NotesLoaderSM.h \
|
||||
NotesLoaderSSC.cpp NotesLoaderSSC.h NotesLoaderSMA.cpp NotesLoaderSMA.h \
|
||||
NotesWriterDWI.cpp NotesWriterDWI.h \
|
||||
NotesWriterJson.cpp NotesWriterJson.h \
|
||||
NotesWriterSM.cpp NotesWriterSM.h NotesWriterSSC.cpp NotesWriterSSC.h \
|
||||
OptionRowHandler.cpp OptionRowHandler.h OptionsList.cpp OptionsList.h \
|
||||
PlayerAI.cpp PlayerAI.h PlayerNumber.cpp PlayerNumber.h PlayerOptions.cpp PlayerOptions.h \
|
||||
@@ -358,24 +373,34 @@ 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 = pcre/get.c pcre/internal.h pcre/maketables.c pcre/pcre.c pcre/pcre.h pcre/study.c
|
||||
EXTRA_DIST += pcre/chartables.c
|
||||
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
|
||||
|
||||
Lua = lua-5.1/src/lapi.c lua-5.1/src/lauxlib.c lua-5.1/src/lbaselib.c lua-5.1/src/lcode.c lua-5.1/src/ldblib.c \
|
||||
lua-5.1/src/ldebug.c lua-5.1/src/ldo.c lua-5.1/src/ldump.c lua-5.1/src/lfunc.c lua-5.1/src/lgc.c lua-5.1/src/linit.c \
|
||||
lua-5.1/src/liolib.c lua-5.1/src/llex.c lua-5.1/src/lmathlib.c lua-5.1/src/lmem.c lua-5.1/src/loadlib.c \
|
||||
lua-5.1/src/lobject.c lua-5.1/src/lopcodes.c lua-5.1/src/loslib.c lua-5.1/src/lparser.c lua-5.1/src/lstate.c \
|
||||
lua-5.1/src/lstring.c lua-5.1/src/lstrlib.c lua-5.1/src/ltable.c lua-5.1/src/ltablib.c lua-5.1/src/ltm.c \
|
||||
lua-5.1/src/lundump.c lua-5.1/src/lvm.c lua-5.1/src/lzio.c lua-5.1/src/lapi.h lua-5.1/src/lauxlib.h lua-5.1/src/lcode.h \
|
||||
lua-5.1/src/ldebug.h lua-5.1/src/ldo.h lua-5.1/src/lfunc.h lua-5.1/src/lgc.h lua-5.1/src/llex.h lua-5.1/src/llimits.h \
|
||||
lua-5.1/src/lmem.h lua-5.1/src/lobject.h lua-5.1/src/lopcodes.h lua-5.1/src/lparser.h lua-5.1/src/lstate.h \
|
||||
lua-5.1/src/lstring.h lua-5.1/src/ltable.h lua-5.1/src/ltm.h lua-5.1/src/luaconf.h lua-5.1/src/lua.h lua-5.1/src/lualib.h \
|
||||
lua-5.1/src/lundump.h lua-5.1/src/lvm.h lua-5.1/src/lzio.h
|
||||
Lua = ../extern/lua-5.1/src/lapi.c ../extern/lua-5.1/src/lauxlib.c ../extern/lua-5.1/src/lbaselib.c ../extern/lua-5.1/src/lcode.c ../extern/lua-5.1/src/ldblib.c \
|
||||
../extern/lua-5.1/src/ldebug.c ../extern/lua-5.1/src/ldo.c ../extern/lua-5.1/src/ldump.c ../extern/lua-5.1/src/lfunc.c ../extern/lua-5.1/src/lgc.c ../extern/lua-5.1/src/linit.c \
|
||||
../extern/lua-5.1/src/liolib.c ../extern/lua-5.1/src/llex.c ../extern/lua-5.1/src/lmathlib.c ../extern/lua-5.1/src/lmem.c ../extern/lua-5.1/src/loadlib.c \
|
||||
../extern/lua-5.1/src/lobject.c ../extern/lua-5.1/src/lopcodes.c ../extern/lua-5.1/src/loslib.c ../extern/lua-5.1/src/lparser.c ../extern/lua-5.1/src/lstate.c \
|
||||
../extern/lua-5.1/src/lstring.c ../extern/lua-5.1/src/lstrlib.c ../extern/lua-5.1/src/ltable.c ../extern/lua-5.1/src/ltablib.c ../extern/lua-5.1/src/ltm.c \
|
||||
../extern/lua-5.1/src/lundump.c ../extern/lua-5.1/src/lvm.c ../extern/lua-5.1/src/lzio.c ../extern/lua-5.1/src/lapi.h ../extern/lua-5.1/src/lauxlib.h ../extern/lua-5.1/src/lcode.h \
|
||||
../extern/lua-5.1/src/ldebug.h ../extern/lua-5.1/src/ldo.h ../extern/lua-5.1/src/lfunc.h ../extern/lua-5.1/src/lgc.h ../extern/lua-5.1/src/llex.h ../extern/lua-5.1/src/llimits.h \
|
||||
../extern/lua-5.1/src/lmem.h ../extern/lua-5.1/src/lobject.h ../extern/lua-5.1/src/lopcodes.h ../extern/lua-5.1/src/lparser.h ../extern/lua-5.1/src/lstate.h \
|
||||
../extern/lua-5.1/src/lstring.h ../extern/lua-5.1/src/ltable.h ../extern/lua-5.1/src/ltm.h ../extern/lua-5.1/src/luaconf.h ../extern/lua-5.1/src/lua.h ../extern/lua-5.1/src/lualib.h \
|
||||
../extern/lua-5.1/src/lundump.h ../extern/lua-5.1/src/lvm.h ../extern/lua-5.1/src/lzio.h
|
||||
|
||||
jsoncpp = jsoncpp/src/lib_json/json_reader.cpp jsoncpp/src/lib_json/json_value.cpp \
|
||||
jsoncpp/src/lib_json/json_writer.cpp
|
||||
jsoncpp = ../extern/jsoncpp/src/lib_json/json_reader.cpp \
|
||||
../extern/jsoncpp/src/lib_json/json_value.cpp \
|
||||
../extern/jsoncpp/src/lib_json/json_writer.cpp \
|
||||
../extern/jsoncpp/include/json/autolink.h \
|
||||
../extern/jsoncpp/include/json/config.h \
|
||||
../extern/jsoncpp/include/json/features.h \
|
||||
../extern/jsoncpp/include/json/forwards.h \
|
||||
../extern/jsoncpp/include/json/json.h \
|
||||
../extern/jsoncpp/include/json/reader.h \
|
||||
../extern/jsoncpp/include/json/value.h \
|
||||
../extern/jsoncpp/include/json/writer.h
|
||||
|
||||
RageFile = \
|
||||
RageFileBasic.cpp RageFileBasic.h \
|
||||
@@ -579,7 +604,11 @@ libtomcrypt_a_CPPFLAGS = -I$(srcdir)/libtomcrypt/src/headers $(AM_CPPFLAGS)
|
||||
|
||||
noinst_LIBRARIES += libtomcrypt.a
|
||||
|
||||
main_SOURCES = $(Screens) \
|
||||
main_CPPFLAGS = -I$(top_srcdir)/extern/jsoncpp/include \
|
||||
-I$(top_srcdir)/extern/glew-1.5.8/include
|
||||
|
||||
main_SOURCES = $(PNG) \
|
||||
$(Screens) \
|
||||
$(DataStructures) \
|
||||
$(FileTypes) \
|
||||
$(StepMania) \
|
||||
@@ -597,15 +626,17 @@ main_LDADD = \
|
||||
libtomcrypt.a libtommath.a
|
||||
|
||||
nodist_stepmania_SOURCES = ver.cpp
|
||||
|
||||
stepmania_CPPFLAGS = $(main_CPPFLAGS)
|
||||
stepmania_SOURCES = $(main_SOURCES)
|
||||
stepmania_LDADD = $(main_LDADD)
|
||||
|
||||
if BUILD_LUA_BINARIES
|
||||
noinst_PROGRAMS += lua luac
|
||||
lua_SOURCES = $(Lua) lua-5.1/src/lua.c
|
||||
lua_SOURCES = $(Lua) ../extern/lua-5.1/src/lua.c
|
||||
lua_LDADD = -lreadline
|
||||
|
||||
luac_SOURCES = $(Lua) lua-5.1/src/luac.c lua-5.1/src/print.c
|
||||
luac_SOURCES = $(Lua) ../extern/lua-5.1/src/luac.c ../extern/lua-5.1/src/print.c
|
||||
luac_LDADD =
|
||||
endif
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
+3
-13
@@ -39,12 +39,11 @@ void ModIcon::Load( RString sMetricsGroup )
|
||||
this->AddChild( &m_text );
|
||||
|
||||
CROP_TEXT_TO_WIDTH.Load( sMetricsGroup, "CropTextToWidth" );
|
||||
|
||||
// stop words
|
||||
/*
|
||||
STOP_WORDS.Load( sMetricsGroup, "StopWords" );
|
||||
m_vStopWords.empty();
|
||||
split(STOP_WORDS, ",", m_vStopWords);
|
||||
*/
|
||||
|
||||
Set("");
|
||||
}
|
||||
@@ -53,17 +52,8 @@ void ModIcon::Set( const RString &_sText )
|
||||
{
|
||||
RString sText = _sText;
|
||||
|
||||
// todo: make these metricable -aj
|
||||
static const RString sStopWords[] =
|
||||
{
|
||||
"1X",
|
||||
"DEFAULT",
|
||||
"OVERHEAD",
|
||||
"OFF",
|
||||
};
|
||||
|
||||
for( unsigned i=0; i<ARRAYLEN(sStopWords); i++ )
|
||||
if( 0==stricmp(sText,sStopWords[i]) )
|
||||
for( unsigned i = 0; i < m_vStopWords.size(); i++ )
|
||||
if( sText.EqualsNoCase(m_vStopWords[i]) )
|
||||
sText = "";
|
||||
|
||||
sText.Replace( " ", "\n" );
|
||||
|
||||
@@ -21,6 +21,8 @@ protected:
|
||||
AutoActor m_sprEmpty;
|
||||
|
||||
ThemeMetric<int> CROP_TEXT_TO_WIDTH;
|
||||
ThemeMetric<RString> STOP_WORDS;
|
||||
vector<RString> m_vStopWords;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+26
-1
@@ -66,10 +66,35 @@ void ModIconRow::HandleMessage( const Message &msg )
|
||||
|
||||
struct OptionColumnEntry
|
||||
{
|
||||
char szString[30];
|
||||
char *szString;
|
||||
int iSlotIndex;
|
||||
|
||||
//void FromStack( lua_State *L, int iPos );
|
||||
};
|
||||
|
||||
/*
|
||||
void OptionColumnEntry::FromStack( lua_State *L, int iPos )
|
||||
{
|
||||
if( lua_type(L, iPos) != LUA_TTABLE )
|
||||
return;
|
||||
|
||||
lua_pushvalue( L, iPos );
|
||||
const int iTab = lua_gettop( L );
|
||||
|
||||
// option name
|
||||
lua_getfield( L, iTab, "Name" );
|
||||
RString sName = lua_tostring( L, -1 );
|
||||
szString = const_cast<char *>(sName.c_str());
|
||||
lua_settop( L, iTab );
|
||||
|
||||
// option icon index
|
||||
lua_getfield( L, iTab, "IconIndex" );
|
||||
iSlotIndex = lua_tointeger( L, -1 );
|
||||
lua_settop( L, iTab );
|
||||
}
|
||||
static vector<OptionColumnEntry> g_OptionColumnEntries;
|
||||
*/
|
||||
|
||||
// todo: metric these? -aj
|
||||
static const OptionColumnEntry g_OptionColumnEntries[] =
|
||||
{
|
||||
|
||||
@@ -22,9 +22,7 @@ public:
|
||||
|
||||
virtual void HandleMessage( const Message &msg );
|
||||
|
||||
//
|
||||
// Commands
|
||||
//
|
||||
virtual void PushSelf( lua_State *L );
|
||||
|
||||
protected:
|
||||
|
||||
+2
-1
@@ -466,7 +466,8 @@ int NoteData::GetNumTapNotes( int iStartIndex, int iEndIndex ) const
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
{
|
||||
const TapNote &tn = GetTapNote(t, r);
|
||||
if( tn.type != TapNote::empty && tn.type != TapNote::mine && tn.type != TapNote::fake )
|
||||
if( tn.type != TapNote::empty && tn.type != TapNote::mine
|
||||
&& tn.type != TapNote::lift && tn.type != TapNote::fake )
|
||||
iNumNotes++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+221
-89
@@ -19,6 +19,7 @@
|
||||
#include "BackgroundUtil.h"
|
||||
#include "Course.h"
|
||||
#include "NoteData.h"
|
||||
#include "Actor.h"
|
||||
|
||||
static ThemeMetric<bool> SHOW_BOARD( "NoteField", "ShowBoard" );
|
||||
static ThemeMetric<bool> SHOW_BEAT_BARS( "NoteField", "ShowBeatBars" );
|
||||
@@ -32,6 +33,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 +268,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 +455,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 +465,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 +475,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 +606,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 +674,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 +686,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 +708,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 +737,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
|
||||
@@ -685,6 +766,12 @@ bool NoteField::IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargets
|
||||
|
||||
void NoteField::DrawPrimitives()
|
||||
{
|
||||
|
||||
// XXX Hack: Set Actor's active player number so the notes get the flashing that matches the steps.
|
||||
// save the active player number (so they can nest)
|
||||
PlayerNumber pnLastActivePlayerNumber = m_pPlayerState->m_PlayerNumber;
|
||||
Actor::m_ActivePlayerNumber = m_pPlayerState->m_PlayerNumber;
|
||||
|
||||
//LOG->Trace( "NoteField::DrawPrimitives()" );
|
||||
|
||||
// This should be filled in on the first update.
|
||||
@@ -739,10 +826,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 +871,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 +889,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 +900,19 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
// Warp text
|
||||
FOREACH_CONST( WarpSegment, GAMESTATE->m_pCurSong->m_Timing.m_WarpSegments, seg )
|
||||
FOREACH_CONST( WarpSegment, timing.m_WarpSegments, seg )
|
||||
{
|
||||
if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = NoteRowToBeat(seg->m_iStartRow);
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawWarpText( fBeat, seg->m_fEndBeat );
|
||||
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 +922,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 +960,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 +998,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 +1008,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 +1141,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 +1172,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 +1214,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 +1239,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;
|
||||
}
|
||||
|
||||
@@ -1123,6 +1252,9 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
cur->m_GhostArrowRow.Draw();
|
||||
|
||||
// restore the active player number
|
||||
Actor::m_ActivePlayerNumber = pnLastActivePlayerNumber;
|
||||
}
|
||||
|
||||
void NoteField::FadeToFail()
|
||||
|
||||
+6
-2
@@ -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"
|
||||
@@ -56,16 +58,18 @@ protected:
|
||||
void DrawMarkerBar( int fBeat );
|
||||
void DrawAreaHighlight( int iStartBeat, int iEndBeat );
|
||||
void DrawBPMText( const float fBeat, const float fBPM );
|
||||
void DrawFreezeText( const float fBeat, const float fBPM, const float bDelay );
|
||||
void DrawFreezeText( const float fBeat, const float fLength, const float bDelay );
|
||||
void DrawWarpText( const float fBeat, const float fNewBeat );
|
||||
void DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator );
|
||||
void DrawTickcountText( const float fBeat, int iTicks );
|
||||
void DrawComboText( const float fBeat, int iCombo );
|
||||
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
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
#include <map>
|
||||
#include "SpecialFiles.h"
|
||||
|
||||
NoteSkinManager* NOTESKIN = NULL; // global object accessable from anywhere in the program
|
||||
/** @brief Have the NoteSkinManager available throughout the program. */
|
||||
NoteSkinManager* NOTESKIN = NULL;
|
||||
|
||||
const RString GAME_COMMON_NOTESKIN_NAME = "common";
|
||||
const RString GAME_BASE_NOTESKIN_NAME = "default";
|
||||
@@ -199,7 +200,7 @@ bool NoteSkinManager::DoesNoteSkinExist( const RString &sSkinName )
|
||||
vector<RString> asSkinNames;
|
||||
GetAllNoteSkinNamesForGame( GAMESTATE->m_pCurGame, asSkinNames );
|
||||
for( unsigned i=0; i<asSkinNames.size(); i++ )
|
||||
if( 0==stricmp(sSkinName, asSkinNames[i]) )
|
||||
if( sSkinName.EqualsNoCase(asSkinNames[i]) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -251,7 +252,7 @@ RString NoteSkinManager::GetMetric( const RString &sButtonName, const RString &s
|
||||
|
||||
int NoteSkinManager::GetMetricI( const RString &sButtonName, const RString &sValueName )
|
||||
{
|
||||
return atoi( GetMetric(sButtonName,sValueName) );
|
||||
return StringToInt( GetMetric(sButtonName,sValueName) );
|
||||
}
|
||||
|
||||
float NoteSkinManager::GetMetricF( const RString &sButtonName, const RString &sValueName )
|
||||
@@ -261,7 +262,8 @@ float NoteSkinManager::GetMetricF( const RString &sButtonName, const RString &sV
|
||||
|
||||
bool NoteSkinManager::GetMetricB( const RString &sButtonName, const RString &sValueName )
|
||||
{
|
||||
return atoi( GetMetric(sButtonName,sValueName) ) != 0;
|
||||
// Could also call GetMetricI here...hmm.
|
||||
return StringToInt( GetMetric(sButtonName,sValueName) ) != 0;
|
||||
}
|
||||
|
||||
apActorCommands NoteSkinManager::GetMetricA( const RString &sButtonName, const RString &sValueName )
|
||||
|
||||
@@ -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 );
|
||||
|
||||
+243
-186
@@ -398,8 +398,8 @@ static void ReadTimeSigs( const NameToData_t &mapNameToData, MeasureToTimeSig_t
|
||||
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 iMeasureNo = StringToInt( sName.substr(1, 3) );
|
||||
int iBMSTrackNo = StringToInt( sName.substr(4, 2) );
|
||||
RString nData = it->second;
|
||||
int totalPairs = nData.size() / 2;
|
||||
if( iBMSTrackNo != BMS_TRACK_TIME_SIG && iBMSTrackNo != 7 )
|
||||
@@ -423,10 +423,10 @@ static void ReadTimeSigs( const NameToData_t &mapNameToData, MeasureToTimeSig_t
|
||||
|
||||
// this is step or offset data. Looks like "#00705"
|
||||
const RString &sData = it->second;
|
||||
int iMeasureNo = atoi( sName.substr(1, 3).c_str() );
|
||||
int iMeasureNo = StringToInt( sName.substr(1, 3) );
|
||||
if( iMeasureNo < iStartMeasureNo )
|
||||
continue;
|
||||
int iBMSTrackNo = atoi( sName.substr(4, 2).c_str() );
|
||||
int iBMSTrackNo = StringToInt( sName.substr(4, 2) );
|
||||
if( iBMSTrackNo == BMS_TRACK_TIME_SIG )
|
||||
out[iMeasureNo] = StringToFloat( sData );
|
||||
}
|
||||
@@ -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;
|
||||
@@ -446,18 +558,128 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
int iPlayer = -1;
|
||||
RString sData;
|
||||
if( GetTagFromMap( mapNameToData, "#player", sData ) )
|
||||
iPlayer = atoi(sData);
|
||||
iPlayer = StringToInt(sData);
|
||||
if( GetTagFromMap( mapNameToData, "#playlevel", sData ) )
|
||||
out.SetMeter( atoi(sData) );
|
||||
out.SetMeter( StringToInt(sData) );
|
||||
|
||||
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];
|
||||
|
||||
@@ -475,8 +697,8 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
continue;
|
||||
|
||||
// this is step or offset data. Looks like "#00705"
|
||||
int iMeasureNo = atoi( sName.substr(1,3).c_str() );
|
||||
int iRawTrackNum = atoi( sName.substr(4,2).c_str() );
|
||||
int iMeasureNo = StringToInt( sName.substr(1,3) );
|
||||
int iRawTrackNum = StringToInt( sName.substr(4,2) );
|
||||
int iRowNo = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||
float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||
const RString &sNoteData = it->second;
|
||||
@@ -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,16 +1177,20 @@ 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
|
||||
delete pNewNotes;
|
||||
}
|
||||
|
||||
// set song's timing data to the main file.
|
||||
out.m_SongTiming = apSteps[iMainDataIndex]->m_Timing;
|
||||
|
||||
SlideDuplicateDifficulties( out );
|
||||
|
||||
ConvertString( out.m_sMainTitle, "utf-8,japanese" );
|
||||
|
||||
+54
-26
@@ -10,6 +10,7 @@
|
||||
#include "GameInput.h"
|
||||
#include "NotesLoader.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "Difficulty.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -140,6 +141,33 @@ static bool Is192( const RString &sStepData, size_t pos )
|
||||
/** @brief All DWI files use 4 beats per measure. */
|
||||
const int BEATS_PER_MEASURE = 4;
|
||||
|
||||
/* We prefer the normal names; recognize a number of others, too. (They'll get
|
||||
* normalized when written to SMs, etc.) */
|
||||
Difficulty DwiCompatibleStringToDifficulty( const RString& sDC )
|
||||
{
|
||||
RString s2 = sDC;
|
||||
s2.MakeLower();
|
||||
if( s2 == "beginner" ) return Difficulty_Beginner;
|
||||
else if( s2 == "easy" ) return Difficulty_Easy;
|
||||
else if( s2 == "basic" ) return Difficulty_Easy;
|
||||
else if( s2 == "light" ) return Difficulty_Easy;
|
||||
else if( s2 == "medium" ) return Difficulty_Medium;
|
||||
else if( s2 == "another" ) return Difficulty_Medium;
|
||||
else if( s2 == "trick" ) return Difficulty_Medium;
|
||||
else if( s2 == "standard" ) return Difficulty_Medium;
|
||||
else if( s2 == "difficult") return Difficulty_Medium;
|
||||
else if( s2 == "hard" ) return Difficulty_Hard;
|
||||
else if( s2 == "ssr" ) return Difficulty_Hard;
|
||||
else if( s2 == "maniac" ) return Difficulty_Hard;
|
||||
else if( s2 == "heavy" ) return Difficulty_Hard;
|
||||
else if( s2 == "smaniac" ) return Difficulty_Challenge;
|
||||
else if( s2 == "challenge" ) return Difficulty_Challenge;
|
||||
else if( s2 == "expert" ) return Difficulty_Challenge;
|
||||
else if( s2 == "oni" ) return Difficulty_Challenge;
|
||||
else if( s2 == "edit" ) return Difficulty_Edit;
|
||||
else return Difficulty_Invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Look through the notes tag to extract the data.
|
||||
* @param sMode the steps type.
|
||||
@@ -206,7 +234,7 @@ static bool LoadFromDWITokens(
|
||||
DEFAULT_FAIL( out.m_StepsType );
|
||||
}
|
||||
|
||||
int iNumFeet = atoi(sNumFeet);
|
||||
int iNumFeet = StringToInt(sNumFeet);
|
||||
// out.SetDescription(sDescription); // Don't put garbage in the description.
|
||||
out.SetMeter(iNumFeet);
|
||||
out.SetDifficulty( DwiCompatibleStringToDifficulty(sDescription) );
|
||||
@@ -447,10 +475,10 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
}
|
||||
|
||||
// handle the data
|
||||
if( 0==stricmp(sValueName,"FILE") )
|
||||
if( sValueName.EqualsNoCase("FILE") )
|
||||
out.m_sMusicFile = sParams[1];
|
||||
|
||||
else if( 0==stricmp(sValueName,"TITLE") )
|
||||
else if( sValueName.EqualsNoCase("TITLE") )
|
||||
{
|
||||
NotesLoader::GetMainAndSubTitlesFromFullTitle( sParams[1], out.m_sMainTitle, out.m_sSubTitle );
|
||||
|
||||
@@ -460,38 +488,38 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
ConvertString( out.m_sSubTitle, "utf-8,english" );
|
||||
}
|
||||
|
||||
else if( 0==stricmp(sValueName,"ARTIST") )
|
||||
else if( sValueName.EqualsNoCase("ARTIST") )
|
||||
{
|
||||
out.m_sArtist = sParams[1];
|
||||
ConvertString( out.m_sArtist, "utf-8,english" );
|
||||
}
|
||||
|
||||
else if( 0==stricmp(sValueName,"GENRE") )
|
||||
else if( sValueName.EqualsNoCase("GENRE") )
|
||||
{
|
||||
out.m_sGenre = sParams[1];
|
||||
ConvertString( out.m_sGenre, "utf-8,english" );
|
||||
}
|
||||
|
||||
else if( 0==stricmp(sValueName,"CDTITLE") )
|
||||
else if( sValueName.EqualsNoCase("CDTITLE") )
|
||||
out.m_sCDTitleFile = sParams[1];
|
||||
|
||||
else if( 0==stricmp(sValueName,"BPM") )
|
||||
else if( sValueName.EqualsNoCase("BPM") )
|
||||
{
|
||||
const float fBPM = StringToFloat( sParams[1] );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
else if( 0==stricmp(sValueName,"DISPLAYBPM") )
|
||||
else if( sValueName.EqualsNoCase("DISPLAYBPM") )
|
||||
{
|
||||
// #DISPLAYBPM:[xxx..xxx]|[xxx]|[*];
|
||||
int iMin, iMax;
|
||||
@@ -515,17 +543,17 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
}
|
||||
}
|
||||
|
||||
else if( 0==stricmp(sValueName,"GAP") )
|
||||
else if( sValueName.EqualsNoCase("GAP") )
|
||||
// the units of GAP is 1/1000 second
|
||||
out.m_Timing.m_fBeat0OffsetInSeconds = -atoi( sParams[1] ) / 1000.0f;
|
||||
out.m_SongTiming.m_fBeat0OffsetInSeconds = -StringToInt( sParams[1] ) / 1000.0f;
|
||||
|
||||
else if( 0==stricmp(sValueName,"SAMPLESTART") )
|
||||
else if( sValueName.EqualsNoCase("SAMPLESTART") )
|
||||
out.m_fMusicSampleStartSeconds = ParseBrokenDWITimestamp(sParams[1], sParams[2], sParams[3]);
|
||||
|
||||
else if( 0==stricmp(sValueName,"SAMPLELENGTH") )
|
||||
else if( sValueName.EqualsNoCase("SAMPLELENGTH") )
|
||||
out.m_fMusicSampleLengthSeconds = ParseBrokenDWITimestamp(sParams[1], sParams[2], sParams[3]);
|
||||
|
||||
else if( 0==stricmp(sValueName,"FREEZE") )
|
||||
else if( sValueName.EqualsNoCase("FREEZE") )
|
||||
{
|
||||
vector<RString> arrayFreezeExpressions;
|
||||
split( sParams[1], ",", arrayFreezeExpressions );
|
||||
@@ -542,12 +570,12 @@ 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 );
|
||||
}
|
||||
}
|
||||
|
||||
else if( 0==stricmp(sValueName,"CHANGEBPM") || 0==stricmp(sValueName,"BPMCHANGE") )
|
||||
else if( sValueName.EqualsNoCase("CHANGEBPM") || sValueName.EqualsNoCase("BPMCHANGE") )
|
||||
{
|
||||
vector<RString> arrayBPMChangeExpressions;
|
||||
split( sParams[1], ",", arrayBPMChangeExpressions );
|
||||
@@ -567,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
|
||||
{
|
||||
@@ -577,12 +605,12 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
}
|
||||
}
|
||||
|
||||
else if( 0==stricmp(sValueName,"SINGLE") ||
|
||||
0==stricmp(sValueName,"DOUBLE") ||
|
||||
0==stricmp(sValueName,"COUPLE") ||
|
||||
0==stricmp(sValueName,"SOLO") )
|
||||
else if( sValueName.EqualsNoCase("SINGLE") ||
|
||||
sValueName.EqualsNoCase("DOUBLE") ||
|
||||
sValueName.EqualsNoCase("COUPLE") ||
|
||||
sValueName.EqualsNoCase("SOLO") )
|
||||
{
|
||||
Steps* pNewNotes = new Steps;
|
||||
Steps* pNewNotes = out.CreateSteps();
|
||||
LoadFromDWITokens(
|
||||
sParams[0],
|
||||
sParams[1],
|
||||
@@ -597,8 +625,8 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
else
|
||||
delete pNewNotes;
|
||||
}
|
||||
else if( 0==stricmp(sValueName,"DISPLAYTITLE") ||
|
||||
0==stricmp(sValueName,"DISPLAYARTIST") )
|
||||
else if( sValueName.EqualsNoCase("DISPLAYTITLE") ||
|
||||
sValueName.EqualsNoCase("DISPLAYARTIST") )
|
||||
{
|
||||
/* We don't want to support these tags. However, we don't want
|
||||
* to pick up images used here as song images (eg. banners). */
|
||||
@@ -628,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
#include "global.h"
|
||||
#include "NotesLoaderJson.h"
|
||||
#include "json/value.h"
|
||||
#include "TimingData.h"
|
||||
#include "RageUtil.h"
|
||||
#include "JsonUtil.h"
|
||||
#include "BackgroundUtil.h"
|
||||
#include "NoteData.h"
|
||||
#include "Song.h"
|
||||
#include "Steps.h"
|
||||
#include "GameManager.h"
|
||||
|
||||
void NotesLoaderJson::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
{
|
||||
GetDirListing( sPath + RString("*.json"), out );
|
||||
}
|
||||
|
||||
void Deserialize(BPMSegment &seg, const Json::Value &root)
|
||||
{
|
||||
seg.m_iStartRow = BeatToNoteRow((float)root["Beat"].asDouble());
|
||||
seg.m_fBPS = (float)(root["BPM"].asDouble() / 60);
|
||||
}
|
||||
|
||||
static void Deserialize(StopSegment &seg, const Json::Value &root)
|
||||
{
|
||||
seg.m_iStartRow = BeatToNoteRow((float)(root["Beat"].asDouble()));
|
||||
seg.m_fStopSeconds = (float)root["Seconds"].asDouble();
|
||||
}
|
||||
|
||||
static void Deserialize(TimingData &td, const Json::Value &root)
|
||||
{
|
||||
JsonUtil::DeserializeVectorObjects( td.m_BPMSegments, Deserialize, root["BpmSegments"] );
|
||||
JsonUtil::DeserializeVectorObjects( td.m_StopSegments, Deserialize, root["StopSegments"] );
|
||||
}
|
||||
|
||||
static void Deserialize(LyricSegment &o, const Json::Value &root)
|
||||
{
|
||||
o.m_fStartTime = (float)root["StartTime"].asDouble();
|
||||
o.m_sLyric = root["Lyric"].asString();
|
||||
o.m_Color.FromString( root["Color"].asString() );
|
||||
}
|
||||
|
||||
static void Deserialize(BackgroundDef &o, const Json::Value &root)
|
||||
{
|
||||
o.m_sEffect = root["Effect"].asString();
|
||||
o.m_sFile1 = root["File1"].asString();
|
||||
o.m_sFile2 = root["File2"].asString();
|
||||
o.m_sColor1 = root["Color1"].asString();
|
||||
}
|
||||
|
||||
static void Deserialize(BackgroundChange &o, const Json::Value &root )
|
||||
{
|
||||
Deserialize( o.m_def, root["Def"] );
|
||||
o.m_fStartBeat = (float)root["StartBeat"].asDouble();
|
||||
o.m_fRate = (float)root["Rate"].asDouble();
|
||||
o.m_sTransition = root["Transition"].asString();
|
||||
}
|
||||
|
||||
static void Deserialize( TapNote &o, const Json::Value &root )
|
||||
{
|
||||
//if( o.type != TapNote::tap )
|
||||
if( root.isInt() )
|
||||
o.type = (TapNote::Type)root["Type"].asInt();
|
||||
//if( o.type == TapNote::hold_head )
|
||||
o.subType = (TapNote::SubType)root["SubType"].asInt();
|
||||
//root["Source"] = (int)source;
|
||||
//if( !o.sAttackModifiers.empty() )
|
||||
o.sAttackModifiers = root["AttackModifiers"].asString();
|
||||
//if( o.fAttackDurationSeconds > 0 )
|
||||
o.fAttackDurationSeconds = (float)root["AttackDurationSeconds"].asDouble();
|
||||
//if( o.bKeysound )
|
||||
o.iKeysoundIndex = root["KeysoundIndex"].asInt();
|
||||
//if( o.iDuration > 0 )
|
||||
o.iDuration = root["Duration"].asInt();
|
||||
//if( o.pn != PLAYER_INVALID )
|
||||
o.pn = (PlayerNumber)root["PlayerNumber"].asInt();
|
||||
}
|
||||
|
||||
static void Deserialize( StepsType st, NoteData &nd, const Json::Value &root )
|
||||
{
|
||||
int iTracks = nd.GetNumTracks();
|
||||
nd.SetNumTracks( iTracks );
|
||||
for( unsigned i=0; i<root.size(); i++ )
|
||||
{
|
||||
Json::Value root2 = root[i];
|
||||
float fBeat = (float)root2[(unsigned)0].asDouble();
|
||||
int iRow = BeatToNoteRow(fBeat);
|
||||
int iTrack = root2[1].asInt();
|
||||
const Json::Value &root3 = root2[2];
|
||||
TapNote tn;
|
||||
Deserialize( tn, root3 );
|
||||
nd.SetTapNote( iTrack, iRow, tn );
|
||||
}
|
||||
}
|
||||
|
||||
static void Deserialize( RadarValues &o, const Json::Value &root )
|
||||
{
|
||||
FOREACH_ENUM( RadarCategory, rc )
|
||||
{
|
||||
o.m_Values.f[rc] = (float)root[ RadarCategoryToString(rc) ].asDouble();
|
||||
}
|
||||
}
|
||||
|
||||
static void Deserialize( Steps &o, const Json::Value &root )
|
||||
{
|
||||
o.m_StepsType = GAMEMAN->StringToStepsType(root["StepsType"].asString());
|
||||
|
||||
o.Decompress();
|
||||
|
||||
NoteData nd;
|
||||
Deserialize( o.m_StepsType, nd, root["NoteData"] );
|
||||
o.SetNoteData( nd );
|
||||
//o.SetHash( root["Hash"].asInt() );
|
||||
o.SetDescription( root["Description"].asString() );
|
||||
o.SetDifficulty( StringToDifficulty(root["Difficulty"].asString()) );
|
||||
o.SetMeter( root["Meter"].asInt() );
|
||||
|
||||
RadarValues rv[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
Deserialize( rv[pn], root["RadarValues"] );
|
||||
}
|
||||
o.SetCachedRadarValues( rv );
|
||||
}
|
||||
|
||||
static void Deserialize( Song &out, const Json::Value &root )
|
||||
{
|
||||
out.SetSongDir( root["SongDir"].asString() );
|
||||
out.m_sGroupName = root["GroupName"].asString();
|
||||
out.m_sMainTitle = root["Title"].asString();
|
||||
out.m_sSubTitle = root["SubTitle"].asString();
|
||||
out.m_sArtist = root["Artist"].asString();
|
||||
out.m_sMainTitleTranslit = root["TitleTranslit"].asString();
|
||||
out.m_sSubTitleTranslit = root["SubTitleTranslit"].asString();
|
||||
out.m_sGenre = root["Genre"].asString();
|
||||
out.m_sCredit = root["Credit"].asString();
|
||||
out.m_sBannerFile = root["Banner"].asString();
|
||||
out.m_sBackgroundFile = root["Background"].asString();
|
||||
out.m_sLyricsFile = root["LyricsFile"].asString();
|
||||
out.m_sCDTitleFile = root["CDTitle"].asString();
|
||||
out.m_sMusicFile = root["Music"].asString();
|
||||
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();
|
||||
if( sSelectable.EqualsNoCase("YES") )
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
else if( sSelectable.EqualsNoCase("NO") )
|
||||
out.m_SelectionDisplay = out.SHOW_NEVER;
|
||||
|
||||
out.m_fFirstBeat = (float)root["FirstBeat"].asDouble();
|
||||
out.m_fLastBeat = (float)root["LastBeat"].asDouble();
|
||||
out.m_sSongFileName = root["SongFileName"].asString();
|
||||
out.m_bHasMusic = root["HasMusic"].asBool();
|
||||
out.m_bHasBanner = root["HasBanner"].asBool();
|
||||
out.m_fMusicLengthSeconds = (float)root["MusicLengthSeconds"].asDouble();
|
||||
|
||||
RString sDisplayBPMType = root["DisplayBpmType"].asString();
|
||||
if( sDisplayBPMType == "*" )
|
||||
out.m_DisplayBPMType = DISPLAY_BPM_RANDOM;
|
||||
else
|
||||
out.m_DisplayBPMType = DISPLAY_BPM_SPECIFIED;
|
||||
|
||||
if( out.m_DisplayBPMType == DISPLAY_BPM_SPECIFIED )
|
||||
{
|
||||
out.m_fSpecifiedBPMMin = (float)root["SpecifiedBpmMin"].asDouble();
|
||||
out.m_fSpecifiedBPMMax = (float)root["SpecifiedBpmMax"].asDouble();
|
||||
}
|
||||
|
||||
Deserialize( out.m_SongTiming, root["TimingData"] );
|
||||
JsonUtil::DeserializeVectorObjects( out.m_LyricSegments, Deserialize, root["LyricSegments"] );
|
||||
|
||||
{
|
||||
const Json::Value &root2 = root["BackgroundChanges"];
|
||||
FOREACH_BackgroundLayer( bl )
|
||||
{
|
||||
const Json::Value &root3 = root2[bl];
|
||||
vector<BackgroundChange> &vBgc = out.GetBackgroundChanges(bl);
|
||||
JsonUtil::DeserializeVectorObjects( vBgc, Deserialize, root3 );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
vector<BackgroundChange> &vBgc = out.GetForegroundChanges();
|
||||
JsonUtil::DeserializeVectorObjects( vBgc, Deserialize, root["ForegroundChanges"] );
|
||||
}
|
||||
|
||||
JsonUtil::DeserializeArrayValuesIntoVector( out.m_vsKeysoundFile, root["KeySounds"] );
|
||||
|
||||
{
|
||||
vector<Steps*> vpSteps;
|
||||
JsonUtil::DeserializeVectorPointers<Steps>( vpSteps, Deserialize, root["Charts"] );
|
||||
FOREACH( Steps*, vpSteps, iter )
|
||||
out.AddSteps( *iter );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool NotesLoaderJson::LoadFromJsonFile( const RString &sPath, Song &out )
|
||||
{
|
||||
Json::Value root;
|
||||
if( !JsonUtil::LoadFromFileShowErrors(root,sPath) )
|
||||
return false;
|
||||
|
||||
Deserialize(out, root);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NotesLoaderJson::LoadFromDir( const RString &sPath, Song &out )
|
||||
{
|
||||
return LoadFromJsonFile(sPath, out);
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
||||
* 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,41 @@
|
||||
/* JsonLoader - Reads a Song from a .json file. */
|
||||
|
||||
#ifndef NotesLoaderJson_H
|
||||
#define NotesLoaderJson_H
|
||||
|
||||
#include "NotesLoader.h"
|
||||
class Song;
|
||||
|
||||
namespace NotesLoaderJson
|
||||
{
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadFromDir( const RString &sPath, Song &out );
|
||||
bool LoadFromJsonFile( const RString &sPath, Song &out );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
||||
* 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.
|
||||
*/
|
||||
+196
-91
@@ -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,25 +80,100 @@ 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") )
|
||||
{
|
||||
iTickCount = atoi( sParams[1] );
|
||||
;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
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(atoi(sParams[1]), 0) );
|
||||
out.SetMeter( max(StringToInt(sParams[1]), 0) );
|
||||
}
|
||||
// new cases from Aldo_MX's fork:
|
||||
else if( sValueName=="PLAYER" )
|
||||
@@ -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 = atoi(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();
|
||||
|
||||
@@ -286,12 +425,12 @@ static void LoadTags( const RString &str, Song &out )
|
||||
split( str, " - ", asBits, false );
|
||||
// Ignore the difficulty, since we get that elsewhere.
|
||||
if( asBits.size() == 3 && (
|
||||
!stricmp(asBits[2], "double") ||
|
||||
!stricmp(asBits[2], "easy") ||
|
||||
!stricmp(asBits[2], "normal") ||
|
||||
!stricmp(asBits[2], "hard") ||
|
||||
!stricmp(asBits[2], "crazy") ||
|
||||
!stricmp(asBits[2], "nightmare"))
|
||||
asBits[2].EqualsNoCase("double") ||
|
||||
asBits[2].EqualsNoCase("easy") ||
|
||||
asBits[2].EqualsNoCase("normal") ||
|
||||
asBits[2].EqualsNoCase("hard") ||
|
||||
asBits[2].EqualsNoCase("crazy") ||
|
||||
asBits[2].EqualsNoCase("nightmare"))
|
||||
)
|
||||
{
|
||||
asBits.erase( asBits.begin()+2, asBits.begin()+3 );
|
||||
@@ -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" )
|
||||
@@ -401,7 +540,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
/* TICKCOUNT will be used below if there are DM compliant BPM changes
|
||||
* and stops. It will be called again in LoadFromKSFFile for the
|
||||
* actual steps. */
|
||||
iTickCount = atoi( sParams[1] );
|
||||
iTickCount = StringToInt( sParams[1] );
|
||||
iTickCount = iTickCount > 0 ? iTickCount : 2; // again, Direct Move uses 4 as a default.
|
||||
// add a tickcount for those using the [Player]
|
||||
// CheckpointsUseTimeSignatures metric. -aj
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -346,8 +346,8 @@ static void ReadTimeSigs( const NameToData_t &mapNameToData, MeasureToTimeSig_t
|
||||
|
||||
// this is step or offset data. Looks like "#00705"
|
||||
const RString &sData = it->second;
|
||||
int iMeasureNo = atoi( sName.substr(1, 3).c_str() );
|
||||
int iPMSTrackNo = atoi( sName.substr(4, 2).c_str() );
|
||||
int iMeasureNo = StringToInt( sName.substr(1, 3) );
|
||||
int iPMSTrackNo = StringToInt( sName.substr(4, 2) );
|
||||
if( iPMSTrackNo == PMS_TRACK_TIME_SIG )
|
||||
out[iMeasureNo] = StringToFloat( sData );
|
||||
}
|
||||
@@ -367,9 +367,9 @@ static bool LoadFromPMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
int iPlayer = -1;
|
||||
RString sData;
|
||||
if( GetTagFromMap( mapNameToData, "#player", sData ) )
|
||||
iPlayer = atoi(sData);
|
||||
iPlayer = StringToInt(sData);
|
||||
if( GetTagFromMap( mapNameToData, "#playlevel", sData ) )
|
||||
out.SetMeter( atoi(sData) );
|
||||
out.SetMeter( StringToInt(sData) );
|
||||
|
||||
NoteData ndNotes;
|
||||
ndNotes.SetNumTracks( NUM_PMS_TRACKS );
|
||||
@@ -396,8 +396,8 @@ static bool LoadFromPMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
continue;
|
||||
|
||||
// this is step or offset data. Looks like "#00705"
|
||||
int iMeasureNo = atoi( sName.substr(1,3).c_str() );
|
||||
int iRawTrackNum = atoi( sName.substr(4,2).c_str() );
|
||||
int iMeasureNo = StringToInt( sName.substr(1,3) );
|
||||
int iRawTrackNum = StringToInt( sName.substr(4,2) );
|
||||
int iRowNo = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||
float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||
const RString &sNoteData = it->second;
|
||||
@@ -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
|
||||
@@ -649,8 +649,8 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
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 iPMSTrackNo = atoi( sName.substr(4, 2).c_str() );
|
||||
int iMeasureNo = StringToInt( sName.substr(1, 3) );
|
||||
int iPMSTrackNo = StringToInt( sName.substr(4, 2) );
|
||||
int iStepIndex = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut );
|
||||
float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut );
|
||||
int iRowsPerMeasure = BeatToNoteRow( fBeatsPerMeasure );
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+369
-330
@@ -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,
|
||||
@@ -45,7 +40,7 @@ void SMLoader::LoadFromSMTokens(
|
||||
out.m_StepsType = GAMEMAN->StringToStepsType( sStepsType );
|
||||
out.SetDescription( sDescription );
|
||||
out.SetCredit( sDescription ); // this is often used for both.
|
||||
out.SetDifficulty( DwiCompatibleStringToDifficulty(sDifficulty) );
|
||||
out.SetDifficulty( StringToDifficulty(sDifficulty) );
|
||||
|
||||
// Handle hacks that originated back when StepMania didn't have
|
||||
// Difficulty_Challenge. (At least v1.64, possibly v3.0 final...)
|
||||
@@ -60,7 +55,7 @@ void SMLoader::LoadFromSMTokens(
|
||||
out.SetDifficulty( Difficulty_Challenge );
|
||||
}
|
||||
|
||||
out.SetMeter( atoi(sMeter) );
|
||||
out.SetMeter( StringToInt(sMeter) );
|
||||
vector<RString> saValues;
|
||||
split( sRadarValues, ",", saValues, true );
|
||||
int categories = NUM_RadarCategory - 1; // Fakes aren't counted in the radar values.
|
||||
@@ -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 - negBeat);
|
||||
out.AddWarpSegment( new_seg );
|
||||
|
||||
negBeat = -1;
|
||||
negBPM = 1;
|
||||
}
|
||||
// too fast. make it a warp.
|
||||
if( fNewBPM > FAST_BPM_WARP )
|
||||
{
|
||||
highspeedBeat = fBeat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add in a warp.
|
||||
if( highspeedBeat > 0 )
|
||||
{
|
||||
WarpSegment new_seg(highspeedBeat, fBeat - highspeedBeat);
|
||||
out.AddWarpSegment( new_seg );
|
||||
highspeedBeat = -1;
|
||||
}
|
||||
{
|
||||
BPMSegment new_seg( BeatToNoteRow( fBeat ), fNewBPM );
|
||||
out.AddBPMSegment( new_seg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bNotEmpty;
|
||||
}
|
||||
|
||||
void 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, 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 = atoi( vs2[1] );
|
||||
seg.m_iDenominator = atoi( 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 = atoi( 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());
|
||||
@@ -401,8 +493,17 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString
|
||||
change.m_sTransition = aBGChangeValues[8];
|
||||
// fall through
|
||||
case 8:
|
||||
{
|
||||
RString tmp = aBGChangeValues[7];
|
||||
tmp.MakeLower();
|
||||
if( ( tmp.find(".ini") != string::npos || tmp.find(".xml") != string::npos )
|
||||
&& !PREFSMAN->m_bQuirksMode )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
change.m_def.m_sFile2 = aBGChangeValues[7];
|
||||
// fall through
|
||||
}
|
||||
case 7:
|
||||
change.m_def.m_sEffect = aBGChangeValues[6];
|
||||
// fall through
|
||||
@@ -411,7 +512,7 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString
|
||||
// Backward compatibility:
|
||||
if( change.m_def.m_sEffect.empty() )
|
||||
{
|
||||
bool bLoop = atoi( aBGChangeValues[5] ) != 0;
|
||||
bool bLoop = StringToInt( aBGChangeValues[5] ) != 0;
|
||||
if( !bLoop )
|
||||
change.m_def.m_sEffect = SBE_StretchNoLoop;
|
||||
}
|
||||
@@ -421,7 +522,7 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString
|
||||
// Backward compatibility:
|
||||
if( change.m_def.m_sEffect.empty() )
|
||||
{
|
||||
bool bRewindMovie = atoi( aBGChangeValues[4] ) != 0;
|
||||
bool bRewindMovie = StringToInt( aBGChangeValues[4] ) != 0;
|
||||
if( bRewindMovie )
|
||||
change.m_def.m_sEffect = SBE_StretchRewind;
|
||||
}
|
||||
@@ -430,14 +531,23 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString
|
||||
// param 9 overrides this.
|
||||
// Backward compatibility:
|
||||
if( change.m_sTransition.empty() )
|
||||
change.m_sTransition = (atoi( aBGChangeValues[3] ) != 0) ? "CrossFade" : "";
|
||||
change.m_sTransition = (StringToInt( aBGChangeValues[3] ) != 0) ? "CrossFade" : "";
|
||||
// fall through
|
||||
case 3:
|
||||
change.m_fRate = StringToFloat( aBGChangeValues[2] );
|
||||
// fall through
|
||||
case 2:
|
||||
{
|
||||
RString tmp = aBGChangeValues[1];
|
||||
tmp.MakeLower();
|
||||
if( ( tmp.find(".ini") != string::npos || tmp.find(".xml") != string::npos )
|
||||
&& !PREFSMAN->m_bQuirksMode )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
change.m_def.m_sFile1 = aBGChangeValues[1];
|
||||
// fall through
|
||||
}
|
||||
case 1:
|
||||
change.m_fStartBeat = StringToFloat( aBGChangeValues[0] );
|
||||
// fall through
|
||||
@@ -457,8 +567,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 +622,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" )
|
||||
@@ -560,12 +658,12 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
else if( sValueName=="HASMUSIC" )
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_bHasMusic = atoi( sParams[1] ) != 0;
|
||||
out.m_bHasMusic = StringToInt( sParams[1] ) != 0;
|
||||
}
|
||||
else if( sValueName=="HASBANNER" )
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_bHasBanner = atoi( sParams[1] ) != 0;
|
||||
out.m_bHasBanner = StringToInt( sParams[1] ) != 0;
|
||||
}
|
||||
|
||||
else if( sValueName=="SAMPLESTART" )
|
||||
@@ -596,20 +694,20 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
|
||||
else if( sValueName=="SELECTABLE" )
|
||||
{
|
||||
if(!stricmp(sParams[1],"YES"))
|
||||
if(sParams[1].EqualsNoCase("YES"))
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
else if(!stricmp(sParams[1],"NO"))
|
||||
else if(sParams[1].EqualsNoCase("NO"))
|
||||
out.m_SelectionDisplay = out.SHOW_NEVER;
|
||||
// ROULETTE from 3.9. It was removed since UnlockManager can serve
|
||||
// the same purpose somehow. This, of course, assumes you're using
|
||||
// unlocks. -aj
|
||||
else if(!stricmp(sParams[1],"ROULETTE"))
|
||||
else if(sParams[1].EqualsNoCase("ROULETTE"))
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
/* The following two cases are just fixes to make sure simfiles that
|
||||
* used 3.9+ features are not excluded here */
|
||||
else if(!stricmp(sParams[1],"ES") || !stricmp(sParams[1],"OMES"))
|
||||
else if(sParams[1].EqualsNoCase("ES") || sParams[1].EqualsNoCase("OMES"))
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
else if( atoi(sParams[1]) > 0 )
|
||||
else if( StringToInt(sParams[1]) > 0 )
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
else
|
||||
LOG->UserLog( "Song file", sPath, "has an unknown #SELECTABLE value, \"%s\"; ignored.", sParams[1].c_str() );
|
||||
@@ -617,27 +715,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 +739,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 +750,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 +771,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 +884,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 +964,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
|
||||
|
||||
+489
-394
@@ -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 - negBeat);
|
||||
out.AddWarpSegment( new_seg );
|
||||
|
||||
negBeat = -1;
|
||||
negBPM = 1;
|
||||
}
|
||||
// too fast. make it a warp.
|
||||
if( fNewBPM > FAST_BPM_WARP )
|
||||
{
|
||||
highspeedBeat = fBeat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add in a warp.
|
||||
if( highspeedBeat > 0 )
|
||||
{
|
||||
WarpSegment new_seg(highspeedBeat, fBeat - highspeedBeat);
|
||||
out.AddWarpSegment( new_seg );
|
||||
highspeedBeat = -1;
|
||||
}
|
||||
{
|
||||
BPMSegment new_seg( BeatToNoteRow( fBeat ), fNewBPM );
|
||||
out.AddBPMSegment( new_seg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bNotEmpty;
|
||||
}
|
||||
|
||||
void SMALoader::ProcessStops( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<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, fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
|
||||
negBeat = -1;
|
||||
negPause = 0;
|
||||
}
|
||||
|
||||
if( fFreezeSeconds < 0.0f )
|
||||
{
|
||||
negBeat = fFreezeBeat;
|
||||
negPause = -fFreezeSeconds;
|
||||
}
|
||||
else if( fFreezeSeconds > 0.0f )
|
||||
{
|
||||
StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
|
||||
out.AddStopSegment( ss );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Process the prior stop if there was one.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
WarpSegment ws( negBeat, fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
}
|
||||
}
|
||||
|
||||
void SMALoader::ProcessDelays( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<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( DwiCompatibleStringToDifficulty(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( atoi(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,22 +543,57 @@ 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(!stricmp(sParams[1],"YES"))
|
||||
if(sParams[1].EqualsNoCase("YES"))
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
else if(!stricmp(sParams[1],"NO"))
|
||||
else if(sParams[1].EqualsNoCase("NO"))
|
||||
out.m_SelectionDisplay = out.SHOW_NEVER;
|
||||
// ROULETTE from 3.9. It was removed since UnlockManager can serve
|
||||
// the same purpose somehow. This, of course, assumes you're using
|
||||
// unlocks. -aj
|
||||
else if(!stricmp(sParams[1],"ROULETTE"))
|
||||
else if(sParams[1].EqualsNoCase("ROULETTE"))
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
/* The following two cases are just fixes to make sure simfiles that
|
||||
* used 3.9+ features are not excluded here */
|
||||
else if(!stricmp(sParams[1],"ES") || !stricmp(sParams[1],"OMES"))
|
||||
else if(sParams[1].EqualsNoCase("ES") || sParams[1].EqualsNoCase("OMES"))
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
else if( atoi(sParams[1]) > 0 )
|
||||
else if( StringToInt(sParams[1]) > 0 )
|
||||
out.m_SelectionDisplay = out.SHOW_ALWAYS;
|
||||
else
|
||||
LOG->UserLog( "Song file", sPath, "has an unknown #SELECTABLE value, \"%s\"; ignored.", sParams[1].c_str() );
|
||||
@@ -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 = atoi( sParams[1] );
|
||||
}
|
||||
else if( sValueName=="BEATSPERMEASURE" )
|
||||
{
|
||||
TimeSignatureSegment new_seg;
|
||||
new_seg.m_iStartRow = 0;
|
||||
new_seg.m_iNumerator = atoi( 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
|
||||
|
||||
+282
-546
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( ";" );
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
#include "global.h"
|
||||
#include "NotesWriterJson.h"
|
||||
#include "TimingData.h"
|
||||
#include "json/value.h"
|
||||
#include "JsonUtil.h"
|
||||
#include "Song.h"
|
||||
#include "BackgroundUtil.h"
|
||||
#include "Steps.h"
|
||||
#include "NoteData.h"
|
||||
#include "GameManager.h"
|
||||
|
||||
void Serialize(const BPMSegment &seg, Json::Value &root)
|
||||
{
|
||||
root["Beat"] = NoteRowToBeat(seg.m_iStartRow);
|
||||
root["BPM"] = seg.m_fBPS * 60;
|
||||
}
|
||||
|
||||
static void Serialize(const StopSegment &seg, Json::Value &root)
|
||||
{
|
||||
root["Beat"] = NoteRowToBeat(seg.m_iStartRow);
|
||||
root["Seconds"] = seg.m_fStopSeconds;
|
||||
}
|
||||
|
||||
static void Serialize(const TimingData &td, Json::Value &root)
|
||||
{
|
||||
JsonUtil::SerializeVectorObjects( td.m_BPMSegments, Serialize, root["BpmSegments"] );
|
||||
JsonUtil::SerializeVectorObjects( td.m_StopSegments, Serialize, root["StopSegments"] );
|
||||
}
|
||||
|
||||
static void Serialize(const LyricSegment &o, Json::Value &root)
|
||||
{
|
||||
root["StartTime"] = (float)o.m_fStartTime;
|
||||
root["Lyric"] = o.m_sLyric;
|
||||
root["Color"] = o.m_Color.ToString();
|
||||
}
|
||||
|
||||
static void Serialize(const BackgroundDef &o, Json::Value &root)
|
||||
{
|
||||
root["Effect"] = o.m_sEffect;
|
||||
root["File1"] = o.m_sFile1;
|
||||
root["File2"] = o.m_sFile2;
|
||||
root["Color1"] = o.m_sColor1;
|
||||
}
|
||||
|
||||
static void Serialize(const BackgroundChange &o, Json::Value &root )
|
||||
{
|
||||
Serialize( o.m_def, root["Def"] );
|
||||
root["StartBeat"] = o.m_fStartBeat;
|
||||
root["Rate"] = o.m_fRate;
|
||||
root["Transition"] = o.m_sTransition;
|
||||
}
|
||||
|
||||
static void Serialize( const TapNote &o, Json::Value &root )
|
||||
{
|
||||
root = Json::Value(Json::objectValue);
|
||||
|
||||
if( o.type != TapNote::tap )
|
||||
root["Type"] = (int)o.type;
|
||||
if( o.type == TapNote::hold_head )
|
||||
root["SubType"] = (int)o.subType;
|
||||
//root["Source"] = (int)source;
|
||||
if( !o.sAttackModifiers.empty() )
|
||||
root["AttackModifiers"] = o.sAttackModifiers;
|
||||
if( o.fAttackDurationSeconds > 0 )
|
||||
root["AttackDurationSeconds"] = o.fAttackDurationSeconds;
|
||||
if( o.iKeysoundIndex != -1 )
|
||||
root["KeysoundIndex"] = o.iKeysoundIndex;
|
||||
if( o.iDuration > 0 )
|
||||
root["Duration"] = o.iDuration;
|
||||
if( o.pn != PLAYER_INVALID )
|
||||
root["PlayerNumber"] = (int)o.pn;
|
||||
}
|
||||
|
||||
static void Serialize( const NoteData &o, Json::Value &root )
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
for(int t=0; t < o.GetNumTracks(); t++ )
|
||||
{
|
||||
NoteData::TrackMap::const_iterator begin, end;
|
||||
o.GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
|
||||
//NoteData::TrackMap tm = o.GetTrack(t);
|
||||
//FOREACHM_CONST( int, TapNote, tm, iter )
|
||||
for( ; begin != end; ++begin )
|
||||
{
|
||||
int iRow = begin->first;
|
||||
TapNote tn = begin->second;
|
||||
root.resize( root.size()+1 );
|
||||
Json::Value &root2 = root[ root.size()-1 ];
|
||||
root2 = Json::Value(Json::arrayValue);
|
||||
root2.resize(3);
|
||||
root2[(unsigned)0] = NoteRowToBeat(iRow);
|
||||
root2[1] = t;
|
||||
Serialize( tn, root2[2] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Serialize( const RadarValues &o, Json::Value &root )
|
||||
{
|
||||
FOREACH_ENUM( RadarCategory, rc )
|
||||
{
|
||||
root[ RadarCategoryToString(rc) ] = o.m_Values.f[rc];
|
||||
}
|
||||
}
|
||||
|
||||
static void Serialize( const Steps &o, Json::Value &root )
|
||||
{
|
||||
root["StepsType"] = StringConversion::ToString(o.m_StepsType);
|
||||
|
||||
o.Decompress();
|
||||
|
||||
NoteData nd;
|
||||
o.GetNoteData( nd );
|
||||
Serialize( nd, root["NoteData"] );
|
||||
root["Hash"] = o.GetHash();
|
||||
root["Description"] = o.GetDescription();
|
||||
root["Difficulty"] = DifficultyToString(o.GetDifficulty());
|
||||
root["Meter"] = o.GetMeter();
|
||||
Serialize( o.GetRadarValues( PLAYER_1 ), root["RadarValues"] );
|
||||
}
|
||||
|
||||
|
||||
bool NotesWriterJson::WriteSong( const RString &sFile, const Song &out, bool bWriteSteps )
|
||||
{
|
||||
Json::Value root;
|
||||
root["SongDir"] = out.GetSongDir();
|
||||
root["GroupName"] = out.m_sGroupName;
|
||||
root["Title"] = out.m_sMainTitle;
|
||||
root["SubTitle"] = out.m_sSubTitle;
|
||||
root["Artist"] = out.m_sArtist;
|
||||
root["TitleTranslit"] = out.m_sMainTitleTranslit;
|
||||
root["SubTitleTranslit"] = out.m_sSubTitleTranslit;
|
||||
root["Genre"] = out.m_sGenre;
|
||||
root["Credit"] = out.m_sCredit;
|
||||
root["Banner"] = out.m_sBannerFile;
|
||||
root["Background"] = out.m_sBackgroundFile;
|
||||
root["LyricsFile"] = out.m_sLyricsFile;
|
||||
root["CDTitle"] = out.m_sCDTitleFile;
|
||||
root["Music"] = out.m_sMusicFile;
|
||||
root["Offset"] = out.m_SongTiming.m_fBeat0OffsetInSeconds;
|
||||
root["SampleStart"] = out.m_fMusicSampleStartSeconds;
|
||||
root["SampleLength"] = out.m_fMusicSampleLengthSeconds;
|
||||
if( out.m_SelectionDisplay == Song::SHOW_ALWAYS )
|
||||
root["Selectable"] = "YES";
|
||||
else if( out.m_SelectionDisplay == Song::SHOW_NEVER )
|
||||
root["Selectable"] = "NO";
|
||||
else
|
||||
root["Selectable"] = "YES";
|
||||
|
||||
root["FirstBeat"] = out.m_fFirstBeat;
|
||||
root["LastBeat"] = out.m_fLastBeat;
|
||||
root["SongFileName"] = out.m_sSongFileName;
|
||||
root["HasMusic"] = out.m_bHasMusic;
|
||||
root["HasBanner"] = out.m_bHasBanner;
|
||||
root["MusicLengthSeconds"] = out.m_fMusicLengthSeconds;
|
||||
|
||||
root["DisplayBpmType"] = StringConversion::ToString(out.m_DisplayBPMType);
|
||||
if( out.m_DisplayBPMType == DISPLAY_BPM_SPECIFIED )
|
||||
{
|
||||
root["SpecifiedBpmMin"] = out.m_fSpecifiedBPMMin;
|
||||
root["SpecifiedBpmMax"] = out.m_fSpecifiedBPMMax;
|
||||
}
|
||||
|
||||
Serialize( out.m_SongTiming, root["TimingData"] );
|
||||
JsonUtil::SerializeVectorObjects( out.m_LyricSegments, Serialize, root["LyricSegments"] );
|
||||
|
||||
{
|
||||
Json::Value &root2 = root["BackgroundChanges"];
|
||||
FOREACH_BackgroundLayer( bl )
|
||||
{
|
||||
Json::Value &root3 = root2[bl];
|
||||
const vector<BackgroundChange> &vBgc = out.GetBackgroundChanges(bl);
|
||||
JsonUtil::SerializeVectorObjects( vBgc, Serialize, root3 );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const vector<BackgroundChange> &vBgc = out.GetForegroundChanges();
|
||||
JsonUtil::SerializeVectorObjects( vBgc, Serialize, root["ForegroundChanges"] );
|
||||
}
|
||||
|
||||
JsonUtil::SerializeArrayValues( out.m_vsKeysoundFile, root["KeySounds"] );
|
||||
|
||||
if( bWriteSteps )
|
||||
{
|
||||
vector<const Steps*> vpSteps;
|
||||
FOREACH_CONST( Steps*, out.GetAllSteps(), iter )
|
||||
{
|
||||
if( (*iter)->IsAutogen() )
|
||||
continue;
|
||||
vpSteps.push_back( *iter );
|
||||
}
|
||||
JsonUtil::SerializeVectorPointers<Steps>( vpSteps, Serialize, root["Charts"] );
|
||||
}
|
||||
|
||||
return JsonUtil::WriteFile( root, sFile, false );
|
||||
}
|
||||
|
||||
bool NotesWriterJson::WriteSteps( const RString &sFile, const Steps &out )
|
||||
{
|
||||
Json::Value root;
|
||||
Serialize( out, root );
|
||||
return JsonUtil::WriteFile( root, sFile, false );
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
||||
* 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,40 @@
|
||||
/* NotesWriterJson - Writes a Song to a .json file. */
|
||||
|
||||
#ifndef NotesWriterJson_H
|
||||
#define NotesWriterJson_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
|
||||
namespace NotesWriterJson
|
||||
{
|
||||
static bool WriteSong( const RString &sFile, const Song &out, bool bWriteSteps );
|
||||
static bool WriteSteps( const RString &sFile, const Steps &out );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2001-2010 Chris Danford, Glenn Maynard
|
||||
* 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.
|
||||
*/
|
||||
+39
-79
@@ -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 )
|
||||
@@ -274,11 +234,11 @@ static RString GetSMNotesTag( const Song &song, const Steps &in )
|
||||
RString desc = (USE_CREDIT ? in.GetCredit() : in.GetDescription());
|
||||
lines.push_back( ssprintf( " %s:", SmEscape(desc).c_str() ) );
|
||||
lines.push_back( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) );
|
||||
lines.push_back( ssprintf( " %d:", clamp( in.GetMeter(), MIN_METER, MAX_METER ) ) );
|
||||
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.
|
||||
|
||||
+131
-131
@@ -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.
|
||||
@@ -294,7 +300,7 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
lines.push_back( ssprintf( "#DESCRIPTION:%s;", SmEscape(in.GetDescription()).c_str() ) );
|
||||
lines.push_back( ssprintf( "#CHARTSTYLE:%s;", SmEscape(in.GetChartStyle()).c_str() ) );
|
||||
lines.push_back( ssprintf( "#DIFFICULTY:%s;", DifficultyToString(in.GetDifficulty()).c_str() ) );
|
||||
lines.push_back( ssprintf( "#METER:%d;", clamp( in.GetMeter(), MIN_METER, MAX_METER ) ) );
|
||||
lines.push_back( ssprintf( "#METER:%d;", in.GetMeter() ) );
|
||||
|
||||
vector<RString> asRadarValues;
|
||||
FOREACH_PlayerNumber( pn )
|
||||
@@ -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:" ) );
|
||||
|
||||
@@ -154,7 +154,7 @@ public:
|
||||
RageException::Throw( "Parse error in \"ScreenOptionsMaster::%s\".", sParam.c_str() );
|
||||
|
||||
m_Def.m_bOneChoiceForAllPlayers = false;
|
||||
const int NumCols = atoi( lCmds.v[0].m_vsArgs[0] );
|
||||
const int NumCols = StringToInt( lCmds.v[0].m_vsArgs[0] );
|
||||
for( unsigned i=1; i<lCmds.v.size(); i++ )
|
||||
{
|
||||
const Command &cmd = lCmds.v[i];
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
else if( sName == "selectone" ) m_Def.m_selectType = SELECT_ONE;
|
||||
else if( sName == "selectnone" ) m_Def.m_selectType = SELECT_NONE;
|
||||
else if( sName == "showoneinrow" ) m_Def.m_layoutType = LAYOUT_SHOW_ONE_IN_ROW;
|
||||
else if( sName == "default" ) m_Def.m_iDefault = atoi( cmd.GetArg(1).s ) - 1; // match ENTRY_MODE
|
||||
else if( sName == "default" ) m_Def.m_iDefault = StringToInt( cmd.GetArg(1).s ) - 1; // match ENTRY_MODE
|
||||
else if( sName == "reloadrowmessages" )
|
||||
{
|
||||
for( unsigned a=1; a<cmd.m_vsArgs.size(); a++ )
|
||||
@@ -177,7 +177,7 @@ public:
|
||||
for( unsigned a=1; a<cmd.m_vsArgs.size(); a++ )
|
||||
{
|
||||
RString sArg = cmd.m_vsArgs[a];
|
||||
PlayerNumber pn = (PlayerNumber)(atoi(sArg)-1);
|
||||
PlayerNumber pn = (PlayerNumber)(StringToInt(sArg)-1);
|
||||
ASSERT( pn >= 0 && pn < NUM_PLAYERS );
|
||||
m_Def.m_vEnabledForPlayers.insert( pn );
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ OptionsCursor::OptionsCursor( const OptionsCursor &cpy ):
|
||||
m_iOriginalCanGoLeftX( cpy.m_iOriginalCanGoLeftX ),
|
||||
m_iOriginalCanGoRightX( cpy.m_iOriginalCanGoRightX )
|
||||
{
|
||||
/* Re-add children, or m_SubActors will point to cpy's children and not our own. */
|
||||
// Re-add children, or m_SubActors will point to cpy's children and not our own.
|
||||
m_SubActors.clear();
|
||||
this->AddChild( m_sprMiddle );
|
||||
this->AddChild( m_sprLeft );
|
||||
|
||||
+2
-2
@@ -28,11 +28,11 @@ protected:
|
||||
AutoActor m_sprMiddle;
|
||||
AutoActor m_sprLeft;
|
||||
AutoActor m_sprRight;
|
||||
|
||||
|
||||
AutoActor m_sprCanGoLeft;
|
||||
AutoActor m_sprCanGoRight;
|
||||
|
||||
// save the metrics-set X because it gets oblitterated on a call to SetBarWidth
|
||||
// save the metrics-set X because it gets obliterated on a call to SetBarWidth
|
||||
int m_iOriginalLeftX;
|
||||
int m_iOriginalRightX;
|
||||
int m_iOriginalCanGoLeftX;
|
||||
|
||||
+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;
|
||||
|
||||
@@ -299,7 +299,7 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut
|
||||
{
|
||||
/* XXX We know what they want, is there any reason not to handle it? */
|
||||
/* Yes. We should be strict in handling the format. -Chris */
|
||||
sErrorOut = ssprintf("Invalid player options \"%s\"; did you mean '*%d'?", s->c_str(), atoi(*s) );
|
||||
sErrorOut = ssprintf("Invalid player options \"%s\"; did you mean '*%d'?", s->c_str(), StringToInt(*s) );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -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.
|
||||
|
||||
+1
-1
@@ -1922,7 +1922,7 @@ RString Profile::MakeUniqueFileNameNoExtension( RString sDir, RString sFileNameB
|
||||
continue;
|
||||
|
||||
ASSERT( matches.size() == 1 );
|
||||
iIndex = atoi( matches[0] )+1;
|
||||
iIndex = StringToInt( matches[0] )+1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ bool ProfileManager::CreateLocalProfile( RString sName, RString &sProfileIDOut )
|
||||
vector<RString> vs;
|
||||
GetLocalProfileIDs( vs );
|
||||
FOREACH_CONST( RString, vs, s )
|
||||
iMaxProfileNumber = atoi( *s );
|
||||
iMaxProfileNumber = StringToInt( *s );
|
||||
|
||||
int iProfileNumber = iMaxProfileNumber + 1;
|
||||
RString sProfileID = ssprintf( "%08d", iProfileNumber );
|
||||
@@ -833,6 +833,7 @@ public:
|
||||
static int GetNumLocalProfiles( T* p, lua_State *L ) { lua_pushnumber(L, p->GetNumLocalProfiles() ); return 1; }
|
||||
static int GetProfileDir( T* p, lua_State *L ) { lua_pushstring(L, p->GetProfileDir(Enum::Check<ProfileSlot>(L, 1)) ); return 1; }
|
||||
static int IsSongNew( T* p, lua_State *L ) { lua_pushboolean(L, p->IsSongNew(Luna<Song>::check(L,1)) ); return 1; }
|
||||
static int ProfileWasLoadedFromMemoryCard( T* p, lua_State *L ) { lua_pushboolean(L, p->ProfileWasLoadedFromMemoryCard(Enum::Check<PlayerNumber>(L, 1)) ); return 1; }
|
||||
|
||||
LunaProfileManager()
|
||||
{
|
||||
@@ -847,6 +848,7 @@ public:
|
||||
ADD_METHOD( GetNumLocalProfiles );
|
||||
ADD_METHOD( GetProfileDir );
|
||||
ADD_METHOD( IsSongNew );
|
||||
ADD_METHOD( ProfileWasLoadedFromMemoryCard );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ static void GetResolutionFromFileName( RString sPath, int &iWidth, int &iHeight
|
||||
if( !re.Compare(sPath, asMatches) )
|
||||
return;
|
||||
|
||||
iWidth = atoi( asMatches[0].c_str() );
|
||||
iHeight = atoi( asMatches[1].c_str() );
|
||||
iWidth = StringToInt( asMatches[0] );
|
||||
iHeight = StringToInt( asMatches[1] );
|
||||
}
|
||||
|
||||
RageBitmapTexture::RageBitmapTexture( RageTextureID name ) :
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include <memory>
|
||||
|
||||
#if defined(_WINDOWS)
|
||||
#include "../extern/zlib/zlib.h"
|
||||
#include "zlib.h"
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "../extern/zlib/zdll.lib")
|
||||
#pragma comment(lib, "zdll.lib")
|
||||
#endif
|
||||
#elif defined(MACOSX)
|
||||
/* Since crypto++ was added to the repository, <zlib.h> includes the zlib.h
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ RageSound::RageSound():
|
||||
m_Mutex( "RageSound" ), m_pSource(NULL),
|
||||
m_sFilePath(""), m_Param(), m_iStreamFrame(0),
|
||||
m_iStoppedSourceFrame(0), m_bPlaying(false),
|
||||
m_sError(""), m_bDeleteWhenFinished(false)
|
||||
m_bDeleteWhenFinished(false), m_sError("")
|
||||
{
|
||||
ASSERT( SOUNDMAN );
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#if defined(_WINDOWS) || defined(MACOSX)
|
||||
#include "../extern/mad-0.15.1b/mad.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "../extern/mad-0.15.1b/msvc++/Release/libmad.lib")
|
||||
#pragma comment(lib, "libmad.lib")
|
||||
#endif //_MSC_VER
|
||||
#else
|
||||
#include <mad.h>
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
// work around namespace bugs in win32/libjpeg:
|
||||
#define XMD_H
|
||||
#undef FAR
|
||||
#include "../extern/libjpeg/jpeglib.h"
|
||||
#include "../extern/libjpeg/jerror.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jerror.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "../extern/libjpeg/jpeg.lib")
|
||||
#pragma comment(lib, "jpeg.lib")
|
||||
#endif
|
||||
|
||||
#pragma warning(disable: 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
|
||||
#if defined(_WINDOWS)
|
||||
# include "../extern/libpng/include/png.h"
|
||||
# include "png.h"
|
||||
# if defined(_MSC_VER)
|
||||
# pragma comment(lib, "../extern/libpng/lib/libpng.lib")
|
||||
# pragma comment(lib, "libpng.lib")
|
||||
# pragma warning(disable: 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
|
||||
# endif // _MSC_VER
|
||||
#else
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace jpeg
|
||||
|
||||
// Pull in JPEG library here.
|
||||
#if defined _MSC_VER
|
||||
#pragma comment(lib, "../extern/libjpeg/jpeg.lib")
|
||||
#pragma comment(lib, "jpeg.lib")
|
||||
#endif
|
||||
|
||||
#define OUTPUT_BUFFER_SIZE 4096
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "RageUtil.h"
|
||||
|
||||
#if defined(WINDOWS)
|
||||
#include "../extern/libpng/include/png.h"
|
||||
#include "png.h"
|
||||
#if defined(_MSC_VER)
|
||||
# pragma comment(lib, "../extern/libpng/lib/libpng.lib")
|
||||
# pragma comment(lib, "libpng.lib")
|
||||
#pragma warning(disable: 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
|
||||
#endif
|
||||
#else
|
||||
|
||||
+2
-2
@@ -51,8 +51,8 @@ void RageTexture::GetFrameDimensionsFromFileName( RString sPath, int* piFramesWi
|
||||
*piFramesWide = *piFramesHigh = 1;
|
||||
return;
|
||||
}
|
||||
*piFramesWide = atoi(asMatch[0]);
|
||||
*piFramesHigh = atoi(asMatch[1]);
|
||||
*piFramesWide = StringToInt(asMatch[0]);
|
||||
*piFramesHigh = StringToInt(asMatch[1]);
|
||||
}
|
||||
|
||||
const RectF *RageTexture::GetTextureCoordRect( int iFrameNo ) const
|
||||
|
||||
+18
-3
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <numeric>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -193,8 +194,8 @@ float HHMMSSToSeconds( const RString &sHHMMSS )
|
||||
arrayBits.insert(arrayBits.begin(), "0" ); // pad missing bits
|
||||
|
||||
float fSeconds = 0;
|
||||
fSeconds += atoi( arrayBits[0] ) * 60 * 60;
|
||||
fSeconds += atoi( arrayBits[1] ) * 60;
|
||||
fSeconds += StringToInt( arrayBits[0] ) * 60 * 60;
|
||||
fSeconds += StringToInt( arrayBits[1] ) * 60;
|
||||
fSeconds += StringToFloat( arrayBits[2] );
|
||||
|
||||
return fSeconds;
|
||||
@@ -1695,6 +1696,20 @@ void MakeLower( wchar_t *p, size_t iLen )
|
||||
UnicodeUpperLower( p, iLen, g_LowerCase );
|
||||
}
|
||||
|
||||
int StringToInt( const RString &sString )
|
||||
{
|
||||
int ret;
|
||||
istringstream ( sString ) >> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
RString IntToString( const int &iNum )
|
||||
{
|
||||
stringstream ss;
|
||||
ss << iNum;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
float StringToFloat( const RString &sString )
|
||||
{
|
||||
float ret = strtof( sString, NULL );
|
||||
@@ -2141,7 +2156,7 @@ namespace StringConversion
|
||||
if( sValue.size() == 0 )
|
||||
return false;
|
||||
|
||||
out = (atoi(sValue) != 0);
|
||||
out = (StringToInt(sValue) != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -405,6 +405,16 @@ void MakeUpper( char *p, size_t iLen );
|
||||
void MakeLower( char *p, size_t iLen );
|
||||
void MakeUpper( wchar_t *p, size_t iLen );
|
||||
void MakeLower( wchar_t *p, size_t iLen );
|
||||
/**
|
||||
* @brief Have a standard way of converting Strings to integers.
|
||||
* @param sString the string to convert.
|
||||
* @return the integer we are after. */
|
||||
int StringToInt( const RString &sString );
|
||||
/**
|
||||
* @brief Have a standard way of converting integers to Strings.
|
||||
* @param iNum the integer to convert.
|
||||
* @return the string we are after. */
|
||||
RString IntToString( const int &iNum );
|
||||
float StringToFloat( const RString &sString );
|
||||
bool StringToFloat( const RString &sString, float &fOut );
|
||||
|
||||
|
||||
@@ -360,14 +360,14 @@ void ScoreKeeperNormal::AddScoreInternal( TapNoteScore score )
|
||||
|
||||
}
|
||||
|
||||
ASSERT( iScore >= 0 );
|
||||
ASSERT_M( iScore >= 0, "iScore < 0 before re-rounding" );
|
||||
|
||||
// Undo rounding from the last tap, and re-round.
|
||||
iScore += m_iScoreRemainder;
|
||||
m_iScoreRemainder = (iScore % m_iRoundTo);
|
||||
iScore = iScore - m_iScoreRemainder;
|
||||
|
||||
ASSERT( iScore >= 0 );
|
||||
ASSERT_M( iScore >= 0, "iScore < 0 after re-rounding" );
|
||||
|
||||
// LOG->Trace( "score: %i", iScore );
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+539
-306
File diff suppressed because it is too large
Load Diff
+76
-29
@@ -71,8 +71,8 @@ enum EditButton
|
||||
EDIT_BUTTON_SCROLL_NEXT,
|
||||
EDIT_BUTTON_SCROLL_PREV,
|
||||
|
||||
EDIT_BUTTON_LABEL_NEXT,
|
||||
EDIT_BUTTON_LABEL_PREV,
|
||||
EDIT_BUTTON_LABEL_NEXT, /**< Jump to the start of the next label downward. */
|
||||
EDIT_BUTTON_LABEL_PREV, /**< Jump to the start of the previous label upward. */
|
||||
|
||||
// These are modifiers to EDIT_BUTTON_SCROLL_*.
|
||||
EDIT_BUTTON_SCROLL_SELECT,
|
||||
@@ -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,
|
||||
@@ -128,8 +129,7 @@ enum EditButton
|
||||
EDIT_BUTTON_SAMPLE_LENGTH_UP,
|
||||
EDIT_BUTTON_SAMPLE_LENGTH_DOWN,
|
||||
|
||||
// This modifies offset, BPM, and stop segment changes.
|
||||
EDIT_BUTTON_ADJUST_FINE,
|
||||
EDIT_BUTTON_ADJUST_FINE, /**< This button modifies offset, BPM, and stop segment changes. */
|
||||
|
||||
EDIT_BUTTON_SAVE, /**< Save the present changes into the chart. */
|
||||
|
||||
@@ -138,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
|
||||
@@ -166,7 +168,9 @@ struct MapEditToDI
|
||||
}
|
||||
};
|
||||
|
||||
// Like MapEditToDI, but maps GameButton instead of DeviceInput.
|
||||
/**
|
||||
* @brief This is similar to MapEditToDI,
|
||||
* but maps GameButton instead of DeviceInput. */
|
||||
struct MapEditButtonToMenuButton
|
||||
{
|
||||
GameButton button[NUM_EditButton][NUM_EDIT_TO_MENU_SLOTS];
|
||||
@@ -214,11 +218,14 @@ protected:
|
||||
|
||||
// Call this before modifying m_NoteDataEdit.
|
||||
void SaveUndo();
|
||||
// Revert m_NoteDataEdit using m_Undo.
|
||||
/** @brief Revert the last change made to m_NoteDataEdit. */
|
||||
void Undo();
|
||||
/** @brief Remove the previously stored NoteData to prevent undoing. */
|
||||
void ClearUndo();
|
||||
// Call this after modifying m_NoteDataEdit. It will Undo() if
|
||||
// MAX_NOTES_PER_MEASURE was exceeded.
|
||||
/**
|
||||
* @brief This is to be called after modifying m_NoteDataEdit.
|
||||
*
|
||||
* It will Undo itself if MAX_NOTES_PER_MEASURE was exceeded. */
|
||||
void CheckNumberOfNotesAndUndo();
|
||||
|
||||
void OnSnapModeChange();
|
||||
@@ -227,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;
|
||||
|
||||
@@ -252,7 +262,10 @@ protected:
|
||||
|
||||
// keep track of where we are and what we're doing
|
||||
float m_fTrailingBeat; // this approaches GAMESTATE->m_fSongBeat, which is the actual beat
|
||||
// The location we were at when shift was pressed, or -1 when shift isn't pressed:
|
||||
/**
|
||||
* @brief The location we were at when shift was pressed.
|
||||
*
|
||||
* If shift wasn't pressed, this will be -1. */
|
||||
int m_iShiftAnchor;
|
||||
|
||||
/** @brief The NoteData that has been cut or copied. */
|
||||
@@ -268,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
|
||||
@@ -311,22 +332,23 @@ protected:
|
||||
ThemeMetric<EditMode> EDIT_MODE;
|
||||
|
||||
public:
|
||||
/** @brief What are the choices that one can make on the main menu? */
|
||||
enum MainMenuChoice
|
||||
{
|
||||
play_selection,
|
||||
set_selection_start,
|
||||
set_selection_end,
|
||||
edit_steps_information,
|
||||
play_whole_song,
|
||||
play_whole_song, /**< Play the entire chart from the beginning. */
|
||||
play_selection_start_to_end,
|
||||
play_current_beat_to_end,
|
||||
save,
|
||||
save, /**< Save the current chart to disk. */
|
||||
revert_to_last_save,
|
||||
revert_from_disk,
|
||||
options,
|
||||
edit_song_info,
|
||||
edit_timing_data,
|
||||
play_preview_music,
|
||||
options, /**< Modify the PlayerOptions and SongOptions. */
|
||||
edit_song_info, /**< Edit some general information about the song. */
|
||||
edit_timing_data, /**< Edit the chart's timing data. */
|
||||
play_preview_music, /**< Play the song's preview music. */
|
||||
exit,
|
||||
save_on_exit,
|
||||
NUM_MAIN_MENU_CHOICES,
|
||||
@@ -361,13 +383,14 @@ public:
|
||||
};
|
||||
void HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAnswers, bool bAllowUndo = true );
|
||||
void HandleAreaMenuChoice( AreaMenuChoice c, bool bAllowUndo = true ) { const vector<int> v; HandleAreaMenuChoice( c, v, bAllowUndo ); }
|
||||
/** @brief How should the selected notes be transformed? */
|
||||
enum TurnType
|
||||
{
|
||||
left,
|
||||
right,
|
||||
mirror,
|
||||
shuffle,
|
||||
super_shuffle,
|
||||
left, /**< Turn the notes as if you were facing to the left. */
|
||||
right, /**< Turn the notes as if you were facing to the right. */
|
||||
mirror, /**< Turn the notes as if you were facing away from the machine. */
|
||||
shuffle, /**< Replace one column with another column. */
|
||||
super_shuffle, /**< Replace each note individually. */
|
||||
NUM_TURN_TYPES
|
||||
};
|
||||
enum TransformType
|
||||
@@ -421,10 +444,10 @@ public:
|
||||
{
|
||||
difficulty,
|
||||
meter,
|
||||
description,
|
||||
chartstyle,
|
||||
step_credit,
|
||||
predict_meter,
|
||||
description, /**< What is the description of this chart? */
|
||||
chartstyle, /**< How is this chart meant to be played? */
|
||||
step_credit, /**< Who wrote this individual chart? */
|
||||
predict_meter, /**< What does the game think this chart's rating should be? */
|
||||
tap_notes,
|
||||
jumps,
|
||||
hands,
|
||||
@@ -450,7 +473,6 @@ public:
|
||||
main_title_transliteration,
|
||||
sub_title_transliteration,
|
||||
artist_transliteration,
|
||||
beat_0_offset,
|
||||
last_beat_hint,
|
||||
preview_start,
|
||||
preview_length,
|
||||
@@ -463,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
|
||||
};
|
||||
|
||||
@@ -505,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
|
||||
@@ -535,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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user