[default -> Xcode4] Lion is approaching.
This commit is contained in:
+3
-3
@@ -105,9 +105,9 @@ void Actor::InitState()
|
||||
#endif
|
||||
m_fSecsIntoEffect = 0;
|
||||
m_fEffectDelta = 0;
|
||||
m_fEffectRampUp = 0.5;
|
||||
m_fEffectRampUp = 0.5f;
|
||||
m_fEffectHoldAtHalf = 0;
|
||||
m_fEffectRampDown = 0.5;
|
||||
m_fEffectRampDown = 0.5f;
|
||||
m_fEffectHoldAtZero = 0;
|
||||
m_fEffectOffset = 0;
|
||||
m_EffectClock = CLOCK_TIMER;
|
||||
@@ -118,7 +118,7 @@ void Actor::InitState()
|
||||
m_bVisible = true;
|
||||
m_fShadowLengthX = 0;
|
||||
m_fShadowLengthY = 0;
|
||||
m_ShadowColor = RageColor(0,0,0,0.5);
|
||||
m_ShadowColor = RageColor(0,0,0,0.5f);
|
||||
m_bIsAnimating = true;
|
||||
m_fHibernateSecondsLeft = 0;
|
||||
m_iDrawOrder = 0;
|
||||
|
||||
+28
-11
@@ -256,7 +256,7 @@ void AdjustSync::AutosyncTempo()
|
||||
// keep only a fraction of the data, such as the 80% with the lowest
|
||||
// error. However, throwing away the ones with high error should
|
||||
// be enough in most cases.
|
||||
float fFilteredError = 0.0;
|
||||
float fFilteredError = 0;
|
||||
s_iStepsFiltered = s_vAutosyncTempoData.size();
|
||||
FilterHighErrorPoints( s_vAutosyncTempoData, fSlope, fIntercept, ERROR_TOO_HIGH );
|
||||
s_iStepsFiltered -= s_vAutosyncTempoData.size();
|
||||
@@ -266,14 +266,23 @@ void AdjustSync::AutosyncTempo()
|
||||
|
||||
GAMESTATE->m_pCurSong->m_SongTiming.m_fBeat0OffsetInSeconds += fIntercept;
|
||||
const float fScaleBPM = 1.0f/(1.0f - fSlope);
|
||||
FOREACH( BPMSegment, GAMESTATE->m_pCurSong->m_SongTiming.m_BPMSegments, i )
|
||||
i->SetBPM( i->GetBPM() * fScaleBPM );
|
||||
TimingData &timing = GAMESTATE->m_pCurSong->m_SongTiming;
|
||||
vector<TimingSegment *> &bpms = timing.allTimingSegments[SEGMENT_BPM];
|
||||
for (unsigned i = 0; i < bpms.size(); i++)
|
||||
{
|
||||
BPMSegment *b = static_cast<BPMSegment *>(bpms[i]);
|
||||
b->SetBPM(b->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_SongTiming.m_StopSegments, i )
|
||||
i->SetPause(i->GetPause() * (1.0f - fSlope));
|
||||
vector<TimingSegment *> &stops = timing.allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
for (unsigned i = 0; i < stops.size(); i++)
|
||||
{
|
||||
StopSegment *s = static_cast<StopSegment *>(stops[i]);
|
||||
s->SetPause(s->GetPause() * (1.0f - fSlope));
|
||||
}
|
||||
|
||||
SCREENMAN->SystemMessage( AUTOSYNC_CORRECTION_APPLIED.GetValue() );
|
||||
}
|
||||
@@ -343,10 +352,14 @@ void AdjustSync::GetSyncChangeTextSong( vector<RString> &vsAddTo )
|
||||
}
|
||||
}
|
||||
|
||||
for( unsigned i=0; i< testing.m_BPMSegments.size(); i++ )
|
||||
vector<TimingSegment *> &bpmTest = testing.allTimingSegments[SEGMENT_BPM];
|
||||
vector<TimingSegment *> &bpmOrig = original.allTimingSegments[SEGMENT_BPM];
|
||||
for( unsigned i=0; i< bpmTest.size(); i++ )
|
||||
{
|
||||
float fOld = Quantize( original.m_BPMSegments[i].GetBPM(), 0.001f );
|
||||
float fNew = Quantize( testing.m_BPMSegments[i].GetBPM(), 0.001f );
|
||||
BPMSegment *bT = static_cast<BPMSegment *>(bpmTest[i]);
|
||||
BPMSegment *bO = static_cast<BPMSegment *>(bpmOrig[i]);
|
||||
float fOld = Quantize( bO->GetBPM(), 0.001f );
|
||||
float fNew = Quantize( bT->GetBPM(), 0.001f );
|
||||
float fDelta = fNew - fOld;
|
||||
|
||||
if( fabsf(fDelta) > 0.0001f )
|
||||
@@ -364,10 +377,14 @@ void AdjustSync::GetSyncChangeTextSong( vector<RString> &vsAddTo )
|
||||
}
|
||||
}
|
||||
|
||||
for( unsigned i=0; i< testing.m_StopSegments.size(); i++ )
|
||||
vector<TimingSegment *> &stopTest = testing.allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
vector<TimingSegment *> &stopOrig = original.allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
for( unsigned i=0; i< stopTest.size(); i++ )
|
||||
{
|
||||
float fOld = Quantize( original.m_StopSegments[i].GetPause(), 0.001f );
|
||||
float fNew = Quantize( testing.m_StopSegments[i].GetPause(), 0.001f );
|
||||
StopSegment *sT = static_cast<StopSegment *>(stopTest[i]);
|
||||
StopSegment *sO = static_cast<StopSegment *>(stopOrig[i]);
|
||||
float fOld = Quantize( sO->GetPause(), 0.001f );
|
||||
float fNew = Quantize( sT->GetPause(), 0.001f );
|
||||
float fDelta = fNew - fOld;
|
||||
|
||||
if( fabsf(fDelta) > 0.0001f )
|
||||
|
||||
+20
-15
@@ -56,10 +56,12 @@ static ThemeMetric<float> DRUNK_OFFSET_FREQUENCY( "ArrowEffects", "DrunkOffsetFr
|
||||
static ThemeMetric<float> DRUNK_ARROW_MAGNITUDE( "ArrowEffects", "DrunkArrowMagnitude" );
|
||||
static ThemeMetric<float> BEAT_OFFSET_HEIGHT( "ArrowEffects", "BeatOffsetHeight" );
|
||||
static ThemeMetric<float> BEAT_PI_HEIGHT( "ArrowEffects", "BeatPIHeight" );
|
||||
static ThemeMetric<float> MINI_PERCENT_BASE( "ArrowEffects", "MiniPercentBase" );
|
||||
static ThemeMetric<float> MINI_PERCENT_GATE( "ArrowEffects", "MiniPercentGate" );
|
||||
static ThemeMetric<float> TINY_PERCENT_BASE( "ArrowEffects", "TinyPercentBase" );
|
||||
static ThemeMetric<float> TINY_PERCENT_GATE( "ArrowEffects", "TinyPercentGate" );
|
||||
static ThemeMetric<bool> DIZZY_HOLD_HEADS( "ArrowEffects", "DizzyHoldHeads" );
|
||||
|
||||
float ArrowGetPercentVisible( const PlayerState* pPlayerState, float fYPosWithoutReverse );
|
||||
|
||||
static float GetNoteFieldHeight( const PlayerState* pPlayerState )
|
||||
{
|
||||
return SCREEN_HEIGHT + fabsf(pPlayerState->m_PlayerOptions.GetCurrent().m_fPerspectiveTilt)*200;
|
||||
@@ -236,7 +238,10 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
||||
if( bShowEffects )
|
||||
fBeatsUntilStep = pCurSteps->m_Timing.GetDisplayedBeat(fNoteBeat) - pCurSteps->m_Timing.GetDisplayedBeat(fSongBeat);
|
||||
float fYOffsetBeatSpacing = fBeatsUntilStep;
|
||||
float fSpeedMultiplier = bShowEffects ? pCurSteps->m_Timing.GetDisplayedSpeedPercent( position.m_fSongBeatVisible, position.m_fMusicSecondsVisible ) : 1.0;
|
||||
float fSpeedMultiplier = bShowEffects ?
|
||||
pCurSteps->m_Timing.GetDisplayedSpeedPercent(
|
||||
position.m_fSongBeatVisible,
|
||||
position.m_fMusicSecondsVisible ) : 1.0f;
|
||||
fYOffset += fSpeedMultiplier * fYOffsetBeatSpacing * (1-pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing);
|
||||
}
|
||||
|
||||
@@ -340,8 +345,8 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
||||
static void ArrowGetReverseShiftAndScale( const PlayerState* pPlayerState, int iCol, float fYReverseOffsetPixels, float &fShiftOut, float &fScaleOut )
|
||||
{
|
||||
// XXX: Hack: we need to scale the reverse shift by the zoom.
|
||||
float fTinyPercent = pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_TINY];
|
||||
float fZoom = 1 - fTinyPercent*0.5f;
|
||||
float fMiniPercent = pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_MINI];
|
||||
float fZoom = 1 - fMiniPercent*0.5f;
|
||||
|
||||
// don't divide by 0
|
||||
if( fabsf(fZoom) < 0.01 )
|
||||
@@ -484,12 +489,12 @@ float ArrowEffects::GetXPos( const PlayerState* pPlayerState, int iColNum, float
|
||||
|
||||
fPixelOffsetFromCenter += pCols[iColNum].fXOffset;
|
||||
|
||||
if( fEffects[PlayerOptions::EFFECT_MINI] != 0 )
|
||||
if( fEffects[PlayerOptions::EFFECT_TINY] != 0 )
|
||||
{
|
||||
// Allow Mini to pull tracks together, but not to push them apart.
|
||||
float fMiniPercent = fEffects[PlayerOptions::EFFECT_MINI];
|
||||
fMiniPercent = min( powf(MINI_PERCENT_BASE, fMiniPercent), (float)MINI_PERCENT_GATE );
|
||||
fPixelOffsetFromCenter *= fMiniPercent;
|
||||
// Allow Tiny to pull tracks together, but not to push them apart.
|
||||
float fTinyPercent = fEffects[PlayerOptions::EFFECT_TINY];
|
||||
fTinyPercent = min( powf(TINY_PERCENT_BASE, fTinyPercent), (float)TINY_PERCENT_GATE );
|
||||
fPixelOffsetFromCenter *= fTinyPercent;
|
||||
}
|
||||
|
||||
return fPixelOffsetFromCenter;
|
||||
@@ -560,7 +565,7 @@ static float GetCenterLine( const PlayerState* pPlayerState )
|
||||
{
|
||||
/* Another mini hack: if EFFECT_MINI is on, then our center line is at
|
||||
* eg. 320, not 160. */
|
||||
const float fMiniPercent = pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_TINY];
|
||||
const float fMiniPercent = pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_MINI];
|
||||
const float fZoom = 1 - fMiniPercent*0.5f;
|
||||
return CENTER_LINE_Y / fZoom;
|
||||
}
|
||||
@@ -736,11 +741,11 @@ float ArrowEffects::GetZoom( const PlayerState* pPlayerState )
|
||||
(GAMESTATE->GetNumSidesJoined()==2 || GAMESTATE->AnyPlayersAreCpu()) )
|
||||
fZoom *= 0.6f;
|
||||
|
||||
float fMiniPercent = pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_MINI];
|
||||
if( fMiniPercent != 0 )
|
||||
float fTinyPercent = pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_TINY];
|
||||
if( fTinyPercent != 0 )
|
||||
{
|
||||
fMiniPercent = powf( 0.5f, fMiniPercent );
|
||||
fZoom *= fMiniPercent;
|
||||
fTinyPercent = powf( 0.5f, fTinyPercent );
|
||||
fZoom *= fTinyPercent;
|
||||
}
|
||||
return fZoom;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,27 @@ bool AttackArray::ContainsTransformOrTurn() const
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<RString> AttackArray::ToVectorString() const
|
||||
{
|
||||
vector<RString> ret;
|
||||
FOREACH_CONST( Attack, *this, a )
|
||||
{
|
||||
ret.push_back(ssprintf("TIME=%f:LEN=%f:MODS=%s",
|
||||
a->fStartSecond,
|
||||
a->fSecsRemaining,
|
||||
a->sModifiers.c_str()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AttackArray::UpdateStartTimes(float delta)
|
||||
{
|
||||
FOREACH(Attack, *this, a)
|
||||
{
|
||||
a->fStartSecond += delta;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Chris Danford
|
||||
* All rights reserved.
|
||||
|
||||
@@ -71,6 +71,16 @@ struct AttackArray : public vector<Attack>
|
||||
* @brief Determine if the list of attacks contains a transform or turn mod.
|
||||
* @return true if it does, or false otherwise. */
|
||||
bool ContainsTransformOrTurn() const;
|
||||
|
||||
/**
|
||||
* @brief Return a string representation used for simfiles.
|
||||
* @return the string representation. */
|
||||
vector<RString> ToVectorString() const;
|
||||
|
||||
/**
|
||||
* @brief Adjust the starting time of all attacks.
|
||||
* @param delta the amount to change. */
|
||||
void UpdateStartTimes(float delta);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "GameConstantsAndTypes.h" // for TapNoteScore
|
||||
#include "RageTexturePreloader.h"
|
||||
|
||||
RString GetAttackPieceName( const RString &sAttack );
|
||||
|
||||
class PlayerState;
|
||||
/** @brief A graphical display for attacks. */
|
||||
class AttackDisplay : public ActorFrame
|
||||
|
||||
@@ -49,7 +49,7 @@ void AutoKeysounds::LoadAutoplaySoundsInto( RageSoundReader_Chain *pChain )
|
||||
* Add all current autoplay sounds in both players to the chain. If a sound is
|
||||
* common to both players, don't pan it; otherwise pan it to that player's side.
|
||||
*/
|
||||
int iNumTracks = m_ndAutoKeysoundsOnly[GAMESTATE->m_MasterPlayerNumber].GetNumTracks();
|
||||
int iNumTracks = m_ndAutoKeysoundsOnly[GAMESTATE->GetMasterPlayerNumber()].GetNumTracks();
|
||||
for( int t = 0; t < iNumTracks; t++ )
|
||||
{
|
||||
int iRow = -1;
|
||||
@@ -63,6 +63,10 @@ void AutoKeysounds::LoadAutoplaySoundsInto( RageSoundReader_Chain *pChain )
|
||||
if( t >= m_ndAutoKeysoundsOnly[pn].GetNumTracks() )
|
||||
continue;
|
||||
int iNextRowForPlayer = iRow;
|
||||
/* XXX: If a BMS file only has one tap note per track,
|
||||
* this will prevent any keysounds from loading.
|
||||
* This leads to failure later on.
|
||||
* We need a better way to prevent this. */
|
||||
if( m_ndAutoKeysoundsOnly[pn].GetNextTapNoteRowForTrack( t, iNextRowForPlayer ) )
|
||||
iNextRow = min( iNextRow, iNextRowForPlayer );
|
||||
}
|
||||
@@ -274,7 +278,7 @@ void AutoKeysounds::FinishLoading()
|
||||
delete pChain;
|
||||
}
|
||||
}
|
||||
ASSERT( m_pSharedSound );
|
||||
ASSERT_M( m_pSharedSound, ssprintf("No keysounds were loaded for the song %s!", pSong->m_sMainTitle.c_str() ));
|
||||
|
||||
m_pSharedSound = new RageSoundReader_PitchChange( m_pSharedSound );
|
||||
m_pSharedSound = new RageSoundReader_PostBuffering( m_pSharedSound );
|
||||
@@ -297,7 +301,7 @@ void AutoKeysounds::FinishLoading()
|
||||
apSounds.push_back( m_pPlayerSounds[1] );
|
||||
}
|
||||
|
||||
if( GAMESTATE->GetNumPlayersEnabled() == 1 && GAMESTATE->m_MasterPlayerNumber == PLAYER_2 )
|
||||
if( GAMESTATE->GetNumPlayersEnabled() == 1 && GAMESTATE->GetMasterPlayerNumber() == PLAYER_2 )
|
||||
swap( m_pPlayerSounds[PLAYER_1], m_pPlayerSounds[PLAYER_2] );
|
||||
|
||||
if( apSounds.size() > 1 )
|
||||
|
||||
+25
-18
@@ -27,7 +27,6 @@ static ThemeMetric<float> LEFT_EDGE ("Background","LeftEdge");
|
||||
static ThemeMetric<float> TOP_EDGE ("Background","TopEdge");
|
||||
static ThemeMetric<float> RIGHT_EDGE ("Background","RightEdge");
|
||||
static ThemeMetric<float> BOTTOM_EDGE ("Background","BottomEdge");
|
||||
#define RECT_BACKGROUND RectF (LEFT_EDGE,TOP_EDGE,RIGHT_EDGE,BOTTOM_EDGE)
|
||||
static ThemeMetric<float> CLAMP_OUTPUT_PERCENT ("Background","ClampOutputPercent");
|
||||
static ThemeMetric<bool> SHOW_DANCING_CHARACTERS ("Background","ShowDancingCharacters");
|
||||
static ThemeMetric<bool> USE_STATIC_BG ("Background","UseStaticBackground");
|
||||
@@ -422,35 +421,41 @@ void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const Bac
|
||||
const TimingData &timing = m_pSong->m_SongTiming;
|
||||
|
||||
// change BG every time signature change or 4 measures
|
||||
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, iter )
|
||||
const vector<TimingSegment *> &tSigs = timing.allTimingSegments[SEGMENT_TIME_SIG];
|
||||
|
||||
for (unsigned i = 0; i < tSigs.size(); i++)
|
||||
{
|
||||
vector<TimeSignatureSegment>::const_iterator next = iter;
|
||||
next++;
|
||||
int iSegmentEndRow = (next == timing.m_vTimeSignatureSegments.end()) ? iEndRow : next->GetRow();
|
||||
TimeSignatureSegment *ts = static_cast<TimeSignatureSegment *>(tSigs[i]);
|
||||
int iSegmentEndRow = (i + 1 == tSigs.size()) ? iEndRow : tSigs[i+1]->GetRow();
|
||||
|
||||
for( int i=max(iter->GetRow(),iStartRow); i<min(iEndRow,iSegmentEndRow); i+=4*iter->GetNoteRowsPerMeasure() )
|
||||
|
||||
for(int j=max(ts->GetRow(),iStartRow);
|
||||
j<min(iEndRow,iSegmentEndRow);
|
||||
j+=4*ts->GetNoteRowsPerMeasure())
|
||||
{
|
||||
// Don't fade. It causes frame rate dip, especially on slower machines.
|
||||
BackgroundDef bd = m_Layer[0].CreateRandomBGA( m_pSong, change.m_def.m_sEffect, m_RandomBGAnimations, this );
|
||||
BackgroundDef bd = m_Layer[0].CreateRandomBGA(m_pSong,
|
||||
change.m_def.m_sEffect,
|
||||
m_RandomBGAnimations, this);
|
||||
if( !bd.IsEmpty() )
|
||||
{
|
||||
BackgroundChange c = change;
|
||||
c.m_def = bd;
|
||||
c.m_fStartBeat = NoteRowToBeat(i);
|
||||
c.m_fStartBeat = NoteRowToBeat(j);
|
||||
m_Layer[0].m_aBGChanges.push_back( c );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// change BG every BPM change that is at the beginning of a measure
|
||||
for( unsigned i=0; i<timing.m_BPMSegments.size(); i++ )
|
||||
const vector<TimingSegment *> &bpms = timing.allTimingSegments[SEGMENT_BPM];
|
||||
for( unsigned i=0; i<bpms.size(); i++ )
|
||||
{
|
||||
const BPMSegment& bpmseg = timing.m_BPMSegments[i];
|
||||
|
||||
bool bAtBeginningOfMeasure = false;
|
||||
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, iter )
|
||||
for (unsigned j=0; j<tSigs.size(); j++)
|
||||
{
|
||||
if( (bpmseg.GetRow() - iter->GetRow()) % iter->GetNoteRowsPerMeasure() == 0 )
|
||||
TimeSignatureSegment *ts = static_cast<TimeSignatureSegment *>(tSigs[j]);
|
||||
if ((bpms[i]->GetRow() - ts->GetRow()) % ts->GetNoteRowsPerMeasure() == 0)
|
||||
{
|
||||
bAtBeginningOfMeasure = true;
|
||||
break;
|
||||
@@ -461,7 +466,7 @@ void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const Bac
|
||||
continue; // skip
|
||||
|
||||
// start so that we don't create a BGChange right on top of fEndBeat
|
||||
bool bInRange = bpmseg.GetRow() >= iStartRow && bpmseg.GetRow() < iEndRow;
|
||||
bool bInRange = bpms[i]->GetRow() >= iStartRow && bpms[i]->GetRow() < iEndRow;
|
||||
if( !bInRange )
|
||||
continue; // skip
|
||||
|
||||
@@ -471,7 +476,7 @@ void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const Bac
|
||||
BackgroundChange c = change;
|
||||
c.m_def.m_sFile1 = bd.m_sFile1;
|
||||
c.m_def.m_sFile2 = bd.m_sFile2;
|
||||
c.m_fStartBeat = bpmseg.GetBeat();
|
||||
c.m_fStartBeat = bpms[i]->GetBeat();
|
||||
m_Layer[0].m_aBGChanges.push_back( c );
|
||||
}
|
||||
}
|
||||
@@ -578,13 +583,15 @@ void BackgroundImpl::LoadFromSong( const Song* pSong )
|
||||
else // pSong doesn't have an animation plan
|
||||
{
|
||||
Layer &layer = m_Layer[0];
|
||||
float firstBeat = pSong->GetFirstBeat();
|
||||
float lastBeat = pSong->GetLastBeat();
|
||||
|
||||
LoadFromRandom( pSong->m_fFirstBeat, pSong->m_fLastBeat, BackgroundChange() );
|
||||
LoadFromRandom( firstBeat, lastBeat, BackgroundChange() );
|
||||
|
||||
// end showing the static song background
|
||||
BackgroundChange change;
|
||||
change.m_def = m_StaticBackgroundDef;
|
||||
change.m_fStartBeat = pSong->m_fLastBeat;
|
||||
change.m_fStartBeat = lastBeat;
|
||||
layer.m_aBGChanges.push_back( change );
|
||||
}
|
||||
|
||||
@@ -643,7 +650,7 @@ void BackgroundImpl::LoadFromSong( const Song* pSong )
|
||||
continue;
|
||||
|
||||
float fStartBeat = change.m_fStartBeat;
|
||||
float fEndBeat = pSong->m_fLastBeat;
|
||||
float fEndBeat = pSong->GetLastBeat();
|
||||
if( i+1 < mainlayer.m_aBGChanges.size() )
|
||||
fEndBeat = mainlayer.m_aBGChanges[i+1].m_fStartBeat;
|
||||
|
||||
|
||||
@@ -68,6 +68,25 @@ RString BackgroundChange::GetTextDescription() const
|
||||
return s;
|
||||
}
|
||||
|
||||
RString BackgroundChange::ToString() const
|
||||
{
|
||||
/* TODO: Technically we need to double-escape the filename
|
||||
* (because it might contain '=') and then unescape the value
|
||||
* returned by the MsdFile. */
|
||||
return ssprintf("%.3f=%s=%.3f=%d=%d=%d=%s=%s=%s=%s=%s",
|
||||
this->m_fStartBeat,
|
||||
SmEscape(this->m_def.m_sFile1).c_str(),
|
||||
this->m_fRate,
|
||||
this->m_sTransition == SBT_CrossFade, // backward compat
|
||||
this->m_def.m_sEffect == SBE_StretchRewind, // backward compat
|
||||
this->m_def.m_sEffect != SBE_StretchNoLoop, // backward compat
|
||||
this->m_def.m_sEffect.c_str(),
|
||||
this->m_def.m_sFile2.c_str(),
|
||||
this->m_sTransition.c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(this->m_def.m_sColor1)).c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(this->m_def.m_sColor2)).c_str());
|
||||
}
|
||||
|
||||
|
||||
const RString BACKGROUND_EFFECTS_DIR = "BackgroundEffects/";
|
||||
const RString BACKGROUND_TRANSITIONS_DIR = "BackgroundTransitions/";
|
||||
|
||||
@@ -64,6 +64,11 @@ struct BackgroundChange
|
||||
RString m_sTransition;
|
||||
|
||||
RString GetTextDescription() const;
|
||||
|
||||
/**
|
||||
* @brief Get the string representation of the change.
|
||||
* @return the string representation. */
|
||||
RString ToString() const;
|
||||
};
|
||||
/** @brief Shared background-related routines. */
|
||||
namespace BackgroundUtil
|
||||
|
||||
@@ -242,7 +242,7 @@ void BeginnerHelper::DrawPrimitives()
|
||||
DISPLAY->SetLighting( true );
|
||||
DISPLAY->SetLightDirectional(
|
||||
0,
|
||||
RageColor(0.5,0.5,0.5,1),
|
||||
RageColor(0.5f,0.5f,0.5f,1),
|
||||
RageColor(1,1,1,1),
|
||||
RageColor(0,0,0,1),
|
||||
RageVector3(0, 0, 1) );
|
||||
@@ -271,7 +271,7 @@ void BeginnerHelper::DrawPrimitives()
|
||||
DISPLAY->SetLighting( true );
|
||||
DISPLAY->SetLightDirectional(
|
||||
0,
|
||||
RageColor(0.5,0.5,0.5,1),
|
||||
RageColor(0.5f,0.5f,0.5f,1),
|
||||
RageColor(1,1,1,1),
|
||||
RageColor(0,0,0,1),
|
||||
RageVector3(0, 0, 1) );
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "RageTextureID.h"
|
||||
#include "ActorUtil.h"
|
||||
|
||||
RString GetRandomFileInDir( RString sDir );
|
||||
|
||||
Character::Character(): m_sCharDir(""), m_sCharacterID(""),
|
||||
m_sDisplayName(""), m_sCardPath(""), m_sIconPath(""),
|
||||
m_bUsableInRave(false), m_iPreloadRefcount(0) {}
|
||||
|
||||
@@ -89,7 +89,7 @@ Character* CharacterManager::GetDefaultCharacter()
|
||||
}
|
||||
|
||||
/* We always have the default character. */
|
||||
ASSERT(0);
|
||||
FAIL_M("There must be a default character available!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -152,6 +152,13 @@ public:
|
||||
LuaHelpers::CreateTableFromArray(vChars, L);
|
||||
return 1;
|
||||
}
|
||||
static int GetCharacterCount(T* p, lua_State *L)
|
||||
{
|
||||
vector<Character*> chars;
|
||||
p->GetCharacters(chars);
|
||||
lua_pushnumber(L, chars.size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
LunaCharacterManager()
|
||||
{
|
||||
@@ -159,6 +166,7 @@ public:
|
||||
// sm-ssc adds:
|
||||
ADD_METHOD( GetRandomCharacter );
|
||||
ADD_METHOD( GetAllCharacters );
|
||||
ADD_METHOD( GetCharacterCount );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
/* CharacterManager - Manage characters. */
|
||||
|
||||
#ifndef CHARACTER_MANAGER_H
|
||||
#define CHARACTER_MANAGER_H
|
||||
|
||||
class Character;
|
||||
struct lua_State;
|
||||
|
||||
/** @brief Manage all of the Characters. */
|
||||
class CharacterManager
|
||||
{
|
||||
public:
|
||||
/** @brief Set up the character manager. */
|
||||
CharacterManager();
|
||||
/** @brief Destroy the character manager. */
|
||||
~CharacterManager();
|
||||
|
||||
void GetCharacters( vector<Character*> &vpCharactersOut );
|
||||
/** @brief Get one installed character at random.
|
||||
* @return The random character. */
|
||||
Character* GetRandomCharacter();
|
||||
/** @brief Get the character assigned as the default.
|
||||
* @return The default character. */
|
||||
Character* GetDefaultCharacter();
|
||||
Character* GetCharacterFromID( RString sCharacterID );
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ static void Version()
|
||||
#endif // WIN32
|
||||
}
|
||||
|
||||
void CommandLineActions::Handle(LoadingWindow* pLW)
|
||||
void CommandLineActions::Handle()
|
||||
{
|
||||
CommandLineArgs args;
|
||||
for(int i=0; i<g_argc; ++i)
|
||||
|
||||
@@ -7,9 +7,8 @@ class LoadingWindow;
|
||||
namespace CommandLineActions
|
||||
{
|
||||
/**
|
||||
* @brief Perform a utility function, then exit.
|
||||
* @param pLW the LoadingWindow that is presently not used? */
|
||||
void Handle(LoadingWindow* pLW);
|
||||
* @brief Perform a utility function, then exit. */
|
||||
void Handle();
|
||||
|
||||
/** @brief The housing for the command line arguments. */
|
||||
class CommandLineArgs
|
||||
|
||||
@@ -42,9 +42,9 @@ void CourseContentsList::SetFromGameState()
|
||||
{
|
||||
RemoveAllChildren();
|
||||
|
||||
if( GAMESTATE->m_MasterPlayerNumber == PlayerNumber_Invalid )
|
||||
if( GAMESTATE->GetMasterPlayerNumber() == PlayerNumber_Invalid )
|
||||
return;
|
||||
const Trail *pMasterTrail = GAMESTATE->m_pCurTrail[GAMESTATE->m_MasterPlayerNumber];
|
||||
const Trail *pMasterTrail = GAMESTATE->m_pCurTrail[GAMESTATE->GetMasterPlayerNumber()];
|
||||
if( pMasterTrail == NULL )
|
||||
return;
|
||||
unsigned uNumEntriesToShow = pMasterTrail->m_vEntries.size();
|
||||
|
||||
+23
-2
@@ -163,18 +163,39 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou
|
||||
// infer entry::Type from the first param
|
||||
// todo: make sure these aren't generating bogus entries due
|
||||
// to a lack of songs. -aj
|
||||
int iNumSongs = SONGMAN->GetNumSongs();
|
||||
LOG->Trace("[CourseLoaderCRS] sParams[1] = %s",sParams[1].c_str());
|
||||
// most played
|
||||
if( sParams[1].Left(strlen("BEST")) == "BEST" )
|
||||
{
|
||||
new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("BEST")) ) - 1;
|
||||
int iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("BEST")) ) - 1;
|
||||
if( iChooseIndex > iNumSongs )
|
||||
{
|
||||
// looking up a song that doesn't exist.
|
||||
LOG->UserLog( "Course file", sPath, "is trying to load BEST%i with only %i songs installed. "
|
||||
"This entry will be ignored.", iChooseIndex, iNumSongs);
|
||||
out.m_bIncomplete = true;
|
||||
continue; // skip this #SONG
|
||||
}
|
||||
|
||||
new_entry.iChooseIndex = iChooseIndex;
|
||||
CLAMP( new_entry.iChooseIndex, 0, 500 );
|
||||
new_entry.songSort = SongSort_MostPlays;
|
||||
}
|
||||
// least played
|
||||
else if( sParams[1].Left(strlen("WORST")) == "WORST" )
|
||||
{
|
||||
new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("WORST")) ) - 1;
|
||||
int iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("BEST")) ) - 1;
|
||||
if( iChooseIndex > iNumSongs )
|
||||
{
|
||||
// looking up a song that doesn't exist.
|
||||
LOG->UserLog( "Course file", sPath, "is trying to load WORST%i with only %i songs installed. "
|
||||
"This entry will be ignored.", iChooseIndex, iNumSongs);
|
||||
out.m_bIncomplete = true;
|
||||
continue; // skip this #SONG
|
||||
}
|
||||
|
||||
new_entry.iChooseIndex = iChooseIndex;
|
||||
CLAMP( new_entry.iChooseIndex, 0, 500 );
|
||||
new_entry.songSort = SongSort_FewestPlays;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ class XNode;
|
||||
class CourseEntry;
|
||||
class Song;
|
||||
|
||||
bool CompareCoursePointersBySortValueAscending( const Course *pSong1, const Course *pSong2 );
|
||||
bool CompareCoursePointersBySortValueDescending( const Course *pSong1, const Course *pSong2 );
|
||||
bool CompareCoursePointersByTitle( const Course *pCourse1, const Course *pCourse2 );
|
||||
|
||||
/** @brief Utility functions that deal with Courses. */
|
||||
namespace CourseUtil
|
||||
{
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "PrefsManager.h"
|
||||
#include "Model.h"
|
||||
|
||||
int Neg1OrPos1();
|
||||
|
||||
#define DC_X( choice ) THEME->GetMetricF("DancingCharacters",ssprintf("2DCharacterXP%d",choice+1))
|
||||
#define DC_Y( choice ) THEME->GetMetricF("DancingCharacters",ssprintf("2DCharacterYP%d",choice+1))
|
||||
|
||||
@@ -164,7 +166,7 @@ void DancingCharacters::LoadNextSong()
|
||||
m_fThisCameraEndBeat = 0;
|
||||
|
||||
ASSERT( GAMESTATE->m_pCurSong );
|
||||
m_fThisCameraEndBeat = GAMESTATE->m_pCurSong->m_fFirstBeat;
|
||||
m_fThisCameraEndBeat = GAMESTATE->m_pCurSong->GetFirstBeat();
|
||||
|
||||
FOREACH_PlayerNumber( p )
|
||||
if( GAMESTATE->IsPlayerEnabled(p) )
|
||||
@@ -210,9 +212,9 @@ void DancingCharacters::Update( float fDelta )
|
||||
bWasGameplayStarting = bGameplayStarting;
|
||||
|
||||
static float fLastBeat = GAMESTATE->m_Position.m_fSongBeat;
|
||||
float firstBeat = GAMESTATE->m_pCurSong->GetFirstBeat();
|
||||
float fThisBeat = GAMESTATE->m_Position.m_fSongBeat;
|
||||
if( fLastBeat < GAMESTATE->m_pCurSong->m_fFirstBeat &&
|
||||
fThisBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat )
|
||||
if( fLastBeat < firstBeat && fThisBeat >= firstBeat )
|
||||
{
|
||||
FOREACH_PlayerNumber( p )
|
||||
m_pCharacter[p]->PlayAnimation( "dance" );
|
||||
@@ -344,7 +346,7 @@ void DancingCharacters::DrawPrimitives()
|
||||
ambient,
|
||||
diffuse,
|
||||
specular,
|
||||
RageVector3(-3, -7.5, +9) );
|
||||
RageVector3(-3, -7.5f, +9) );
|
||||
|
||||
if( PREFSMAN->m_bCelShadeModels )
|
||||
{
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "EnumHelper.h"
|
||||
#include <ctime>
|
||||
|
||||
int StringToDayInYear( RString sDayInYear );
|
||||
|
||||
/** @brief The number of days we check for previously. */
|
||||
const int NUM_LAST_DAYS = 7;
|
||||
/** @brief The number of weeks we check for previously. */
|
||||
|
||||
+20
-35
@@ -67,49 +67,34 @@ RString GetCustomDifficulty( StepsType st, Difficulty dc, CourseType ct )
|
||||
return DifficultyToString( dc );
|
||||
}
|
||||
|
||||
const StepsTypeInfo &sti = GAMEMAN->GetStepsTypeInfo( st );
|
||||
|
||||
switch( sti.m_StepsTypeCategory )
|
||||
if( dc == Difficulty_Edit )
|
||||
{
|
||||
DEFAULT_FAIL(sti.m_StepsTypeCategory);
|
||||
case StepsTypeCategory_Single:
|
||||
case StepsTypeCategory_Double:
|
||||
if( dc == Difficulty_Edit )
|
||||
return "Edit";
|
||||
}
|
||||
// OPTIMIZATION OPPORTUNITY: cache these metrics and cache the splitting
|
||||
vector<RString> vsNames;
|
||||
split( NAMES, ",", vsNames );
|
||||
FOREACH( RString, vsNames, sName )
|
||||
{
|
||||
ThemeMetric<StepsType> STEPS_TYPE("CustomDifficulty",(*sName)+"StepsType");
|
||||
if( STEPS_TYPE == StepsType_Invalid || st == STEPS_TYPE ) // match
|
||||
{
|
||||
return "Edit";
|
||||
}
|
||||
else
|
||||
{
|
||||
// OPTIMIZATION OPPORTUNITY: cache these metrics and cache the splitting
|
||||
vector<RString> vsNames;
|
||||
split( NAMES, ",", vsNames );
|
||||
FOREACH( RString, vsNames, sName )
|
||||
ThemeMetric<Difficulty> DIFFICULTY("CustomDifficulty",(*sName)+"Difficulty");
|
||||
if( DIFFICULTY == Difficulty_Invalid || dc == DIFFICULTY ) // match
|
||||
{
|
||||
ThemeMetric<StepsType> STEPS_TYPE("CustomDifficulty",(*sName)+"StepsType");
|
||||
if( STEPS_TYPE == StepsType_Invalid || st == STEPS_TYPE ) // match
|
||||
ThemeMetric<CourseType> COURSE_TYPE("CustomDifficulty",(*sName)+"CourseType");
|
||||
if( COURSE_TYPE == CourseType_Invalid || ct == COURSE_TYPE ) // match
|
||||
{
|
||||
ThemeMetric<Difficulty> DIFFICULTY("CustomDifficulty",(*sName)+"Difficulty");
|
||||
if( DIFFICULTY == Difficulty_Invalid || dc == DIFFICULTY ) // match
|
||||
{
|
||||
ThemeMetric<CourseType> COURSE_TYPE("CustomDifficulty",(*sName)+"CourseType");
|
||||
if( COURSE_TYPE == CourseType_Invalid || ct == COURSE_TYPE ) // match
|
||||
{
|
||||
ThemeMetric<RString> STRING("CustomDifficulty",(*sName)+"String");
|
||||
return STRING.GetValue();
|
||||
}
|
||||
}
|
||||
ThemeMetric<RString> STRING("CustomDifficulty",(*sName)+"String");
|
||||
return STRING.GetValue();
|
||||
}
|
||||
}
|
||||
// no matching CustomDifficulty, so use a regular difficulty name
|
||||
if( dc == Difficulty_Invalid )
|
||||
return RString();
|
||||
return DifficultyToString( dc );
|
||||
}
|
||||
case StepsTypeCategory_Couple:
|
||||
return "Couple";
|
||||
case StepsTypeCategory_Routine:
|
||||
return "Routine";
|
||||
}
|
||||
// no matching CustomDifficulty, so use a regular difficulty name
|
||||
if( dc == Difficulty_Invalid )
|
||||
return RString();
|
||||
return DifficultyToString( dc );
|
||||
}
|
||||
|
||||
LuaFunction( GetCustomDifficulty, GetCustomDifficulty(Enum::Check<StepsType>(L,1), Enum::Check<Difficulty>(L, 2), Enum::Check<CourseType>(L, 3, true)) );
|
||||
|
||||
+4
-12
@@ -11,18 +11,10 @@
|
||||
#include "SongUtil.h"
|
||||
#include "XmlFile.h"
|
||||
|
||||
// MAX_METERS was previously set to NUM_Difficulty + MAX_EDITS_PER_SONG.
|
||||
// This was all fine and well until AutoSetStyle was created. In certain
|
||||
// gametypes (technomotion for example), if autogen is on, the number of
|
||||
// available stepcharts exceeds MAX_METERS, resulting in a crash.
|
||||
// My first thought to fix this was:
|
||||
// (NUM_Difficulty (6) * NUM_StepsType (32)) + MAX_EDITS_PER_SONG (5*profileSlot (2)) = 202
|
||||
// However, 202 rows may be a bit overkill.
|
||||
// Dance has 6 stepstypes counting 3panel, Pump and Techno have 5.
|
||||
// (6 difficulties * 6 stepstypes) + MAX_EDITS_PER_SONG (which is 5 * profileSlots) = 46 rows
|
||||
// 46 seems to be a good enough number for this. If we get a crash again, up the
|
||||
// "magic" 6. -aj
|
||||
#define MAX_METERS (NUM_Difficulty * 6) + MAX_EDITS_PER_SONG
|
||||
/** @brief Specifies the max number of charts available for a song.
|
||||
*
|
||||
* This includes autogenned charts. */
|
||||
#define MAX_METERS (NUM_Difficulty * NUM_StepsType) + MAX_EDITS_PER_SONG
|
||||
|
||||
REGISTER_ACTOR_CLASS( StepsDisplayList );
|
||||
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ void EditMenu::GetSongsToShowForGroup( const RString &sGroup, vector<Song*> &vpS
|
||||
for( int i=vpSongsOut.size()-1; i>=0; i-- )
|
||||
{
|
||||
const Song* pSong = vpSongsOut[i];
|
||||
if( !pSong->NormallyDisplayed() || pSong->IsTutorial() || SONGMAN->WasLoadedFromAdditionalSongs(pSong) )
|
||||
if( !pSong->NormallyDisplayed() || pSong->IsTutorial() )
|
||||
vpSongsOut.erase( vpSongsOut.begin()+i );
|
||||
}
|
||||
break;
|
||||
|
||||
+102
-13
@@ -10,6 +10,7 @@
|
||||
#include "EnumHelper.h"
|
||||
#include "ThemeMetric.h"
|
||||
|
||||
/** @brief What type of row is needed for the EditMenu? */
|
||||
enum EditMenuRow
|
||||
{
|
||||
ROW_GROUP,
|
||||
@@ -19,11 +20,19 @@ enum EditMenuRow
|
||||
ROW_SOURCE_STEPS_TYPE,
|
||||
ROW_SOURCE_STEPS,
|
||||
ROW_ACTION,
|
||||
NUM_EditMenuRow
|
||||
NUM_EditMenuRow /**< The number of EditMenuRows available. */
|
||||
};
|
||||
/** @brief Loop through each EditMenuRow. */
|
||||
#define FOREACH_EditMenuRow( r ) FOREACH_ENUM( EditMenuRow, r )
|
||||
/**
|
||||
* @brief Turn the EditMenuRow into a string.
|
||||
* @param r the row.
|
||||
* @return the string. */
|
||||
const RString& EditMenuRowToString( EditMenuRow r );
|
||||
/**
|
||||
* @brief Turn the EditMenuRow into a localized string.
|
||||
* @param r the row.
|
||||
* @return the localized string. */
|
||||
const RString& EditMenuRowToLocalizedString( EditMenuRow r );
|
||||
|
||||
/** @brief The different actions one can take on a step. */
|
||||
@@ -38,9 +47,18 @@ enum EditMenuAction
|
||||
};
|
||||
/** @brief Loop through each EditMenuAction. */
|
||||
#define FOREACH_EditMenuAction( ema ) FOREACH_ENUM( EditMenuAction, ema )
|
||||
/**
|
||||
* @brief Turn the EditMenuAction into a string.
|
||||
* @param ema the action.
|
||||
* @return the string. */
|
||||
const RString& EditMenuActionToString( EditMenuAction ema );
|
||||
/**
|
||||
* @brief Turn the EditMenuAction into a localized string.
|
||||
* @param ema the action.
|
||||
* @return the localized string. */
|
||||
const RString& EditMenuActionToLocalizedString( EditMenuAction ema );
|
||||
|
||||
/** @brief How many arrows are used for the EditMenu? */
|
||||
const int NUM_ARROWS = 2;
|
||||
|
||||
/**
|
||||
@@ -50,39 +68,110 @@ const int NUM_ARROWS = 2;
|
||||
class EditMenu: public ActorFrame
|
||||
{
|
||||
public:
|
||||
/** @brief Set up the EditMenu. */
|
||||
EditMenu();
|
||||
/** @brief Destroy the EditMenu. */
|
||||
~EditMenu();
|
||||
void Load( const RString &sType );
|
||||
|
||||
/** @brief Determine if we can move up.
|
||||
* @return true if we can, false otherwise. */
|
||||
bool CanGoUp();
|
||||
/** @brief Determine if we can move down.
|
||||
* @return true if we can, false otherwise. */
|
||||
bool CanGoDown();
|
||||
/** @brief Determine if we can move left.
|
||||
* @return true if we can, false otherwise. */
|
||||
bool CanGoLeft();
|
||||
/** @brief Determine if we can move right.
|
||||
* @return true if we can, false otherwise. */
|
||||
bool CanGoRight();
|
||||
/** @brief Determine if the EditMenuRow is selectable.
|
||||
* @param row the row in question.
|
||||
* @return true if it can be selected, false otherwise. */
|
||||
bool RowIsSelectable( EditMenuRow row );
|
||||
|
||||
/** @brief Move up to the next selection. */
|
||||
void Up();
|
||||
/** @brief Move down to the next selection. */
|
||||
void Down();
|
||||
/** @brief Move left to the next selection. */
|
||||
void Left();
|
||||
/** @brief Move right to the next selection. */
|
||||
void Right();
|
||||
|
||||
void RefreshAll();
|
||||
|
||||
RString GetSelectedGroup() const
|
||||
/** @brief Retrieve the currently selected group.
|
||||
* @return the current group. */
|
||||
RString GetSelectedGroup() const
|
||||
{
|
||||
if( !SHOW_GROUPS.GetValue() ) return GROUP_ALL;
|
||||
ASSERT_M((int)m_iSelection[ROW_GROUP] < (int)m_sGroups.size(),
|
||||
ssprintf("Group selection %d < Number of groups %d", m_iSelection[ROW_GROUP], (int)m_sGroups.size()));
|
||||
int groups = static_cast<int>(m_sGroups.size());
|
||||
ASSERT_M(m_iSelection[ROW_GROUP] < groups,
|
||||
ssprintf("Group selection %d < Number of groups %d",
|
||||
m_iSelection[ROW_GROUP],
|
||||
groups));
|
||||
return m_sGroups[m_iSelection[ROW_GROUP]];
|
||||
}
|
||||
Song* GetSelectedSong() const { ASSERT(m_iSelection[ROW_SONG] < (int)m_pSongs.size()); return m_pSongs[m_iSelection[ROW_SONG]]; }
|
||||
StepsType GetSelectedStepsType() const { ASSERT(m_iSelection[ROW_STEPS_TYPE] < (int)m_StepsTypes.size()); return m_StepsTypes[m_iSelection[ROW_STEPS_TYPE]]; }
|
||||
Steps* GetSelectedSteps() const { ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size()); return m_vpSteps[m_iSelection[ROW_STEPS]].pSteps; }
|
||||
Difficulty GetSelectedDifficulty() const { ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size()); return m_vpSteps[m_iSelection[ROW_STEPS]].dc; }
|
||||
StepsType GetSelectedSourceStepsType() const { ASSERT(m_iSelection[ROW_SOURCE_STEPS_TYPE] < (int)m_StepsTypes.size()); return m_StepsTypes[m_iSelection[ROW_SOURCE_STEPS_TYPE]]; }
|
||||
Steps* GetSelectedSourceSteps() const { ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size()); return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].pSteps; }
|
||||
Difficulty GetSelectedSourceDifficulty() const { ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size()); return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].dc; }
|
||||
EditMenuAction GetSelectedAction() const { ASSERT(m_iSelection[ROW_ACTION] < (int)m_Actions.size()); return m_Actions[m_iSelection[ROW_ACTION]]; }
|
||||
|
||||
/** @brief Retrieve the currently selected song.
|
||||
* @return the current song. */
|
||||
Song* GetSelectedSong() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_SONG] < (int)m_pSongs.size());
|
||||
return m_pSongs[m_iSelection[ROW_SONG]];
|
||||
}
|
||||
/** @brief Retrieve the currently selected steps type.
|
||||
* @return the current steps type. */
|
||||
StepsType GetSelectedStepsType() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_STEPS_TYPE] < (int)m_StepsTypes.size());
|
||||
return m_StepsTypes[m_iSelection[ROW_STEPS_TYPE]];
|
||||
}
|
||||
/** @brief Retrieve the currently selected steps.
|
||||
* @return the current steps. */
|
||||
Steps* GetSelectedSteps() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size());
|
||||
return m_vpSteps[m_iSelection[ROW_STEPS]].pSteps;
|
||||
}
|
||||
/** @brief Retrieve the currently selected difficulty.
|
||||
* @return the current difficulty. */
|
||||
Difficulty GetSelectedDifficulty() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size());
|
||||
return m_vpSteps[m_iSelection[ROW_STEPS]].dc;
|
||||
}
|
||||
/** @brief Retrieve the currently selected source steps type.
|
||||
* @return the current source steps type. */
|
||||
StepsType GetSelectedSourceStepsType() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_SOURCE_STEPS_TYPE] < (int)m_StepsTypes.size());
|
||||
return m_StepsTypes[m_iSelection[ROW_SOURCE_STEPS_TYPE]];
|
||||
}
|
||||
/** @brief Retrieve the currently selected source steps.
|
||||
* @return the current source steps. */
|
||||
Steps* GetSelectedSourceSteps() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size());
|
||||
return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].pSteps;
|
||||
}
|
||||
/** @brief Retrieve the currently selected difficulty.
|
||||
* @return the current difficulty. */
|
||||
Difficulty GetSelectedSourceDifficulty() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size());
|
||||
return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].dc;
|
||||
}
|
||||
/** @brief Retrieve the currently selected action.
|
||||
* @return the current action. */
|
||||
EditMenuAction GetSelectedAction() const
|
||||
{
|
||||
ASSERT(m_iSelection[ROW_ACTION] < (int)m_Actions.size());
|
||||
return m_Actions[m_iSelection[ROW_ACTION]];
|
||||
}
|
||||
/** @brief Retrieve the currently selected row.
|
||||
* @return the current row. */
|
||||
EditMenuRow GetSelectedRow() const { return m_SelectedRow; }
|
||||
|
||||
private:
|
||||
|
||||
+62
-29
@@ -15,7 +15,12 @@ extern "C"
|
||||
/** @brief A general foreach loop for enumerators. */
|
||||
#define FOREACH_ENUM( e, var ) for( e var=(e)0; var<NUM_##e; enum_add<e>( var, +1 ) )
|
||||
|
||||
int CheckEnum( lua_State *L, LuaReference &table, int iPos, int iInvalid, const char *szType, bool bAllowInvalid );
|
||||
int CheckEnum(lua_State *L,
|
||||
LuaReference &table,
|
||||
int iPos,
|
||||
int iInvalid,
|
||||
const char *szType,
|
||||
bool bAllowInvalid);
|
||||
|
||||
template<typename T>
|
||||
struct EnumTraits
|
||||
@@ -33,7 +38,12 @@ namespace Enum
|
||||
template<typename T>
|
||||
static T Check( lua_State *L, int iPos, bool bAllowInvalid = false )
|
||||
{
|
||||
return (T) CheckEnum( L, EnumTraits<T>::StringToEnum, iPos, EnumTraits<T>::Invalid, EnumTraits<T>::szName, bAllowInvalid );
|
||||
return (T) CheckEnum(L,
|
||||
EnumTraits<T>::StringToEnum,
|
||||
iPos,
|
||||
EnumTraits<T>::Invalid,
|
||||
EnumTraits<T>::szName,
|
||||
bAllowInvalid);
|
||||
}
|
||||
template<typename T>
|
||||
static void Push( lua_State *L, T iVal )
|
||||
@@ -57,37 +67,47 @@ namespace Enum
|
||||
const RString &EnumToString( int iVal, int iMax, const char **szNameArray, auto_ptr<RString> *pNameCache ); // XToString helper
|
||||
|
||||
#define XToString(X) \
|
||||
COMPILE_ASSERT( NUM_##X == ARRAYLEN(X##Names) ); \
|
||||
const RString& X##ToString( X x ) \
|
||||
{ \
|
||||
static auto_ptr<RString> as_##X##Name[NUM_##X+2]; \
|
||||
return EnumToString( x, NUM_##X, X##Names, as_##X##Name ); \
|
||||
} \
|
||||
namespace StringConversion { template<> RString ToString<X>( const X &value ) { return X##ToString(value); } }
|
||||
const RString& X##ToString(X x); \
|
||||
COMPILE_ASSERT( NUM_##X == ARRAYLEN(X##Names) ); \
|
||||
const RString& X##ToString( X x ) \
|
||||
{ \
|
||||
static auto_ptr<RString> as_##X##Name[NUM_##X+2]; \
|
||||
return EnumToString( x, NUM_##X, X##Names, as_##X##Name ); \
|
||||
} \
|
||||
namespace StringConversion { template<> RString ToString<X>( const X &value ) { return X##ToString(value); } }
|
||||
|
||||
#define XToLocalizedString(X) \
|
||||
const RString &X##ToLocalizedString( X x ) \
|
||||
{ \
|
||||
static auto_ptr<LocalizedString> g_##X##Name[NUM_##X]; \
|
||||
if( g_##X##Name[0].get() == NULL ) { \
|
||||
for( unsigned i = 0; i < NUM_##X; ++i ) \
|
||||
{ \
|
||||
auto_ptr<LocalizedString> ap( new LocalizedString(#X, X##ToString((X)i)) ); \
|
||||
g_##X##Name[i] = ap; \
|
||||
} \
|
||||
const RString &X##ToLocalizedString(X x); \
|
||||
const RString &X##ToLocalizedString( X x ) \
|
||||
{ \
|
||||
static auto_ptr<LocalizedString> g_##X##Name[NUM_##X]; \
|
||||
if( g_##X##Name[0].get() == NULL ) { \
|
||||
for( unsigned i = 0; i < NUM_##X; ++i ) \
|
||||
{ \
|
||||
auto_ptr<LocalizedString> ap( new LocalizedString(#X, X##ToString((X)i)) ); \
|
||||
g_##X##Name[i] = ap; \
|
||||
} \
|
||||
return g_##X##Name[x]->GetValue(); \
|
||||
}
|
||||
} \
|
||||
return g_##X##Name[x]->GetValue(); \
|
||||
}
|
||||
|
||||
#define StringToX(X) \
|
||||
X StringTo##X( const RString& s ) \
|
||||
{ \
|
||||
for( unsigned i = 0; i < ARRAYLEN(X##Names); ++i ) \
|
||||
if( !s.CompareNoCase(X##Names[i]) ) \
|
||||
return (X)i; \
|
||||
return X##_Invalid; \
|
||||
X StringTo##X(const RString&); \
|
||||
X StringTo##X( const RString& s ) \
|
||||
{ \
|
||||
for( unsigned i = 0; i < ARRAYLEN(X##Names); ++i ) \
|
||||
if( !s.CompareNoCase(X##Names[i]) ) \
|
||||
return (X)i; \
|
||||
return X##_Invalid; \
|
||||
} \
|
||||
namespace StringConversion \
|
||||
{ \
|
||||
template<> bool FromString<X>( const RString &sValue, X &out ) \
|
||||
{ \
|
||||
out = StringTo##X(sValue); \
|
||||
return out != X##_Invalid; \
|
||||
} \
|
||||
namespace StringConversion { template<> bool FromString<X>( const RString &sValue, X &out ) { out = StringTo##X(sValue); return out != X##_Invalid; } }
|
||||
}
|
||||
|
||||
// currently unused
|
||||
#define LuaDeclareType(X)
|
||||
@@ -123,8 +143,21 @@ static void Lua##X(lua_State* L) \
|
||||
REGISTER_WITH_LUA_FUNCTION( Lua##X ); \
|
||||
template<> X EnumTraits<X>::Invalid = X##_Invalid; \
|
||||
template<> const char *EnumTraits<X>::szName = #X; \
|
||||
namespace LuaHelpers { template<> bool FromStack<X>( lua_State *L, X &Object, int iOffset ) { Object = Enum::Check<X>( L, iOffset, true ); return Object != EnumTraits<X>::Invalid; } } \
|
||||
namespace LuaHelpers { template<> void Push<X>( lua_State *L, const X &Object ) { Enum::Push<X>( L, Object ); } }
|
||||
namespace LuaHelpers \
|
||||
{ \
|
||||
template<> bool FromStack<X>( lua_State *L, X &Object, int iOffset ) \
|
||||
{ \
|
||||
Object = Enum::Check<X>( L, iOffset, true ); \
|
||||
return Object != EnumTraits<X>::Invalid; \
|
||||
} \
|
||||
} \
|
||||
namespace LuaHelpers \
|
||||
{ \
|
||||
template<> void Push<X>( lua_State *L, const X &Object ) \
|
||||
{ \
|
||||
Enum::Push<X>( L, Object ); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ class Style;
|
||||
class Game;
|
||||
struct lua_State;
|
||||
|
||||
int GetNumCreditsPaid();
|
||||
int GetCreditsRequiredToPlayStyle( const Style *style );
|
||||
|
||||
class GameCommand
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "PlayerNumber.h"
|
||||
#include <float.h>
|
||||
|
||||
RString StepsTypeToString( StepsType st );
|
||||
|
||||
static vector<RString> GenerateRankingToFillInMarker()
|
||||
{
|
||||
@@ -359,12 +360,17 @@ float DisplayBpms::GetMin() const
|
||||
}
|
||||
|
||||
float DisplayBpms::GetMax() const
|
||||
{
|
||||
return this->GetMaxWithin();
|
||||
}
|
||||
|
||||
float DisplayBpms::GetMaxWithin(float highest) const
|
||||
{
|
||||
float fMax = 0;
|
||||
FOREACH_CONST( float, vfBpms, f )
|
||||
{
|
||||
if( *f != -1 )
|
||||
fMax = max( fMax, *f );
|
||||
fMax = clamp(max( fMax, *f ), 0, highest);
|
||||
}
|
||||
return fMax;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#define GAME_CONSTANTS_AND_TYPES_H
|
||||
|
||||
#include "EnumHelper.h"
|
||||
#include <float.h> // need the max for default.
|
||||
|
||||
// Note definitions
|
||||
/** @brief Define the mininum difficulty value allowed. */
|
||||
@@ -513,6 +514,13 @@ struct DisplayBpms
|
||||
* @return the maximum BPM.
|
||||
*/
|
||||
float GetMax() const;
|
||||
/**
|
||||
* @brief Retrieve the maximum BPM of the set,
|
||||
* but no higher than a certain value.
|
||||
* @param highest the highest BPM to use.
|
||||
* @return the maximum BPM.
|
||||
*/
|
||||
float GetMaxWithin(float highest = FLT_MAX) const;
|
||||
/**
|
||||
* @brief Determine if the BPM is really constant.
|
||||
* @return Whether the BPM is constant or not.
|
||||
|
||||
+60
-10
@@ -251,7 +251,7 @@ static const Style g_Style_Dance_Double =
|
||||
static const Style g_Style_Dance_Couple =
|
||||
{ // STYLE_DANCE_COUPLE
|
||||
true, // m_bUsedForGameplay
|
||||
true, // m_bUsedForEdit
|
||||
false, // m_bUsedForEdit
|
||||
false, // m_bUsedForDemonstration
|
||||
false, // m_bUsedForHowToPlay
|
||||
"couple", // m_szName
|
||||
@@ -324,6 +324,50 @@ static const Style g_Style_Dance_Solo =
|
||||
false, // m_bLockDifficulties
|
||||
};
|
||||
|
||||
static const Style g_Style_Dance_Couple_Edit =
|
||||
{ // STYLE_DANCE_COUPLE
|
||||
false, // m_bUsedForGameplay
|
||||
true, // m_bUsedForEdit
|
||||
false, // m_bUsedForDemonstration
|
||||
false, // m_bUsedForHowToPlay
|
||||
"couple-edit", // m_szName
|
||||
StepsType_dance_couple, // m_StepsType
|
||||
StyleType_OnePlayerTwoSides, // m_StyleType
|
||||
8, // m_iColsPerPlayer
|
||||
{ // m_ColumnInfo[NUM_PLAYERS][MAX_COLS_PER_PLAYER];
|
||||
{ // PLAYER_1
|
||||
{ TRACK_1, -DANCE_COL_SPACING*4.f, NULL },
|
||||
{ TRACK_2, -DANCE_COL_SPACING*3.f, NULL },
|
||||
{ TRACK_3, -DANCE_COL_SPACING*2.f, NULL },
|
||||
{ TRACK_4, -DANCE_COL_SPACING*1.f, NULL },
|
||||
{ TRACK_5, +DANCE_COL_SPACING*1.f, NULL },
|
||||
{ TRACK_6, +DANCE_COL_SPACING*2.f, NULL },
|
||||
{ TRACK_7, +DANCE_COL_SPACING*3.f, NULL },
|
||||
{ TRACK_8, +DANCE_COL_SPACING*4.f, NULL },
|
||||
},
|
||||
{ // PLAYER_2
|
||||
{ TRACK_1, -DANCE_COL_SPACING*4.f, NULL },
|
||||
{ TRACK_2, -DANCE_COL_SPACING*3.f, NULL },
|
||||
{ TRACK_3, -DANCE_COL_SPACING*2.f, NULL },
|
||||
{ TRACK_4, -DANCE_COL_SPACING*1.f, NULL },
|
||||
{ TRACK_5, +DANCE_COL_SPACING*1.f, NULL },
|
||||
{ TRACK_6, +DANCE_COL_SPACING*2.f, NULL },
|
||||
{ TRACK_7, +DANCE_COL_SPACING*3.f, NULL },
|
||||
{ TRACK_8, +DANCE_COL_SPACING*4.f, NULL },
|
||||
},
|
||||
},
|
||||
{ // m_iInputColumn[NUM_GameController][NUM_GameButton]
|
||||
{ 0, 3, 2, 1, Style::END_MAPPING },
|
||||
{ 4, 7, 6, 5, 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_Dance_ThreePanel =
|
||||
{ // STYLE_DANCE_THREEPANEL
|
||||
true, // m_bUsedForGameplay
|
||||
@@ -449,6 +493,7 @@ static const Style *g_apGame_Dance_Styles[] =
|
||||
&g_Style_Dance_Double,
|
||||
&g_Style_Dance_Couple,
|
||||
&g_Style_Dance_Solo,
|
||||
&g_Style_Dance_Couple_Edit,
|
||||
&g_Style_Dance_Routine,
|
||||
&g_Style_Dance_ThreePanel,
|
||||
NULL
|
||||
@@ -725,7 +770,7 @@ static const Style g_Style_Pump_Couple_Edit =
|
||||
false, // m_bUsedForHowToPlay
|
||||
"couple-edit", // m_szName
|
||||
StepsType_pump_couple, // m_StepsType
|
||||
StyleType_OnePlayerOneSide, // m_StyleType
|
||||
StyleType_OnePlayerTwoSides, // m_StyleType
|
||||
10, // m_iColsPerPlayer
|
||||
{ // m_ColumnInfo[NUM_PLAYERS][MAX_COLS_PER_PLAYER];
|
||||
{ // PLAYER_1
|
||||
@@ -741,19 +786,24 @@ static const Style g_Style_Pump_Couple_Edit =
|
||||
{ TRACK_10, +PUMP_COL_SPACING*5.0f+4, NULL },
|
||||
},
|
||||
{ // PLAYER_2
|
||||
{ TRACK_1, -PUMP_COL_SPACING*2.0f, NULL },
|
||||
{ TRACK_2, -PUMP_COL_SPACING*1.0f, NULL },
|
||||
{ TRACK_3, +PUMP_COL_SPACING*0.0f, NULL },
|
||||
{ TRACK_4, +PUMP_COL_SPACING*1.0f, NULL },
|
||||
{ TRACK_5, +PUMP_COL_SPACING*2.0f, NULL },
|
||||
{ TRACK_1, -PUMP_COL_SPACING*5.0f-4, NULL },
|
||||
{ TRACK_2, -PUMP_COL_SPACING*4.0f-4, NULL },
|
||||
{ TRACK_3, -PUMP_COL_SPACING*3.0f-4, NULL },
|
||||
{ TRACK_4, -PUMP_COL_SPACING*2.0f-4, NULL },
|
||||
{ TRACK_5, -PUMP_COL_SPACING*1.0f-4, NULL },
|
||||
{ TRACK_6, +PUMP_COL_SPACING*1.0f+4, NULL },
|
||||
{ TRACK_7, +PUMP_COL_SPACING*2.0f+4, NULL },
|
||||
{ TRACK_8, +PUMP_COL_SPACING*3.0f+4, NULL },
|
||||
{ TRACK_9, +PUMP_COL_SPACING*4.0f+4, NULL },
|
||||
{ TRACK_10, +PUMP_COL_SPACING*5.0f+4, NULL },
|
||||
},
|
||||
},
|
||||
{ // m_iInputColumn[NUM_GameController][NUM_GameButton]
|
||||
{ 1, 3, 2, 0, 4, 6, 8, 7, 5, 9, Style::END_MAPPING },
|
||||
{ 1, 3, 2, 0, 4, 6, 8, 7, 5, 9, Style::END_MAPPING },
|
||||
{ 1, 3, 2, 0, 4, Style::END_MAPPING },
|
||||
{ 6, 8, 7, 5, 9, Style::END_MAPPING },
|
||||
},
|
||||
{ // m_iColumnDrawOrder[MAX_COLS_PER_PLAYER];
|
||||
2,1,3,0,4
|
||||
2,1,3,0,4, 2+5,1+5,3+5,0+5,4+5
|
||||
},
|
||||
false, // m_bNeedsZoomOutWith2Players
|
||||
false, // m_bCanUseBeginnerHelper
|
||||
|
||||
@@ -60,8 +60,8 @@ struct MusicPlaying
|
||||
RageSound *m_Music;
|
||||
MusicPlaying( RageSound *Music )
|
||||
{
|
||||
m_Timing.AddBPMSegment( BPMSegment(0,120) );
|
||||
m_NewTiming.AddBPMSegment( BPMSegment(0,120) );
|
||||
m_Timing.AddSegment( SEGMENT_BPM, new BPMSegment(0,120) );
|
||||
m_NewTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0,120) );
|
||||
m_bHasTiming = false;
|
||||
m_bTimingDelayed = false;
|
||||
m_bApplyMusicRate = false;
|
||||
@@ -142,8 +142,10 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
{
|
||||
LOG->Trace( "Found '%s'", ToPlay.m_sTimingFile.c_str() );
|
||||
Song song;
|
||||
if( GetExtension(ToPlay.m_sTimingFile) == ".ssc" &&
|
||||
SSCLoader::LoadFromSSCFile(ToPlay.m_sTimingFile, song) )
|
||||
SSCLoader loaderSSC;
|
||||
SMLoader loaderSM;
|
||||
if(GetExtension(ToPlay.m_sTimingFile) == ".ssc" &&
|
||||
loaderSSC.LoadFromSimfile(ToPlay.m_sTimingFile, song) )
|
||||
{
|
||||
ToPlay.HasTiming = true;
|
||||
ToPlay.m_TimingData = song.m_SongTiming;
|
||||
@@ -152,8 +154,8 @@ static void StartMusic( MusicToPlay &ToPlay )
|
||||
if( pStepsCabinetLights )
|
||||
pStepsCabinetLights->GetNoteData( ToPlay.m_LightsData );
|
||||
}
|
||||
else if( GetExtension(ToPlay.m_sTimingFile) == ".sm" &&
|
||||
SMLoader::LoadFromSMFile(ToPlay.m_sTimingFile, song) )
|
||||
else if(GetExtension(ToPlay.m_sTimingFile) == ".sm" &&
|
||||
loaderSM.LoadFromSimfile(ToPlay.m_sTimingFile, song) )
|
||||
{
|
||||
ToPlay.HasTiming = true;
|
||||
ToPlay.m_TimingData = song.m_SongTiming;
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
class TimingData;
|
||||
class RageSound;
|
||||
struct lua_State;
|
||||
|
||||
int MusicThread_start( void *p );
|
||||
|
||||
/** @brief High-level sound utilities. */
|
||||
class GameSoundManager
|
||||
{
|
||||
|
||||
+52
-25
@@ -106,6 +106,7 @@ static Preference<Premium> g_Premium( "Premium", Premium_Off );
|
||||
Preference<bool> GameState::m_bAutoJoin( "AutoJoin", false );
|
||||
|
||||
GameState::GameState() :
|
||||
processedTiming( NULL ),
|
||||
m_pCurGame( Message_CurrentGameChanged ),
|
||||
m_pCurStyle( Message_CurrentStyleChanged ),
|
||||
m_PlayMode( Message_PlayModeChanged ),
|
||||
@@ -122,14 +123,14 @@ GameState::GameState() :
|
||||
m_pCurTrail( Message_CurrentTrailP1Changed ),
|
||||
m_bGameplayLeadIn( Message_GameplayLeadInChanged ),
|
||||
m_bDidModeChangeNoteSkin( false ),
|
||||
m_bIsUsingStepTiming( true ),
|
||||
m_bInStepEditor( false ),
|
||||
m_stEdit( Message_EditStepsTypeChanged ),
|
||||
m_cdEdit( Message_EditCourseDifficultyChanged ),
|
||||
m_pEditSourceSteps( Message_EditSourceStepsChanged ),
|
||||
m_stEditSource( Message_EditSourceStepsTypeChanged ),
|
||||
m_iEditCourseEntryIndex( Message_EditCourseEntryIndexChanged ),
|
||||
m_sEditLocalProfileID( Message_EditLocalProfileIDChanged ),
|
||||
m_bIsUsingStepTiming( true ),
|
||||
m_bInStepEditor( false )
|
||||
m_sEditLocalProfileID( Message_EditLocalProfileIDChanged )
|
||||
{
|
||||
g_pImpl = new GameStateImpl;
|
||||
|
||||
@@ -188,6 +189,27 @@ GameState::~GameState()
|
||||
|
||||
SAFE_DELETE( m_Environment );
|
||||
SAFE_DELETE( g_pImpl );
|
||||
SAFE_DELETE( processedTiming );
|
||||
}
|
||||
|
||||
PlayerNumber GameState::GetMasterPlayerNumber() const
|
||||
{
|
||||
return this->masterPlayerNumber;
|
||||
}
|
||||
|
||||
void GameState::SetMasterPlayerNumber(const PlayerNumber p)
|
||||
{
|
||||
this->masterPlayerNumber = p;
|
||||
}
|
||||
|
||||
TimingData * GameState::GetProcessedTimingData() const
|
||||
{
|
||||
return this->processedTiming;
|
||||
}
|
||||
|
||||
void GameState::SetProcessedTimingData(TimingData * t)
|
||||
{
|
||||
this->processedTiming = t;
|
||||
}
|
||||
|
||||
void GameState::ApplyGameCommand( const RString &sCommand, PlayerNumber pn )
|
||||
@@ -244,7 +266,7 @@ void GameState::ResetPlayer( PlayerNumber pn )
|
||||
|
||||
void GameState::Reset()
|
||||
{
|
||||
m_MasterPlayerNumber = PLAYER_INVALID; // must initialize for UnjoinPlayer
|
||||
this->SetMasterPlayerNumber(PLAYER_INVALID); // must initialize for UnjoinPlayer
|
||||
|
||||
FOREACH_PlayerNumber( pn )
|
||||
UnjoinPlayer( pn );
|
||||
@@ -334,14 +356,14 @@ void GameState::JoinPlayer( PlayerNumber pn )
|
||||
* give the new player the same number of stage tokens that the old player
|
||||
* has. */
|
||||
if( GetCoinMode() == CoinMode_Pay && GetPremium() == Premium_2PlayersFor1Credit && GetNumSidesJoined() == 1 )
|
||||
m_iPlayerStageTokens[pn] = m_iPlayerStageTokens[m_MasterPlayerNumber];
|
||||
m_iPlayerStageTokens[pn] = m_iPlayerStageTokens[this->GetMasterPlayerNumber()];
|
||||
else
|
||||
m_iPlayerStageTokens[pn] = PREFSMAN->m_iSongsPerPlay;
|
||||
|
||||
m_bSideIsJoined[pn] = true;
|
||||
|
||||
if( m_MasterPlayerNumber == PLAYER_INVALID )
|
||||
m_MasterPlayerNumber = pn;
|
||||
if( this->GetMasterPlayerNumber() == PLAYER_INVALID )
|
||||
this->SetMasterPlayerNumber(pn);
|
||||
|
||||
// if first player to join, set start time
|
||||
if( GetNumSidesJoined() == 1 )
|
||||
@@ -385,8 +407,8 @@ void GameState::UnjoinPlayer( PlayerNumber pn )
|
||||
|
||||
ResetPlayer( pn );
|
||||
|
||||
if( m_MasterPlayerNumber == pn )
|
||||
m_MasterPlayerNumber = GetFirstHumanPlayer();
|
||||
if( this->GetMasterPlayerNumber() == pn )
|
||||
this->SetMasterPlayerNumber(GetFirstHumanPlayer());
|
||||
|
||||
/* Unjoin STATSMAN first, so steps used by this player are released
|
||||
* and can be released by PROFILEMAN. */
|
||||
@@ -398,7 +420,7 @@ void GameState::UnjoinPlayer( PlayerNumber pn )
|
||||
MESSAGEMAN->Broadcast( msg );
|
||||
|
||||
// If there are no players left, reset some non-player-specific stuff, too.
|
||||
if( m_MasterPlayerNumber == PLAYER_INVALID )
|
||||
if( this->GetMasterPlayerNumber() == PLAYER_INVALID )
|
||||
{
|
||||
SongOptions so;
|
||||
GetDefaultSongOptions( so );
|
||||
@@ -635,8 +657,8 @@ int GameState::GetNumStagesForCurrentSongAndStepsOrCourse() const
|
||||
if( pStyle == NULL )
|
||||
{
|
||||
const Steps *pSteps = NULL;
|
||||
if( m_MasterPlayerNumber != PlayerNumber_Invalid )
|
||||
pSteps = m_pCurSteps[m_MasterPlayerNumber];
|
||||
if( this->GetMasterPlayerNumber() != PlayerNumber_Invalid )
|
||||
pSteps = m_pCurSteps[this->GetMasterPlayerNumber()];
|
||||
// Don't call GetFirstCompatibleStyle if numSidesJoined == 0.
|
||||
// This happens because on SContinue when players are unjoined,
|
||||
// pCurSteps will still be set while no players are joined. -Chris
|
||||
@@ -690,7 +712,11 @@ void GameState::BeginStage()
|
||||
if( !ARE_STAGE_PLAYER_MODS_FORCED )
|
||||
{
|
||||
FOREACH_PlayerNumber( p )
|
||||
m_pPlayerState[p]->m_PlayerOptions.Assign( ModsLevel_Stage, m_pPlayerState[p]->m_PlayerOptions.GetPreferred() );
|
||||
{
|
||||
ModsGroup<PlayerOptions> &po = m_pPlayerState[p]->m_PlayerOptions;
|
||||
po.Assign(ModsLevel_Stage,
|
||||
m_pPlayerState[p]->m_PlayerOptions.GetPreferred());
|
||||
}
|
||||
}
|
||||
if( !ARE_STAGE_SONG_MODS_FORCED )
|
||||
m_SongOptions.Assign( ModsLevel_Stage, m_SongOptions.GetPreferred() );
|
||||
@@ -969,7 +995,8 @@ update player position code goes here
|
||||
float GameState::GetSongPercent( float beat ) const
|
||||
{
|
||||
// 0 = first step; 1 = last step
|
||||
return (beat - m_pCurSong->m_fFirstBeat) / m_pCurSong->m_fLastBeat;
|
||||
float curTime = this->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat(beat);
|
||||
return (curTime - m_pCurSong->GetFirstSecond()) / m_pCurSong->GetLastSecond();
|
||||
}
|
||||
|
||||
int GameState::GetNumStagesLeft( PlayerNumber pn ) const
|
||||
@@ -994,9 +1021,9 @@ bool GameState::IsFinalStageForAnyHumanPlayer() const
|
||||
|
||||
bool GameState::IsAnExtraStage() const
|
||||
{
|
||||
if( m_MasterPlayerNumber == PlayerNumber_Invalid )
|
||||
if( this->GetMasterPlayerNumber() == PlayerNumber_Invalid )
|
||||
return false;
|
||||
return !IsEventMode() && !IsCourseMode() && m_iAwardedExtraStages[m_MasterPlayerNumber] > 0;
|
||||
return !IsEventMode() && !IsCourseMode() && m_iAwardedExtraStages[this->GetMasterPlayerNumber()] > 0;
|
||||
}
|
||||
|
||||
static ThemeMetric<bool> LOCK_EXTRA_STAGE_SELECTION("GameState","LockExtraStageSelection");
|
||||
@@ -1007,16 +1034,16 @@ bool GameState::IsAnExtraStageAndSelectionLocked() const
|
||||
|
||||
bool GameState::IsExtraStage() const
|
||||
{
|
||||
if( m_MasterPlayerNumber == PlayerNumber_Invalid )
|
||||
if( this->GetMasterPlayerNumber() == PlayerNumber_Invalid )
|
||||
return false;
|
||||
return !IsEventMode() && !IsCourseMode() && m_iAwardedExtraStages[m_MasterPlayerNumber] == 1;
|
||||
return !IsEventMode() && !IsCourseMode() && m_iAwardedExtraStages[this->GetMasterPlayerNumber()] == 1;
|
||||
}
|
||||
|
||||
bool GameState::IsExtraStage2() const
|
||||
{
|
||||
if( m_MasterPlayerNumber == PlayerNumber_Invalid )
|
||||
if( this->GetMasterPlayerNumber() == PlayerNumber_Invalid )
|
||||
return false;
|
||||
return !IsEventMode() && !IsCourseMode() && m_iAwardedExtraStages[m_MasterPlayerNumber] == 2;
|
||||
return !IsEventMode() && !IsCourseMode() && m_iAwardedExtraStages[this->GetMasterPlayerNumber()] == 2;
|
||||
}
|
||||
|
||||
Stage GameState::GetCurrentStage() const
|
||||
@@ -1059,7 +1086,7 @@ int GameState::GetCourseSongIndex() const
|
||||
}
|
||||
else
|
||||
{
|
||||
return STATSMAN->m_CurStageStats.m_player[m_MasterPlayerNumber].m_iSongsPlayed-1;
|
||||
return STATSMAN->m_CurStageStats.m_player[this->GetMasterPlayerNumber()].m_iSongsPlayed-1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,7 +1158,7 @@ void GameState::SetCurrentStyle( const Style *pStyle )
|
||||
if( INPUTMAPPER )
|
||||
{
|
||||
if( GetCurrentStyle() && GetCurrentStyle()->m_StyleType == StyleType_OnePlayerTwoSides )
|
||||
INPUTMAPPER->SetJoinControllers( m_MasterPlayerNumber );
|
||||
INPUTMAPPER->SetJoinControllers( this->GetMasterPlayerNumber() );
|
||||
else
|
||||
INPUTMAPPER->SetJoinControllers( PLAYER_INVALID );
|
||||
}
|
||||
@@ -1192,7 +1219,7 @@ bool GameState::IsHumanPlayer( PlayerNumber pn ) const
|
||||
return true;
|
||||
case StyleType_OnePlayerOneSide:
|
||||
case StyleType_OnePlayerTwoSides:
|
||||
return pn == m_MasterPlayerNumber;
|
||||
return pn == this->GetMasterPlayerNumber();
|
||||
default:
|
||||
ASSERT(0); // invalid style type
|
||||
return false;
|
||||
@@ -1276,7 +1303,7 @@ EarnedExtraStage GameState::CalculateEarnedExtraStage() const
|
||||
if( GetSmallestNumStagesLeftForAnyHumanPlayer() > 0 )
|
||||
return EarnedExtraStage_No;
|
||||
|
||||
if( m_iAwardedExtraStages[m_MasterPlayerNumber] >= 2 )
|
||||
if( m_iAwardedExtraStages[this->GetMasterPlayerNumber()] >= 2 )
|
||||
return EarnedExtraStage_No;
|
||||
|
||||
FOREACH_EnabledPlayer( pn )
|
||||
@@ -2124,7 +2151,7 @@ public:
|
||||
DEFINE_METHOD( IsPlayerEnabled, IsPlayerEnabled(Enum::Check<PlayerNumber>(L, 1)) )
|
||||
DEFINE_METHOD( IsHumanPlayer, IsHumanPlayer(Enum::Check<PlayerNumber>(L, 1)) )
|
||||
DEFINE_METHOD( GetPlayerDisplayName, GetPlayerDisplayName(Enum::Check<PlayerNumber>(L, 1)) )
|
||||
DEFINE_METHOD( GetMasterPlayerNumber, m_MasterPlayerNumber )
|
||||
DEFINE_METHOD( GetMasterPlayerNumber, GetMasterPlayerNumber() )
|
||||
DEFINE_METHOD( GetMultiplayer, m_bMultiplayer )
|
||||
static int SetMultiplayer( T* p, lua_State *L )
|
||||
{
|
||||
|
||||
+26
-2
@@ -32,9 +32,15 @@ class Style;
|
||||
class TimingData;
|
||||
class Trail;
|
||||
|
||||
SortOrder GetDefaultSort();
|
||||
|
||||
/** @brief Holds game data that is not saved between sessions. */
|
||||
class GameState
|
||||
{
|
||||
/** @brief The player number used with Styles where one player controls both sides. */
|
||||
PlayerNumber masterPlayerNumber;
|
||||
/** @brief The TimingData that is used for processing certain functions. */
|
||||
TimingData * processedTiming;
|
||||
public:
|
||||
/** @brief Set up the GameState with initial values. */
|
||||
GameState();
|
||||
@@ -91,8 +97,6 @@ public:
|
||||
* to get one credit, only to have to put in another four coins to get
|
||||
* the three credits needed to begin the game. */
|
||||
BroadcastOnChange<int> m_iCoins;
|
||||
/** @brief The player number used with Styles where one player controls both sides. */
|
||||
PlayerNumber m_MasterPlayerNumber;
|
||||
bool m_bMultiplayer;
|
||||
int m_iNumMultiplayerNoteFields;
|
||||
bool DifficultiesLocked() const;
|
||||
@@ -139,6 +143,26 @@ public:
|
||||
PlayerNumber GetFirstDisabledPlayer() const;
|
||||
bool IsCpuPlayer( PlayerNumber pn ) const;
|
||||
bool AnyPlayersAreCpu() const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the present master player number.
|
||||
* @return The master player number. */
|
||||
PlayerNumber GetMasterPlayerNumber() const;
|
||||
|
||||
/**
|
||||
* @brief Set the master player number.
|
||||
* @param p the master player number. */
|
||||
void SetMasterPlayerNumber(const PlayerNumber p);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the present timing data being processed.
|
||||
* @return the timing data pointer. */
|
||||
TimingData * GetProcessedTimingData() const;
|
||||
|
||||
/**
|
||||
* @brief Set the timing data to be used with processing.
|
||||
* @param t the timing data. */
|
||||
void SetProcessedTimingData(TimingData * t);
|
||||
|
||||
bool IsCourseMode() const;
|
||||
bool IsBattleMode() const; // not Rave
|
||||
|
||||
@@ -47,7 +47,7 @@ void GameplayAssist::PlayTicks( const NoteData &nd, const PlayerState *ps )
|
||||
if( nd.IsThereATapOrHoldHeadAtRow( r ) )
|
||||
iClapRow = r;
|
||||
|
||||
if( iClapRow != -1 && !timing.IsWarpAtRow( iClapRow ) && !timing.IsFakeAtRow( iClapRow ) )
|
||||
if( iClapRow != -1 && timing.IsJudgableAtRow(iClapRow))
|
||||
{
|
||||
const float fTickBeat = NoteRowToBeat( iClapRow );
|
||||
const float fTickSecond = timing.GetElapsedTimeFromBeatNoOffset( fTickBeat );
|
||||
|
||||
@@ -88,7 +88,9 @@ void GhostArrowRow::Update( float fDeltaTime )
|
||||
void GhostArrowRow::DrawPrimitives()
|
||||
{
|
||||
for( unsigned c=0; c<m_Ghost.size(); c++ )
|
||||
{
|
||||
m_Ghost[c]->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void GhostArrowRow::DidTapNote( int iCol, TapNoteScore tns, bool bBright )
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "global.h"
|
||||
#include "InGameLoadingWindow.h"
|
||||
#include "ScreenManager.h"
|
||||
#include "ThemeManager.h"
|
||||
#include "ActorUtil.h"
|
||||
|
||||
//REGISTER_ACTOR_CLASS( InGameLoadingWindow );
|
||||
|
||||
InGameLoadingWindow::InGameLoadingWindow() {
|
||||
SetName("InGameLoadingWindow");
|
||||
m_Text.SetName("LoadingText");
|
||||
m_Text.LoadFromFont( THEME->GetPathF(m_sName, "LoadingText") );
|
||||
m_Text.SetXY(0,0);
|
||||
AddChild(&m_Text);
|
||||
}
|
||||
|
||||
InGameLoadingWindow::~InGameLoadingWindow() {
|
||||
RemoveChild(&m_Text);
|
||||
}
|
||||
|
||||
void InGameLoadingWindow::SetText( RString str ) {
|
||||
textChanged=true;
|
||||
currentText=str;
|
||||
}
|
||||
|
||||
void InGameLoadingWindow::Update(float delta) {
|
||||
if(textChanged) {
|
||||
m_Text.SetText( currentText );
|
||||
textChanged=false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "arch/LoadingWindow/LoadingWindow.h"
|
||||
#include "BitmapText.h"
|
||||
#include "RageTimer.h"
|
||||
#include "global.h"
|
||||
#include "ActorFrame.h"
|
||||
|
||||
class InGameLoadingWindow: public LoadingWindow, public ActorFrame {
|
||||
|
||||
public:
|
||||
InGameLoadingWindow();
|
||||
~InGameLoadingWindow();
|
||||
|
||||
void SetText( RString str );
|
||||
void Update ( float delta );
|
||||
|
||||
private:
|
||||
bool textChanged;
|
||||
RString currentText;
|
||||
BitmapText m_Text;
|
||||
};
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ static const AutoMappings g_DefaultKeyMappings = AutoMappings(
|
||||
AutoMappingEntry( 0, KEY_KP_C2, GAME_BUTTON_MENUDOWN, true ),
|
||||
AutoMappingEntry( 0, KEY_KP_ENTER, GAME_BUTTON_START, true ),
|
||||
AutoMappingEntry( 0, KEY_KP_C0, GAME_BUTTON_SELECT, true ),
|
||||
AutoMappingEntry( 0, KEY_NUMLOCK, GAME_BUTTON_BACK, true ),
|
||||
AutoMappingEntry( 0, KEY_HYPHEN, GAME_BUTTON_BACK, true ), // laptop keyboards.
|
||||
AutoMappingEntry( 0, KEY_F1, GAME_BUTTON_COIN, false ),
|
||||
AutoMappingEntry( 0, KEY_SCRLLOCK, GAME_BUTTON_OPERATOR, false )
|
||||
);
|
||||
|
||||
+3
-2
@@ -11,6 +11,7 @@
|
||||
#include "ThemeMetric.h"
|
||||
#include "PlayerState.h"
|
||||
|
||||
void ReloadItems();
|
||||
|
||||
#define NUM_ITEM_TYPES THEME->GetMetricF("Inventory","NumItemTypes")
|
||||
#define ITEM_DURATION_SECONDS THEME->GetMetricF("Inventory","ItemDurationSeconds")
|
||||
@@ -118,10 +119,10 @@ void Inventory::Update( float fDelta )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Song &song = *GAMESTATE->m_pCurSong;
|
||||
// use items if this player is CPU-controlled
|
||||
if( m_pPlayerState->m_PlayerController != PC_HUMAN &&
|
||||
GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat )
|
||||
GAMESTATE->m_Position.m_fSongBeat < song.GetLastBeat() )
|
||||
{
|
||||
// every 1 seconds, try to use an item
|
||||
int iLastSecond = (int)(RageTimer::GetTimeSinceStartFast() - fDelta);
|
||||
|
||||
+1
-2
@@ -14,8 +14,7 @@ bool JsonUtil::LoadFromString(Json::Value &root, RString sData, RString &sErrorO
|
||||
if (!parsingSuccessful)
|
||||
{
|
||||
RString err = reader.getFormatedErrorMessages();
|
||||
sErrorOut = ssprintf("JSON: LoadFromFileShowErrors failed: %s", err.c_str());
|
||||
LOG->Warn(sErrorOut);
|
||||
LOG->Warn("JSON: LoadFromFileShowErrors failed: %s", err.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -31,6 +31,24 @@ namespace JsonUtil
|
||||
for(unsigned i=0; i<v.size(); i++)
|
||||
fn(*v[i], root[i]);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void SerializeVectorPointers(const vector<T*> &v, void fn(const T &, Json::Value &), Json::Value &root)
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
root.resize(v.size());
|
||||
for(unsigned i=0; i<v.size(); i++)
|
||||
fn(*v[i], root[i]);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void SerializeVectorPointers(const vector<const T*> &v, void fn(const T *, Json::Value &), Json::Value &root)
|
||||
{
|
||||
root = Json::Value(Json::arrayValue);
|
||||
root.resize(v.size());
|
||||
for(unsigned i=0; i<v.size(); i++)
|
||||
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)
|
||||
@@ -149,6 +167,19 @@ namespace JsonUtil
|
||||
fn(*v[i], root[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void DeserializeVectorPointers(vector<T*> &v, void fn(T *, const Json::Value &), const Json::Value &root)
|
||||
{
|
||||
for(unsigned i=0; i<v.size(); i++)
|
||||
SAFE_DELETE(v[i]);
|
||||
v.resize(root.size());
|
||||
for(unsigned i=0; i<v.size(); i++)
|
||||
{
|
||||
v[i] = new T;
|
||||
fn(*v[i], root[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void DeserializeArrayValues(vector<T> &v, const Json::Value &root)
|
||||
|
||||
@@ -172,7 +172,7 @@ void LifeMeterBar::ChangeLife( HoldNoteScore score, TapNoteScore tscore )
|
||||
switch( score )
|
||||
{
|
||||
case HNS_Held: fDeltaLife = +0; break;
|
||||
case HNS_LetGo: fDeltaLife = -1.0; break;
|
||||
case HNS_LetGo: fDeltaLife = -1.0f; break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
+69
-44
@@ -26,6 +26,14 @@ void LifeMeterBattery::Load( const PlayerState *pPlayerState, PlayerStageStats *
|
||||
const RString sType = "LifeMeterBattery";
|
||||
PlayerNumber pn = pPlayerState->m_PlayerNumber;
|
||||
|
||||
MIN_SCORE_TO_KEEP_LIFE.Load(sType, "MinScoreToKeepLife");
|
||||
DANGER_THRESHOLD.Load(sType, "DangerThreshold");
|
||||
SUBTRACT_LIVES.Load(sType, "SubtractLives");
|
||||
MINES_SUBTRACT_LIVES.Load(sType, "MinesSubtractLives");
|
||||
HELD_ADD_LIVES.Load(sType, "HeldAddLives");
|
||||
LET_GO_SUBTRACT_LIVES.Load(sType, "LetGoSubtractLives");
|
||||
|
||||
LIVES_FORMAT.Load(sType, "NumLivesFormat");
|
||||
BATTERY_BLINK_TIME.Load(sType, "BatteryBlinkTime"); // 1.2f by default
|
||||
|
||||
bool bPlayerEnabled = GAMESTATE->IsPlayerEnabled( pPlayerState );
|
||||
@@ -101,42 +109,54 @@ void LifeMeterBattery::OnSongEnded()
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void LifeMeterBattery::SubtractLives( int iLives )
|
||||
{
|
||||
if( iLives <= 0 )
|
||||
return;
|
||||
|
||||
m_iTrailingLivesLeft = m_iLivesLeft;
|
||||
m_iLivesLeft -= iLives;
|
||||
m_soundLoseLife.Play();
|
||||
m_textNumLives.PlayCommand("LoseLife");
|
||||
|
||||
Refresh();
|
||||
m_fBatteryBlinkTime = BATTERY_BLINK_TIME;
|
||||
}
|
||||
|
||||
void LifeMeterBattery::AddLives( int iLives )
|
||||
{
|
||||
if( iLives <= 0 )
|
||||
return;
|
||||
|
||||
m_iTrailingLivesLeft = m_iLivesLeft;
|
||||
m_iLivesLeft += iLives;
|
||||
m_soundGainLife.Play();
|
||||
m_textNumLives.PlayCommand("GainLife");
|
||||
|
||||
Refresh();
|
||||
m_fBatteryBlinkTime = 0;
|
||||
}
|
||||
|
||||
void LifeMeterBattery::ChangeLives(int iLifeDiff)
|
||||
{
|
||||
if( iLifeDiff < 0 )
|
||||
SubtractLives( abs(iLifeDiff) );
|
||||
else if( iLifeDiff > 0 )
|
||||
AddLives(iLifeDiff);
|
||||
}
|
||||
|
||||
void LifeMeterBattery::ChangeLife( TapNoteScore score )
|
||||
{
|
||||
if( m_iLivesLeft == 0 )
|
||||
return;
|
||||
|
||||
// todo: let the themer decide how this is handled. -aj
|
||||
switch( score )
|
||||
// this probably doesn't handle hold checkpoints. -aj
|
||||
if( score == TNS_HitMine && MINES_SUBTRACT_LIVES > 0 )
|
||||
SubtractLives(MINES_SUBTRACT_LIVES);
|
||||
else
|
||||
{
|
||||
case TNS_W1:
|
||||
case TNS_W2:
|
||||
case TNS_W3:
|
||||
break;
|
||||
case TNS_W4:
|
||||
case TNS_W5:
|
||||
case TNS_Miss:
|
||||
case TNS_HitMine:
|
||||
m_iTrailingLivesLeft = m_iLivesLeft;
|
||||
m_iLivesLeft--;
|
||||
m_soundLoseLife.Play();
|
||||
|
||||
m_textNumLives.PlayCommand("LoseLife");
|
||||
/*
|
||||
m_textNumLives.SetZoom( 1.5f );
|
||||
m_textNumLives.BeginTweening( 0.15f );
|
||||
m_textNumLives.SetZoom( 1.0f );
|
||||
*/
|
||||
|
||||
Refresh();
|
||||
m_fBatteryBlinkTime = BATTERY_BLINK_TIME;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
/*
|
||||
// xxx: this doesn't handle hold checkpoints.
|
||||
ASSERT(0);
|
||||
*/
|
||||
if( score < MIN_SCORE_TO_KEEP_LIFE && score > TNS_CheckpointMiss && SUBTRACT_LIVES > 0 )
|
||||
SubtractLives(SUBTRACT_LIVES);
|
||||
}
|
||||
|
||||
Message msg( "LifeChanged" );
|
||||
@@ -148,16 +168,19 @@ void LifeMeterBattery::ChangeLife( TapNoteScore score )
|
||||
|
||||
void LifeMeterBattery::ChangeLife( HoldNoteScore score, TapNoteScore tscore )
|
||||
{
|
||||
switch( score )
|
||||
{
|
||||
case HNS_Held:
|
||||
break;
|
||||
case HNS_LetGo:
|
||||
ChangeLife( TNS_Miss ); // LetGo is the same as a miss
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
if( m_iLivesLeft == 0 )
|
||||
return;
|
||||
|
||||
if( score == HNS_Held && HELD_ADD_LIVES > 0 )
|
||||
AddLives(HELD_ADD_LIVES);
|
||||
if( score == HNS_LetGo && LET_GO_SUBTRACT_LIVES > 0 )
|
||||
SubtractLives(LET_GO_SUBTRACT_LIVES);
|
||||
|
||||
Message msg( "LifeChanged" );
|
||||
msg.SetParam( "Player", m_pPlayerState->m_PlayerNumber );
|
||||
msg.SetParam( "LifeMeter", LuaReference::CreateFromPush(*this) );
|
||||
msg.SetParam( "LivesLeft", GetLivesLeft() );
|
||||
MESSAGEMAN->Broadcast( msg );
|
||||
}
|
||||
|
||||
void LifeMeterBattery::HandleTapScoreNone()
|
||||
@@ -171,12 +194,12 @@ void LifeMeterBattery::ChangeLife( float fDeltaLifePercent )
|
||||
|
||||
bool LifeMeterBattery::IsInDanger() const
|
||||
{
|
||||
return false;
|
||||
return m_iLivesLeft < DANGER_THRESHOLD;
|
||||
}
|
||||
|
||||
bool LifeMeterBattery::IsHot() const
|
||||
{
|
||||
return false;
|
||||
return m_iLivesLeft == GAMESTATE->m_SongOptions.GetSong().m_iBatteryLives;
|
||||
}
|
||||
|
||||
bool LifeMeterBattery::IsFailing() const
|
||||
@@ -208,7 +231,8 @@ void LifeMeterBattery::Refresh()
|
||||
}
|
||||
else
|
||||
{
|
||||
m_textNumLives.SetText( ssprintf("x%d", m_iLivesLeft-1) );
|
||||
//m_textNumLives.SetText( ssprintf("x%d", m_iLivesLeft-1) );
|
||||
m_textNumLives.SetText( ssprintf(LIVES_FORMAT.GetValue(), m_iLivesLeft-1) );
|
||||
m_sprBattery.SetState( 3 );
|
||||
}
|
||||
}
|
||||
@@ -244,13 +268,14 @@ class LunaLifeMeterBattery: public Luna<LifeMeterBattery>
|
||||
{
|
||||
public:
|
||||
static int GetLivesLeft( T* p, lua_State *L ) { lua_pushnumber( L, p->GetLivesLeft() ); return 1; }
|
||||
// is this right? wtf -q2x
|
||||
static int GetTotalLives( T* p, lua_State *L ) { lua_pushnumber( L, GAMESTATE->m_SongOptions.GetSong().m_iBatteryLives ); return 1; }
|
||||
static int ChangeLives( T* p, lua_State *L ) { p->ChangeLives(IArg(1)); return 0; }
|
||||
|
||||
LunaLifeMeterBattery()
|
||||
{
|
||||
ADD_METHOD( GetLivesLeft );
|
||||
ADD_METHOD( GetTotalLives );
|
||||
ADD_METHOD( ChangeLives );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+11
-1
@@ -32,18 +32,28 @@ public:
|
||||
|
||||
void Refresh();
|
||||
int GetLivesLeft() { return m_iLivesLeft; }
|
||||
void ChangeLives(int iLifeDiff);
|
||||
|
||||
// Lua
|
||||
virtual void PushSelf( lua_State *L );
|
||||
|
||||
private:
|
||||
void SubtractLives( int iLives );
|
||||
void AddLives( int iLives );
|
||||
|
||||
int m_iLivesLeft; // dead when 0
|
||||
int m_iTrailingLivesLeft; // lags m_iLivesLeft
|
||||
|
||||
float m_fBatteryBlinkTime; // if > 0 battery is blinking
|
||||
|
||||
// theme metrics added for sm-ssc
|
||||
ThemeMetric<float> BATTERY_BLINK_TIME;
|
||||
ThemeMetric<TapNoteScore> MIN_SCORE_TO_KEEP_LIFE;
|
||||
ThemeMetric<int> DANGER_THRESHOLD;
|
||||
ThemeMetric<int> SUBTRACT_LIVES;
|
||||
ThemeMetric<int> MINES_SUBTRACT_LIVES;
|
||||
ThemeMetric<int> HELD_ADD_LIVES;
|
||||
ThemeMetric<int> LET_GO_SUBTRACT_LIVES;
|
||||
ThemeMetric<RString> LIVES_FORMAT;
|
||||
|
||||
AutoActor m_sprFrame;
|
||||
Sprite m_sprBattery;
|
||||
|
||||
@@ -220,9 +220,11 @@ inline bool MyLua_checkintboolean( lua_State *L, int iArg )
|
||||
#define FArg(n) ((float) luaL_checknumber(L,(n)))
|
||||
|
||||
#define LuaFunction( func, expr ) \
|
||||
int LuaFunc_##func( lua_State *L ); \
|
||||
int LuaFunc_##func( lua_State *L ) { \
|
||||
LuaHelpers::Push( L, expr ); return 1; \
|
||||
} \
|
||||
void LuaFunc_Register_##func( lua_State *L ); \
|
||||
void LuaFunc_Register_##func( lua_State *L ) { lua_register( L, #func, LuaFunc_##func ); } \
|
||||
REGISTER_WITH_LUA_FUNCTION( LuaFunc_Register_##func );
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ void LyricDisplay::Update( float fDeltaTime )
|
||||
if( m_iCurLyricNumber+1 < GAMESTATE->m_pCurSong->m_LyricSegments.size() )
|
||||
fEndTime = pSong->m_LyricSegments[m_iCurLyricNumber+1].m_fStartTime;
|
||||
else
|
||||
fEndTime = pSong->m_SongTiming.GetElapsedTimeFromBeat( pSong->m_fLastBeat );
|
||||
|
||||
fEndTime = pSong->GetLastSecond();
|
||||
|
||||
const float fDistance = fEndTime - pSong->m_LyricSegments[m_iCurLyricNumber].m_fStartTime;
|
||||
const float fTweenBufferTime = IN_LENGTH.GetValue() + OUT_LENGTH.GetValue();
|
||||
|
||||
|
||||
+2
-1
@@ -107,7 +107,7 @@ ScreenSyncOverlay.cpp ScreenSyncOverlay.h \
|
||||
ScreenSystemLayer.cpp ScreenSystemLayer.h ScreenSetTime.cpp ScreenSetTime.h \
|
||||
ScreenSongOptions.cpp ScreenSongOptions.h \
|
||||
ScreenSplash.cpp ScreenSplash.h \
|
||||
ScreenTestFonts.cpp ScreenTestFonts.h ScreenTestInput.cpp ScreenTestInput.h \
|
||||
ScreenTestInput.cpp ScreenTestInput.h \
|
||||
ScreenTestLights.cpp ScreenTestLights.h ScreenTestSound.cpp ScreenTestSound.h ScreenTextEntry.cpp ScreenTextEntry.h \
|
||||
ScreenTitleMenu.cpp ScreenTitleMenu.h \
|
||||
ScreenUnlockBrowse.cpp ScreenUnlockBrowse.h \
|
||||
@@ -340,6 +340,7 @@ DualScrollBar.cpp DualScrollBar.h \
|
||||
EditMenu.cpp EditMenu.h FadingBanner.cpp FadingBanner.h \
|
||||
GradeDisplay.cpp GradeDisplay.h GraphDisplay.cpp GraphDisplay.h \
|
||||
GrooveRadar.cpp GrooveRadar.h HelpDisplay.cpp HelpDisplay.h \
|
||||
InGameLoadingWindow.cpp InGameLoadingWindow.h \
|
||||
MemoryCardDisplay.cpp MemoryCardDisplay.h \
|
||||
MenuTimer.cpp MenuTimer.h \
|
||||
ModIcon.cpp ModIcon.h ModIconRow.cpp ModIconRow.h \
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "ThemeMetric.h"
|
||||
#include "AutoActor.h"
|
||||
|
||||
RString WARNING_COMMAND_NAME( size_t i );
|
||||
|
||||
class MenuTimer : public ActorFrame
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -73,8 +73,8 @@ void SongMeterDisplay::Update( float fDeltaTime )
|
||||
{
|
||||
if( GAMESTATE->m_pCurSong )
|
||||
{
|
||||
float fSongStartSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fFirstBeat );
|
||||
float fSongEndSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat );
|
||||
float fSongStartSeconds = GAMESTATE->m_pCurSong->GetFirstSecond();
|
||||
float fSongEndSeconds = GAMESTATE->m_pCurSong->GetLastSecond();
|
||||
float fPercentPositionSong = SCALE( GAMESTATE->m_Position.m_fMusicSeconds, fSongStartSeconds, fSongEndSeconds, 0.0f, 1.0f );
|
||||
CLAMP( fPercentPositionSong, 0, 1 );
|
||||
|
||||
|
||||
+3
-24
@@ -10,6 +10,8 @@
|
||||
#include "LuaManager.h"
|
||||
#include "Foreach.h"
|
||||
|
||||
int OptionToPreferredColumn( RString sOptionText );
|
||||
|
||||
REGISTER_ACTOR_CLASS( ModIconRow );
|
||||
|
||||
ModIconRow::ModIconRow()
|
||||
@@ -66,35 +68,12 @@ void ModIconRow::HandleMessage( const Message &msg )
|
||||
|
||||
struct OptionColumnEntry
|
||||
{
|
||||
char *szString;
|
||||
const 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[] =
|
||||
{
|
||||
|
||||
+3
-3
@@ -969,7 +969,7 @@ void MusicWheel::FilterWheelItemDatas(vector<MusicWheelItemData *> &aUnFilteredD
|
||||
}
|
||||
|
||||
/* If the song has no steps for the current style, remove it. */
|
||||
if( !pSong->HasStepsType(GAMESTATE->GetCurrentStyle()->m_StepsType) )
|
||||
if( !CommonMetrics::AUTO_SET_STYLE && !pSong->HasStepsType(GAMESTATE->GetCurrentStyle()->m_StepsType) )
|
||||
{
|
||||
aiRemove[i] = true;
|
||||
continue;
|
||||
@@ -1476,11 +1476,11 @@ RString MusicWheel::JumpToPrevGroup()
|
||||
// in case it wasn't found above:
|
||||
for( unsigned int i = m_CurWheelItemData.size()-1; i > 0; --i )
|
||||
{
|
||||
LOG->Trace( ssprintf("JumpToPrevGroup iteration 2 | i = %u",i) );
|
||||
LOG->Trace( "JumpToPrevGroup iteration 2 | i = %u",i );
|
||||
if( m_CurWheelItemData[i]->m_Type == TYPE_SECTION )
|
||||
{
|
||||
m_iSelection = i;
|
||||
LOG->Trace( ssprintf("finding it in #2 | i = %u | text = %s",i, m_CurWheelItemData[i]->m_sText.c_str()) );
|
||||
LOG->Trace( "finding it in #2 | i = %u | text = %s",i, m_CurWheelItemData[i]->m_sText.c_str() );
|
||||
return m_CurWheelItemData[i]->m_sText;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +170,10 @@ MusicWheelItem::MusicWheelItem( const MusicWheelItem &cpy ):
|
||||
|
||||
MusicWheelItem::~MusicWheelItem()
|
||||
{
|
||||
FOREACH_ENUM( MusicWheelItemType, i )
|
||||
{
|
||||
SAFE_DELETE(m_pText[i]);
|
||||
}
|
||||
delete m_pTextSectionCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ void NetworkSyncManager::ReportScore(int playerID, int step, int score, int comb
|
||||
if( !useSMserver ) //Make sure that we are using the network
|
||||
return;
|
||||
|
||||
LOG->Trace( ssprintf("Player ID %i combo = %i", playerID, combo) );
|
||||
LOG->Trace( "Player ID %i combo = %i", playerID, combo );
|
||||
m_packet.ClearPacket();
|
||||
|
||||
m_packet.Write1( NSCGSU );
|
||||
|
||||
+220
-11
@@ -9,6 +9,7 @@
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "XmlFile.h"
|
||||
#include "GameState.h" // blame radar calculations.
|
||||
#include "Foreach.h"
|
||||
#include "RageUtil_AutoPtr.h"
|
||||
|
||||
@@ -458,6 +459,31 @@ int NoteData::GetLastRow() const
|
||||
return iOldestRowFoundSoFar;
|
||||
}
|
||||
|
||||
bool NoteData::IsTap(const TapNote &tn, const int row) const
|
||||
{
|
||||
return (tn.type != TapNote::empty && tn.type != TapNote::mine
|
||||
&& tn.type != TapNote::lift && tn.type != TapNote::fake
|
||||
&& GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(row));
|
||||
}
|
||||
|
||||
bool NoteData::IsMine(const TapNote &tn, const int row) const
|
||||
{
|
||||
return (tn.type == TapNote::mine
|
||||
&& GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(row));
|
||||
}
|
||||
|
||||
bool NoteData::IsLift(const TapNote &tn, const int row) const
|
||||
{
|
||||
return (tn.type == TapNote::lift
|
||||
&& GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(row));
|
||||
}
|
||||
|
||||
bool NoteData::IsFake(const TapNote &tn, const int row) const
|
||||
{
|
||||
return (tn.type == TapNote::fake
|
||||
|| !GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(row));
|
||||
}
|
||||
|
||||
int NoteData::GetNumTapNotes( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
int iNumNotes = 0;
|
||||
@@ -465,9 +491,7 @@ 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::lift && tn.type != TapNote::fake )
|
||||
if (this->IsTap(GetTapNote(t, r), r))
|
||||
iNumNotes++;
|
||||
}
|
||||
}
|
||||
@@ -485,7 +509,7 @@ int NoteData::GetNumRowsWithTap( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
int iNumNotes = 0;
|
||||
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, r, iStartIndex, iEndIndex )
|
||||
if( IsThereATapAtRow(r) )
|
||||
if( IsThereATapAtRow(r) && GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r) )
|
||||
iNumNotes++;
|
||||
|
||||
return iNumNotes;
|
||||
@@ -498,7 +522,7 @@ int NoteData::GetNumMines( int iStartIndex, int iEndIndex ) const
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
if( GetTapNote(t, r).type == TapNote::mine )
|
||||
if (this->IsMine(GetTapNote(t, r), r))
|
||||
iNumMines++;
|
||||
}
|
||||
|
||||
@@ -509,7 +533,7 @@ int NoteData::GetNumRowsWithTapOrHoldHead( int iStartIndex, int iEndIndex ) cons
|
||||
{
|
||||
int iNumNotes = 0;
|
||||
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, r, iStartIndex, iEndIndex )
|
||||
if( IsThereATapOrHoldHeadAtRow(r) )
|
||||
if( IsThereATapOrHoldHeadAtRow(r) && GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r) )
|
||||
iNumNotes++;
|
||||
|
||||
return iNumNotes;
|
||||
@@ -560,7 +584,8 @@ int NoteData::GetNumRowsWithSimultaneousPresses( int iMinSimultaneousPresses, in
|
||||
{
|
||||
if( !RowNeedsAtLeastSimultaneousPresses(iMinSimultaneousPresses,r) )
|
||||
continue;
|
||||
|
||||
if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r))
|
||||
continue;
|
||||
iNum++;
|
||||
}
|
||||
|
||||
@@ -576,7 +601,10 @@ int NoteData::GetNumRowsWithSimultaneousTaps( int iMinTaps, int iStartIndex, int
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
const TapNote &tn = GetTapNote(t, r);
|
||||
if( tn.type != TapNote::mine && tn.type != TapNote::empty && tn.type != TapNote::fake ) // mines don't count
|
||||
if( tn.type != TapNote::mine // mines don't count.
|
||||
&& tn.type != TapNote::empty
|
||||
&& tn.type != TapNote::fake
|
||||
&& GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r))
|
||||
iNumNotesThisIndex++;
|
||||
}
|
||||
if( iNumNotesThisIndex >= iMinTaps )
|
||||
@@ -598,6 +626,8 @@ int NoteData::GetNumHoldNotes( int iStartIndex, int iEndIndex ) const
|
||||
if( lBegin->second.type != TapNote::hold_head ||
|
||||
lBegin->second.subType != TapNote::hold_head_hold )
|
||||
continue;
|
||||
if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(lBegin->first))
|
||||
continue;
|
||||
iNumHolds++;
|
||||
}
|
||||
}
|
||||
@@ -616,6 +646,8 @@ int NoteData::GetNumRolls( int iStartIndex, int iEndIndex ) const
|
||||
if( lBegin->second.type != TapNote::hold_head ||
|
||||
lBegin->second.subType != TapNote::hold_head_roll )
|
||||
continue;
|
||||
if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(lBegin->first))
|
||||
continue;
|
||||
iNumRolls++;
|
||||
}
|
||||
}
|
||||
@@ -629,7 +661,7 @@ int NoteData::GetNumLifts( int iStartIndex, int iEndIndex ) const
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
if( GetTapNote(t, r).type == TapNote::lift )
|
||||
if( this->IsLift(GetTapNote(t, r), r))
|
||||
iNumLifts++;
|
||||
}
|
||||
|
||||
@@ -643,13 +675,190 @@ int NoteData::GetNumFakes( int iStartIndex, int iEndIndex ) const
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
if( GetTapNote(t, r).type == TapNote::fake )
|
||||
iNumFakes++;
|
||||
if( this->IsFake(GetTapNote(t, r), r))
|
||||
iNumFakes++;
|
||||
}
|
||||
|
||||
return iNumFakes;
|
||||
}
|
||||
|
||||
bool NoteData::IsPlayer1(const int track, const TapNote &tn) const
|
||||
{
|
||||
if (this->IsComposite())
|
||||
{
|
||||
return tn.pn == PLAYER_1;
|
||||
}
|
||||
return track < (this->GetNumTracks() / 2);
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumTapNotesTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
pair<int, int> num(0, 0);
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
{
|
||||
const TapNote &tn = GetTapNote(t, r);
|
||||
if (this->IsTap(tn, r))
|
||||
{
|
||||
if (this->IsPlayer1(t, tn))
|
||||
num.first++;
|
||||
else
|
||||
num.second++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumRowsWithSimultaneousTapsTwoPlayer(int minTaps,
|
||||
int startRow,
|
||||
int endRow) const
|
||||
{
|
||||
pair<int, int> num(0, 0);
|
||||
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, r, startRow, endRow )
|
||||
{
|
||||
pair<int, int> found(0, 0);
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
const TapNote &tn = GetTapNote(t, r);
|
||||
if (this->IsTap(tn, r))
|
||||
{
|
||||
if (this->IsPlayer1(t, tn))
|
||||
found.first++;
|
||||
else
|
||||
found.second++;
|
||||
}
|
||||
}
|
||||
if (found.first >= minTaps)
|
||||
num.first++;
|
||||
if (found.second >= minTaps)
|
||||
num.second++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumJumpsTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
return GetNumRowsWithSimultaneousTapsTwoPlayer( 2, iStartIndex, iEndIndex );
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumHandsTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
return GetNumRowsWithSimultaneousTapsTwoPlayer( 3, iStartIndex, iEndIndex );
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumQuadsTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
return GetNumRowsWithSimultaneousTapsTwoPlayer( 4, iStartIndex, iEndIndex );
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumHoldNotesTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
pair<int, int> num(0, 0);
|
||||
for( int t=0; t<GetNumTracks(); ++t )
|
||||
{
|
||||
NoteData::TrackMap::const_iterator lBegin, lEnd;
|
||||
GetTapNoteRangeExclusive( t, iStartIndex, iEndIndex, lBegin, lEnd );
|
||||
for( ; lBegin != lEnd; ++lBegin )
|
||||
{
|
||||
if( lBegin->second.type != TapNote::hold_head ||
|
||||
lBegin->second.subType != TapNote::hold_head_hold )
|
||||
continue;
|
||||
if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(lBegin->first))
|
||||
continue;
|
||||
if (this->IsPlayer1(t, lBegin->second))
|
||||
num.first++;
|
||||
else
|
||||
num.second++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumMinesTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
pair<int, int> num(0, 0);
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
{
|
||||
const TapNote &tn = GetTapNote(t, r);
|
||||
if (this->IsMine(tn, r))
|
||||
{
|
||||
if (this->IsPlayer1(t, tn))
|
||||
num.first++;
|
||||
else
|
||||
num.second++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumRollsTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
pair<int, int> num(0, 0);
|
||||
for( int t=0; t<GetNumTracks(); ++t )
|
||||
{
|
||||
NoteData::TrackMap::const_iterator lBegin, lEnd;
|
||||
GetTapNoteRangeExclusive( t, iStartIndex, iEndIndex, lBegin, lEnd );
|
||||
for( ; lBegin != lEnd; ++lBegin )
|
||||
{
|
||||
if( lBegin->second.type != TapNote::hold_head ||
|
||||
lBegin->second.subType != TapNote::hold_head_roll )
|
||||
continue;
|
||||
if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(lBegin->first))
|
||||
continue;
|
||||
if (this->IsPlayer1(t, lBegin->second))
|
||||
num.first++;
|
||||
else
|
||||
num.second++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumLiftsTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
pair<int, int> num(0, 0);
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
{
|
||||
const TapNote &tn = GetTapNote(t, r);
|
||||
if (this->IsLift(tn, r))
|
||||
{
|
||||
if (this->IsPlayer1(t, tn))
|
||||
num.first++;
|
||||
else
|
||||
num.second++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
pair<int, int> NoteData::GetNumFakesTwoPlayer( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
pair<int, int> num(0, 0);
|
||||
for( int t=0; t<GetNumTracks(); t++ )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, r, iStartIndex, iEndIndex )
|
||||
{
|
||||
const TapNote &tn = GetTapNote(t, r);
|
||||
if (this->IsFake(tn, r))
|
||||
{
|
||||
if (this->IsPlayer1(t, tn))
|
||||
num.first++;
|
||||
else
|
||||
num.second++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/*
|
||||
int NoteData::GetNumMinefields( int iStartIndex, int iEndIndex ) const
|
||||
{
|
||||
|
||||
+105
-10
@@ -90,7 +90,46 @@ private:
|
||||
// There's no point in inserting empty notes into the map.
|
||||
// Any blank space in the map is defined to be empty.
|
||||
vector<TrackMap> m_TapNotes;
|
||||
|
||||
/**
|
||||
* @brief Determine whether this note is for Player 1 or Player 2.
|
||||
* @param track the track/column the note is in.
|
||||
* @param tn the note in question. Required for routine mode.
|
||||
* @return true if it's for player 1, false for player 2. */
|
||||
bool IsPlayer1(const int track, const TapNote &tn) const;
|
||||
|
||||
/**
|
||||
* @brief Determine if the note in questino should be counted as a tap.
|
||||
* @param tn the note in question.
|
||||
* @param row the row it lives in.
|
||||
* @return true if it's a tap, false otherwise. */
|
||||
bool IsTap(const TapNote &tn, const int row) const;
|
||||
|
||||
/**
|
||||
* @brief Determine if the note in questino should be counted as a mine.
|
||||
* @param tn the note in question.
|
||||
* @param row the row it lives in.
|
||||
* @return true if it's a mine, false otherwise. */
|
||||
bool IsMine(const TapNote &tn, const int row) const;
|
||||
|
||||
/**
|
||||
* @brief Determine if the note in questino should be counted as a lift.
|
||||
* @param tn the note in question.
|
||||
* @param row the row it lives in.
|
||||
* @return true if it's a lift, false otherwise. */
|
||||
bool IsLift(const TapNote &tn, const int row) const;
|
||||
|
||||
/**
|
||||
* @brief Determine if the note in questino should be counted as a fake.
|
||||
* @param tn the note in question.
|
||||
* @param row the row it lives in.
|
||||
* @return true if it's a fake, false otherwise. */
|
||||
bool IsFake(const TapNote &tn, const int row) const;
|
||||
|
||||
pair<int, int> GetNumRowsWithSimultaneousTapsTwoPlayer(int minTaps = 2,
|
||||
int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
public:
|
||||
void Init();
|
||||
|
||||
@@ -130,7 +169,8 @@ public:
|
||||
* @param iEndRow the ending point.
|
||||
* @param begin the eventual beginning point of the range.
|
||||
* @param end the eventual end point of the range. */
|
||||
void GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const;
|
||||
void GetTapNoteRange(int iTrack, int iStartRow, int iEndRow,
|
||||
TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const;
|
||||
/**
|
||||
* @brief Return a constant iterator range for [rowBegin,rowEnd).
|
||||
* @param iTrack the column to use.
|
||||
@@ -158,13 +198,17 @@ public:
|
||||
|
||||
/* Return an iterator range include iStartRow to iEndRow. Extend the range to include
|
||||
* hold notes overlapping the boundary. */
|
||||
void GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end, bool bIncludeAdjacent=false ) const;
|
||||
void GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::iterator &begin, TrackMap::iterator &end, bool bIncludeAdjacent=false );
|
||||
void GetTapNoteRangeInclusive(int iTrack, int iStartRow, int iEndRow,
|
||||
TrackMap::const_iterator &begin, TrackMap::const_iterator &end, bool bIncludeAdjacent=false ) const;
|
||||
void GetTapNoteRangeInclusive(int iTrack, int iStartRow, int iEndRow,
|
||||
TrackMap::iterator &begin, TrackMap::iterator &end, bool bIncludeAdjacent=false );
|
||||
|
||||
/* Return an iterator range include iStartRow to iEndRow. Shrink the range to exclude
|
||||
* hold notes overlapping the boundary. */
|
||||
void GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const;
|
||||
void GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::iterator &begin, TrackMap::iterator &end );
|
||||
void GetTapNoteRangeExclusive(int iTrack, int iStartRow, int iEndRow,
|
||||
TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const;
|
||||
void GetTapNoteRangeExclusive(int iTrack, int iStartRow, int iEndRow,
|
||||
TrackMap::iterator &begin, TrackMap::iterator &end );
|
||||
|
||||
|
||||
/* Returns the row of the first TapNote on the track that has a row greater than rowInOut. */
|
||||
@@ -175,7 +219,17 @@ public:
|
||||
|
||||
void MoveTapNoteTrack( int dest, int src );
|
||||
void SetTapNote( int track, int row, const TapNote& tn );
|
||||
void AddHoldNote( int iTrack, int iStartRow, int iEndRow, TapNote tn ); // add note hold note merging overlapping HoldNotes and destroying TapNotes underneath
|
||||
/**
|
||||
* @brief Add a hold note, merging other overlapping holds and destroying
|
||||
* tap notes underneath.
|
||||
* @param iTrack the column to work with.
|
||||
* @param iStartRow the starting row.
|
||||
* @param iEndRow the ending row.
|
||||
* @param tn the tap note. */
|
||||
void AddHoldNote(int iTrack,
|
||||
int iStartRow,
|
||||
int iEndRow,
|
||||
TapNote tn );
|
||||
|
||||
void ClearRangeForTrack( int rowBegin, int rowEnd, int iTrack );
|
||||
void ClearRange( int rowBegin, int rowEnd );
|
||||
@@ -222,7 +276,12 @@ public:
|
||||
|
||||
// Count rows that contain iMinTaps or more taps.
|
||||
int GetNumRowsWithSimultaneousTaps( int iMinTaps, int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const;
|
||||
int GetNumJumps( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const { return GetNumRowsWithSimultaneousTaps( 2, iStartIndex, iEndIndex ); }
|
||||
int GetNumJumps( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const
|
||||
{
|
||||
return GetNumRowsWithSimultaneousTaps( 2, iStartIndex, iEndIndex );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This row needs at least iMinSimultaneousPresses either tapped or held.
|
||||
bool RowNeedsAtLeastSimultaneousPresses( int iMinSimultaneousPresses, int row ) const;
|
||||
@@ -230,15 +289,51 @@ public:
|
||||
|
||||
// Count rows that need iMinSimultaneousPresses either tapped or held.
|
||||
int GetNumRowsWithSimultaneousPresses( int iMinSimultaneousPresses, int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const;
|
||||
int GetNumHands( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const { return GetNumRowsWithSimultaneousPresses( 3, iStartIndex, iEndIndex ); }
|
||||
int GetNumQuads( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const { return GetNumRowsWithSimultaneousPresses( 4, iStartIndex, iEndIndex ); }
|
||||
int GetNumHands( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const
|
||||
{
|
||||
return GetNumRowsWithSimultaneousPresses( 3, iStartIndex, iEndIndex );
|
||||
}
|
||||
int GetNumQuads( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const
|
||||
{
|
||||
return GetNumRowsWithSimultaneousPresses( 4, iStartIndex, iEndIndex );
|
||||
}
|
||||
|
||||
// and the other notetypes
|
||||
int GetNumLifts( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const;
|
||||
int GetNumFakes( int iStartIndex = 0, int iEndIndex = MAX_NOTE_ROW ) const;
|
||||
|
||||
// the couple/routine style variants of the above.
|
||||
pair<int, int> GetNumTapNotesTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumJumpsTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumHandsTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumQuadsTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumHoldNotesTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumMinesTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumRollsTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumLiftsTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
pair<int, int> GetNumFakesTwoPlayer(int startRow = 0,
|
||||
int endRow = MAX_NOTE_ROW) const;
|
||||
|
||||
// Transformations
|
||||
void LoadTransformed( const NoteData& original, int iNewNumTracks, const int iOriginalTrackToTakeFrom[] ); // -1 for iOriginalTracksToTakeFrom means no track
|
||||
void LoadTransformed(const NoteData& original,
|
||||
int iNewNumTracks,
|
||||
const int iOriginalTrackToTakeFrom[] ); // -1 for iOriginalTracksToTakeFrom means no track
|
||||
|
||||
// XML
|
||||
XNode* CreateNode() const;
|
||||
|
||||
@@ -778,13 +778,6 @@ void NoteDataUtil::LoadTransformedLightsFromTwo( const NoteData &marquee, const
|
||||
NoteDataUtil::RemoveMines( out );
|
||||
}
|
||||
|
||||
struct RadarStats {
|
||||
int taps;
|
||||
int jumps;
|
||||
int hands;
|
||||
int quads;
|
||||
};
|
||||
|
||||
RadarStats CalculateRadarStatsFast( const NoteData &in, RadarStats &out )
|
||||
{
|
||||
out.taps = 0;
|
||||
@@ -799,12 +792,18 @@ RadarStats CalculateRadarStatsFast( const NoteData &in, RadarStats &out )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( in, t, r, 0, MAX_NOTE_ROW )
|
||||
{
|
||||
/* This function deals strictly with taps, jumps, hands, and quads.
|
||||
* As such, all rows in here have to be judgable. */
|
||||
if (!GAMESTATE->GetProcessedTimingData()->IsJudgableAtRow(r))
|
||||
continue;
|
||||
|
||||
const TapNote &tn = in.GetTapNote(t, r);
|
||||
switch( tn.type )
|
||||
{
|
||||
case TapNote::mine:
|
||||
case TapNote::empty:
|
||||
case TapNote::fake:
|
||||
case TapNote::autoKeysound:
|
||||
continue; // skip these types - they don't count
|
||||
}
|
||||
|
||||
@@ -886,7 +885,7 @@ void NoteDataUtil::CalculateRadarValues( const NoteData &in, float fSongSeconds,
|
||||
case RadarCategory_Jumps: out[rc] = (float) stats.jumps; break;
|
||||
case RadarCategory_Holds: out[rc] = (float) in.GetNumHoldNotes(); break;
|
||||
case RadarCategory_Mines: out[rc] = (float) in.GetNumMines(); break;
|
||||
case RadarCategory_Hands: out[rc] = (float) stats.hands; break;
|
||||
case RadarCategory_Hands: out[rc] = (float) in.GetNumHands(); break;
|
||||
case RadarCategory_Rolls: out[rc] = (float) in.GetNumRolls(); break;
|
||||
case RadarCategory_Lifts: out[rc] = (float) in.GetNumLifts(); break;
|
||||
case RadarCategory_Fakes: out[rc] = (float) in.GetNumFakes(); break;
|
||||
|
||||
@@ -10,6 +10,24 @@ class NoteData;
|
||||
class Song;
|
||||
struct AttackArray;
|
||||
|
||||
/** @brief A limited selection of the RadarValues. */
|
||||
struct RadarStats
|
||||
{
|
||||
/** @brief The number of tap notes in the song. */
|
||||
int taps;
|
||||
/** @brief The number of jumps in the song. */
|
||||
int jumps;
|
||||
/** @brief The number of 3 panel hits in the song. */
|
||||
int hands;
|
||||
/** @brief The number of 4 panel hits in the song. */
|
||||
int quads;
|
||||
};
|
||||
|
||||
void PlaceAutoKeysound( NoteData &out, int row, TapNote akTap );
|
||||
int FindLongestOverlappingHoldNoteForAnyTrack( const NoteData &in, int iRow );
|
||||
void LightTransformHelper( const NoteData &in, NoteData &out, const vector<int> &aiTracks );
|
||||
RadarStats CalculateRadarStatsFast( const NoteData &in, RadarStats &out );
|
||||
|
||||
/**
|
||||
* @brief Utility functions that deal with NoteData.
|
||||
*
|
||||
|
||||
@@ -349,7 +349,7 @@ void NoteDataWithScoring::GetActualRadarValues( const NoteData &in, const Player
|
||||
case RadarCategory_Hands: out[rc] = (float) GetSuccessfulHands( in ); break;
|
||||
case RadarCategory_Rolls: out[rc] = (float) GetNumHoldNotesWithScore( in, TapNote::hold_head_roll, HNS_Held ); break;
|
||||
case RadarCategory_Lifts: out[rc] = (float) GetSuccessfulLifts( in, TNS_W4 ); break;
|
||||
case RadarCategory_Fakes: out[rc] = (float) in.GetNumLifts(); break;
|
||||
case RadarCategory_Fakes: out[rc] = (float) in.GetNumFakes(); break;
|
||||
//case RadarCategory_Minefields: out[rc] = (float) GetNumMinefieldsWithScore( in, TapNote::hold_head_mine, HNS_Held ); break;
|
||||
DEFAULT_FAIL( rc );
|
||||
}
|
||||
|
||||
+57
-5
@@ -46,6 +46,8 @@ static const NoteType MAX_DISPLAY_NOTE_TYPE = (NoteType)7;
|
||||
struct NoteMetricCache_t
|
||||
{
|
||||
bool m_bDrawHoldHeadForTapsOnSameRow;
|
||||
bool m_bDrawRollHeadForTapsOnSameRow;
|
||||
bool m_bTapHoldRollOnRowMeansHold;
|
||||
float m_fAnimationLength[NUM_NotePart];
|
||||
bool m_bAnimationIsVivid[NUM_NotePart];
|
||||
RageVector2 m_fAdditionTextureCoordOffset[NUM_NotePart];
|
||||
@@ -69,6 +71,8 @@ struct NoteMetricCache_t
|
||||
void NoteMetricCache_t::Load( const RString &sButton )
|
||||
{
|
||||
m_bDrawHoldHeadForTapsOnSameRow = NOTESKIN->GetMetricB(sButton,"DrawHoldHeadForTapsOnSameRow");
|
||||
m_bDrawRollHeadForTapsOnSameRow = NOTESKIN->GetMetricB(sButton,"DrawRollHeadForTapsOnSameRow");
|
||||
m_bTapHoldRollOnRowMeansHold = NOTESKIN->GetMetricB(sButton,"TapHoldRollOnRowMeansHold");
|
||||
FOREACH_NotePart( p )
|
||||
{
|
||||
const RString &s = NotePartToString(p);
|
||||
@@ -271,6 +275,11 @@ bool NoteDisplay::DrawHoldHeadForTapsOnSameRow() const
|
||||
return cache->m_bDrawHoldHeadForTapsOnSameRow;
|
||||
}
|
||||
|
||||
bool NoteDisplay::DrawRollHeadForTapsOnSameRow() const
|
||||
{
|
||||
return cache->m_bDrawRollHeadForTapsOnSameRow;
|
||||
}
|
||||
|
||||
void NoteDisplay::Update( float fDeltaTime )
|
||||
{
|
||||
/* This function is static: it's called once per game loop, not once per
|
||||
@@ -683,11 +692,18 @@ void NoteDisplay::DrawActor( const TapNote& tn, Actor* pActor, NotePart part, in
|
||||
const float fGlow = ArrowEffects::GetGlow( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar );
|
||||
const RageColor diffuse = RageColor(fColorScale,fColorScale,fColorScale,fAlpha);
|
||||
const RageColor glow = RageColor(1,1,1,fGlow);
|
||||
float fRotationX = 0, fRotationY = 0, fRotationZ = 0;
|
||||
float fRotationX = 0, fRotationZ = 0;
|
||||
const float fRotationY = ArrowEffects::GetRotationY( m_pPlayerState, fYOffset );
|
||||
|
||||
bool bIsHoldHead = tn.type == tn.hold_head;
|
||||
bool bIsHoldCap = bIsHoldHead || tn.type == tn.hold_tail;
|
||||
|
||||
fRotationZ = ArrowEffects::GetRotationZ( m_pPlayerState, fBeat, bIsHoldHead );
|
||||
if( !bIsHoldCap )
|
||||
{
|
||||
fRotationX = ArrowEffects::GetRotationX( m_pPlayerState, fYOffset );
|
||||
}
|
||||
|
||||
fRotationX = ArrowEffects::GetRotationX( m_pPlayerState, fYOffset );
|
||||
fRotationY = ArrowEffects::GetRotationY( m_pPlayerState, fYOffset );
|
||||
fRotationZ = ArrowEffects::GetRotationZ( m_pPlayerState, fBeat, tn.type == tn.hold_head );
|
||||
if( tn.type != tn.hold_head )
|
||||
fColorScale *= ArrowEffects::GetBrightness( m_pPlayerState, fBeat );
|
||||
|
||||
@@ -718,7 +734,13 @@ void NoteDisplay::DrawActor( const TapNote& tn, Actor* pActor, NotePart part, in
|
||||
}
|
||||
}
|
||||
|
||||
void NoteDisplay::DrawTap( const TapNote& tn, int iCol, float fBeat, bool bOnSameRowAsHoldStart, bool bIsAddition, float fPercentFadeToFail, float fReverseOffsetPixels, float fDrawDistanceAfterTargetsPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar )
|
||||
void NoteDisplay::DrawTap(const TapNote& tn, int iCol, float fBeat,
|
||||
bool bOnSameRowAsHoldStart, bool bOnSameRowAsRollStart,
|
||||
bool bIsAddition, float fPercentFadeToFail,
|
||||
float fReverseOffsetPixels,
|
||||
float fDrawDistanceAfterTargetsPixels,
|
||||
float fDrawDistanceBeforeTargetsPixels,
|
||||
float fFadeInPercentOfDrawFar)
|
||||
{
|
||||
Actor* pActor = NULL;
|
||||
NotePart part = NotePart_Tap;
|
||||
@@ -738,10 +760,40 @@ void NoteDisplay::DrawTap( const TapNote& tn, int iCol, float fBeat, bool bOnSam
|
||||
pActor = GetTapActor( m_TapFake, NotePart_Fake, fBeat );
|
||||
part = NotePart_Fake;
|
||||
}
|
||||
// TODO: Simplify all of the below.
|
||||
else if (bOnSameRowAsHoldStart && bOnSameRowAsRollStart)
|
||||
{
|
||||
if (cache->m_bDrawHoldHeadForTapsOnSameRow && cache->m_bDrawRollHeadForTapsOnSameRow)
|
||||
{
|
||||
if (cache->m_bTapHoldRollOnRowMeansHold) // another new metric?
|
||||
{
|
||||
pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, fBeat, false, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, fBeat, true, false );
|
||||
}
|
||||
}
|
||||
else if (cache->m_bDrawHoldHeadForTapsOnSameRow)
|
||||
{
|
||||
pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, fBeat, false, false );
|
||||
}
|
||||
else if (cache->m_bDrawRollHeadForTapsOnSameRow)
|
||||
{
|
||||
pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, fBeat, true, false );
|
||||
}
|
||||
}
|
||||
|
||||
else if( bOnSameRowAsHoldStart && cache->m_bDrawHoldHeadForTapsOnSameRow )
|
||||
{
|
||||
pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, fBeat, false, false );
|
||||
}
|
||||
|
||||
else if( bOnSameRowAsRollStart && cache->m_bDrawRollHeadForTapsOnSameRow )
|
||||
{
|
||||
pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, fBeat, true, false );
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
pActor = GetTapActor( m_TapNote, NotePart_Tap, fBeat );
|
||||
|
||||
+22
-2
@@ -80,13 +80,33 @@ public:
|
||||
|
||||
static void Update( float fDeltaTime );
|
||||
|
||||
void DrawTap( const TapNote& tn, int iCol, float fBeat, bool bOnSameRowAsHoldStart, bool bIsAddition,
|
||||
float fPercentFadeToFail, float fReverseOffsetPixels, float fDrawDistanceAfterTargetsPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar );
|
||||
/**
|
||||
* @brief Draw the TapNote onto the NoteField.
|
||||
* @param tn the TapNote in question.
|
||||
* @param iCol the column.
|
||||
* @param float fBeat the beat to draw them on.
|
||||
* @param bOnSameRowAsHoldStart a flag to see if a hold is on the same beat.
|
||||
* @param bOnSameRowAsRollStart a flag to see if a roll is on the same beat.
|
||||
* @param bIsAddition a flag to see if this note was added via mods.
|
||||
* @param fPercentFadeToFail at what point do the notes fade on failure?
|
||||
* @param fReverseOffsetPixels How are the notes adjusted on Reverse?
|
||||
* @param fDrawDistanceAfterTargetsPixels how much to draw after the receptors.
|
||||
* @param fDrawDistanceBeforeTargetsPixels how much ot draw before the receptors.
|
||||
* @param fFadeInPercentOfDrawFar when to start fading in. */
|
||||
void DrawTap(const TapNote& tn, int iCol, float fBeat,
|
||||
bool bOnSameRowAsHoldStart, bool bOnSameRowAsRollBeat,
|
||||
bool bIsAddition, float fPercentFadeToFail,
|
||||
float fReverseOffsetPixels,
|
||||
float fDrawDistanceAfterTargetsPixels,
|
||||
float fDrawDistanceBeforeTargetsPixels,
|
||||
float fFadeInPercentOfDrawFar );
|
||||
void DrawHold( const TapNote& tn, int iCol, int iRow, bool bIsBeingHeld, const HoldNoteResult &Result,
|
||||
bool bIsAddition, float fPercentFadeToFail, float fReverseOffsetPixels, float fDrawDistanceAfterTargetsPixels, float fDrawDistanceBeforeTargetsPixels,
|
||||
float fDrawDistanceBeforeTargetsPixels2, float fFadeInPercentOfDrawFar );
|
||||
|
||||
bool DrawHoldHeadForTapsOnSameRow() const;
|
||||
|
||||
bool DrawRollHeadForTapsOnSameRow() const;
|
||||
|
||||
private:
|
||||
void SetActiveFrame( float fNoteBeat, Actor &actorToSet, float fAnimationLength, bool bVivid );
|
||||
|
||||
+104
-54
@@ -20,6 +20,9 @@
|
||||
#include "Course.h"
|
||||
#include "NoteData.h"
|
||||
|
||||
float FindFirstDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceAfterTargetsPixels );
|
||||
float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceBeforeTargetsPixels );
|
||||
|
||||
static ThemeMetric<bool> SHOW_BOARD( "NoteField", "ShowBoard" );
|
||||
static ThemeMetric<bool> SHOW_BEAT_BARS( "NoteField", "ShowBeatBars" );
|
||||
static ThemeMetric<float> FADE_BEFORE_TARGETS_PERCENT( "NoteField", "FadeBeforeTargetsPercent" );
|
||||
@@ -208,10 +211,10 @@ void NoteField::Load(
|
||||
|
||||
//int i1 = m_pNoteData->GetNumTracks();
|
||||
//int i2 = GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer;
|
||||
|
||||
ASSERT_M( m_pNoteData->GetNumTracks() == GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer,
|
||||
ssprintf("NumTracks %d = ColsPerPlayer %d",m_pNoteData->GetNumTracks(), GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer) );
|
||||
|
||||
ASSERT_M(m_pNoteData->GetNumTracks() == GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer,
|
||||
ssprintf("NumTracks %d = ColsPerPlayer %d",m_pNoteData->GetNumTracks(),
|
||||
GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer));
|
||||
|
||||
// The NoteSkin may have changed at the beginning of a new course song.
|
||||
RString sNoteSkinLower = m_pPlayerState->m_PlayerOptions.GetCurrent().m_sNoteSkin;
|
||||
|
||||
@@ -303,7 +306,7 @@ void NoteField::Update( float fDeltaTime )
|
||||
// TODO: Remove use of PlayerNumber.
|
||||
|
||||
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
||||
if( pn == GAMESTATE->m_MasterPlayerNumber )
|
||||
if( pn == GAMESTATE->GetMasterPlayerNumber() )
|
||||
NoteDisplay::Update( fDeltaTime );
|
||||
}
|
||||
|
||||
@@ -574,7 +577,7 @@ void NoteField::DrawTickcountText( const float fBeat, int iTicks )
|
||||
m_textMeasureNumber.Draw();
|
||||
}
|
||||
|
||||
void NoteField::DrawComboText( const float fBeat, int iCombo )
|
||||
void NoteField::DrawComboText( const float fBeat, int iCombo, int iMiss )
|
||||
{
|
||||
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
|
||||
const float fYPos = ArrowEffects::GetYPos( m_pPlayerState, 0, fYOffset, m_fYReverseOffsetPixels );
|
||||
@@ -586,7 +589,7 @@ void NoteField::DrawComboText( const float fBeat, int iCombo )
|
||||
m_textMeasureNumber.SetHorizAlign( COMBO_IS_LEFT_SIDE ? align_right : align_left );
|
||||
m_textMeasureNumber.SetDiffuse( COMBO_COLOR );
|
||||
m_textMeasureNumber.SetGlow( RageColor(1,1,1,RageFastCos(RageTimer::GetTimeSinceStartFast()*2)/2+0.5f) );
|
||||
m_textMeasureNumber.SetText( ssprintf("%d", iCombo) );
|
||||
m_textMeasureNumber.SetText( ssprintf("%d/%d", iCombo, iMiss) );
|
||||
m_textMeasureNumber.SetXY( (COMBO_IS_LEFT_SIDE ? -xBase - xOffset : xBase + xOffset), fYPos );
|
||||
m_textMeasureNumber.Draw();
|
||||
}
|
||||
@@ -814,7 +817,7 @@ void NoteField::DrawPrimitives()
|
||||
|
||||
float fDrawScale = 1;
|
||||
fDrawScale *= 1 + 0.5f * fabsf( current_po.m_fPerspectiveTilt );
|
||||
fDrawScale *= 1 + fabsf( current_po.m_fEffects[PlayerOptions::EFFECT_TINY] );
|
||||
fDrawScale *= 1 + fabsf( current_po.m_fEffects[PlayerOptions::EFFECT_MINI] );
|
||||
|
||||
iDrawDistanceAfterTargetsPixels = (int)(iDrawDistanceAfterTargetsPixels * fDrawScale);
|
||||
iDrawDistanceBeforeTargetsPixels = (int)(iDrawDistanceBeforeTargetsPixels * fDrawScale);
|
||||
@@ -846,27 +849,26 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
const TimingData *pTiming = GetDisplayedTiming(m_pPlayerState);
|
||||
|
||||
const vector<TimingSegment *> *segs = pTiming->allTimingSegments;
|
||||
unsigned i = 0;
|
||||
// Draw beat bars
|
||||
if( ( GAMESTATE->IsEditing() || SHOW_BEAT_BARS ) && pTiming != NULL )
|
||||
{
|
||||
const TimingData &timing = *pTiming;
|
||||
const vector<TimeSignatureSegment> &vTimeSignatureSegments = timing.m_vTimeSignatureSegments;
|
||||
const vector<TimingSegment *> &tSigs = segs[SEGMENT_TIME_SIG];
|
||||
int iMeasureIndex = 0;
|
||||
FOREACH_CONST( TimeSignatureSegment, vTimeSignatureSegments, iter )
|
||||
for (i = 0; i < tSigs.size(); i++)
|
||||
{
|
||||
vector<TimeSignatureSegment>::const_iterator next = iter;
|
||||
next++;
|
||||
int iSegmentEndRow = (next == vTimeSignatureSegments.end()) ? iLastRowToDraw : next->GetRow();
|
||||
|
||||
TimeSignatureSegment *ts = static_cast<TimeSignatureSegment *>(tSigs[i]);
|
||||
int iSegmentEndRow = (i + 1 == tSigs.size()) ? iLastRowToDraw : tSigs[i+1]->GetRow();
|
||||
|
||||
// beat bars every 16th note
|
||||
int iDrawBeatBarsEveryRows = BeatToNoteRow( ((float)iter->GetDen()) / 4 ) / 4;
|
||||
int iDrawBeatBarsEveryRows = BeatToNoteRow( ((float)ts->GetDen()) / 4 ) / 4;
|
||||
|
||||
// In 4/4, every 16th beat bar is a measure
|
||||
int iMeasureBarFrequency = iter->GetNum() * 4;
|
||||
int iMeasureBarFrequency = ts->GetNum() * 4;
|
||||
int iBeatBarsDrawn = 0;
|
||||
|
||||
for( int i=iter->GetRow(); i < iSegmentEndRow; i += iDrawBeatBarsEveryRows )
|
||||
for( int j=ts->GetRow(); j < iSegmentEndRow; j += iDrawBeatBarsEveryRows )
|
||||
{
|
||||
bool bMeasureBar = iBeatBarsDrawn % iMeasureBarFrequency == 0;
|
||||
BeatBarType type = quarter_beat;
|
||||
@@ -876,7 +878,7 @@ void NoteField::DrawPrimitives()
|
||||
type = beat;
|
||||
else if( iBeatBarsDrawn % 2 == 0 )
|
||||
type = half_beat;
|
||||
float fBeat = NoteRowToBeat(i);
|
||||
float fBeat = NoteRowToBeat(j);
|
||||
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
{
|
||||
@@ -899,8 +901,9 @@ void NoteField::DrawPrimitives()
|
||||
// Scroll text
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
FOREACH_CONST( ScrollSegment, timing.m_ScrollSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_SCROLL].size(); i++)
|
||||
{
|
||||
ScrollSegment *seg = static_cast<ScrollSegment *>(segs[SEGMENT_SCROLL][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -911,8 +914,9 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
// BPM text
|
||||
FOREACH_CONST( BPMSegment, timing.m_BPMSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_BPM].size(); i++)
|
||||
{
|
||||
BPMSegment *seg = static_cast<BPMSegment *>(segs[SEGMENT_BPM][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -922,8 +926,9 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
// Freeze text
|
||||
FOREACH_CONST( StopSegment, timing.m_StopSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_STOP_DELAY].size(); i++)
|
||||
{
|
||||
StopSegment *seg = static_cast<StopSegment *>(segs[SEGMENT_STOP_DELAY][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -933,8 +938,9 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
|
||||
// Warp text
|
||||
FOREACH_CONST( WarpSegment, timing.m_WarpSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_WARP].size(); i++)
|
||||
{
|
||||
WarpSegment *seg = static_cast<WarpSegment *>(segs[SEGMENT_WARP][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -945,8 +951,9 @@ void NoteField::DrawPrimitives()
|
||||
|
||||
|
||||
// Time Signature text
|
||||
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_TIME_SIG].size(); i++)
|
||||
{
|
||||
TimeSignatureSegment *seg = static_cast<TimeSignatureSegment *>(segs[SEGMENT_TIME_SIG][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -958,8 +965,9 @@ void NoteField::DrawPrimitives()
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
// Tickcount text
|
||||
FOREACH_CONST( TickcountSegment, timing.m_TickcountSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_TICKCOUNT].size(); i++)
|
||||
{
|
||||
TickcountSegment *seg = static_cast<TickcountSegment *>(segs[SEGMENT_TICKCOUNT][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -972,20 +980,22 @@ void NoteField::DrawPrimitives()
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
// Combo text
|
||||
FOREACH_CONST( ComboSegment, timing.m_ComboSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_COMBO].size(); i++)
|
||||
{
|
||||
ComboSegment *seg = static_cast<ComboSegment *>(segs[SEGMENT_COMBO][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
if( IS_ON_SCREEN(fBeat) )
|
||||
DrawComboText( fBeat, seg->GetCombo() );
|
||||
DrawComboText( fBeat, seg->GetCombo(), seg->GetMissCombo() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Label text
|
||||
FOREACH_CONST( LabelSegment, timing.m_LabelSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_LABEL].size(); i++)
|
||||
{
|
||||
LabelSegment *seg = static_cast<LabelSegment *>(segs[SEGMENT_LABEL][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -996,8 +1006,10 @@ void NoteField::DrawPrimitives()
|
||||
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
FOREACH_CONST( SpeedSegment, timing.m_SpeedSegments, seg )
|
||||
// Speed text
|
||||
for (i = 0; i < segs[SEGMENT_SPEED].size(); i++)
|
||||
{
|
||||
SpeedSegment *seg = static_cast<SpeedSegment *>(segs[SEGMENT_SPEED][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -1008,11 +1020,12 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
}
|
||||
|
||||
// Speed text
|
||||
// Fake text
|
||||
if( GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
FOREACH_CONST( FakeSegment, timing.m_FakeSegments, seg )
|
||||
for (i = 0; i < segs[SEGMENT_FAKE].size(); i++)
|
||||
{
|
||||
FakeSegment *seg = static_cast<FakeSegment *>(segs[SEGMENT_FAKE][i]);
|
||||
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
|
||||
{
|
||||
float fBeat = seg->GetBeat();
|
||||
@@ -1042,6 +1055,22 @@ void NoteField::DrawPrimitives()
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ?
|
||||
GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks :
|
||||
GAMESTATE->m_pCurSong->m_Attacks;
|
||||
FOREACH_CONST(Attack, attacks, a)
|
||||
{
|
||||
float fBeat = timing.GetBeatFromElapsedTime(a->fStartSecond);
|
||||
if (BeatToNoteRow(fBeat) >= iFirstRowToDraw &&
|
||||
BeatToNoteRow(fBeat) <= iLastRowToDraw &&
|
||||
IS_ON_SCREEN(fBeat))
|
||||
{
|
||||
this->DrawAttackText(fBeat, *a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !GAMESTATE->m_bIsUsingStepTiming )
|
||||
{
|
||||
@@ -1055,55 +1084,55 @@ void NoteField::DrawPrimitives()
|
||||
case EditMode_Full:
|
||||
{
|
||||
vector<BackgroundChange>::iterator iter[NUM_BackgroundLayer];
|
||||
FOREACH_BackgroundLayer( i )
|
||||
iter[i] = GAMESTATE->m_pCurSong->GetBackgroundChanges(i).begin();
|
||||
FOREACH_BackgroundLayer( j )
|
||||
iter[j] = GAMESTATE->m_pCurSong->GetBackgroundChanges(j).begin();
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
float fLowestBeat = FLT_MAX;
|
||||
vector<BackgroundLayer> viLowestIndex;
|
||||
|
||||
FOREACH_BackgroundLayer( i )
|
||||
FOREACH_BackgroundLayer( j )
|
||||
{
|
||||
if( iter[i] == GAMESTATE->m_pCurSong->GetBackgroundChanges(i).end() )
|
||||
if( iter[j] == GAMESTATE->m_pCurSong->GetBackgroundChanges(j).end() )
|
||||
continue;
|
||||
|
||||
float fBeat = iter[i]->m_fStartBeat;
|
||||
float fBeat = iter[j]->m_fStartBeat;
|
||||
if( fBeat < fLowestBeat )
|
||||
{
|
||||
fLowestBeat = fBeat;
|
||||
viLowestIndex.clear();
|
||||
viLowestIndex.push_back( i );
|
||||
viLowestIndex.push_back( j );
|
||||
}
|
||||
else if( fBeat == fLowestBeat )
|
||||
{
|
||||
viLowestIndex.push_back( i );
|
||||
viLowestIndex.push_back( j );
|
||||
}
|
||||
}
|
||||
|
||||
if( viLowestIndex.empty() )
|
||||
{
|
||||
FOREACH_BackgroundLayer( i )
|
||||
ASSERT( iter[i] == GAMESTATE->m_pCurSong->GetBackgroundChanges(i).end() );
|
||||
FOREACH_BackgroundLayer( j )
|
||||
ASSERT( iter[j] == GAMESTATE->m_pCurSong->GetBackgroundChanges(j).end() );
|
||||
break;
|
||||
}
|
||||
|
||||
if( IS_ON_SCREEN(fLowestBeat) )
|
||||
{
|
||||
vector<RString> vsBGChanges;
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, i )
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, bl )
|
||||
{
|
||||
ASSERT( iter[*i] != GAMESTATE->m_pCurSong->GetBackgroundChanges(*i).end() );
|
||||
const BackgroundChange& change = *iter[*i];
|
||||
ASSERT( iter[*bl] != GAMESTATE->m_pCurSong->GetBackgroundChanges(*bl).end() );
|
||||
const BackgroundChange& change = *iter[*bl];
|
||||
RString s = change.GetTextDescription();
|
||||
if( *i!=0 )
|
||||
s = ssprintf("%d: ",*i) + s;
|
||||
if( *bl!=0 )
|
||||
s = ssprintf("%d: ",*bl) + s;
|
||||
vsBGChanges.push_back( s );
|
||||
}
|
||||
DrawBGChangeText( fLowestBeat, join("\n",vsBGChanges) );
|
||||
}
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, i )
|
||||
iter[*i]++;
|
||||
FOREACH_CONST( BackgroundLayer, viLowestIndex, bl )
|
||||
iter[*bl]++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1143,11 +1172,13 @@ void NoteField::DrawPrimitives()
|
||||
float fSelectedRangeGlow = SCALE( RageFastCos(RageTimer::GetTimeSinceStartFast()*2), -1, 1, 0.1f, 0.3f );
|
||||
|
||||
const Style* pStyle = GAMESTATE->GetCurrentStyle();
|
||||
ASSERT( GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer == m_pNoteData->GetNumTracks() );
|
||||
ASSERT_M(m_pNoteData->GetNumTracks() == GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer,
|
||||
ssprintf("NumTracks %d = ColsPerPlayer %d",m_pNoteData->GetNumTracks(),
|
||||
GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer));
|
||||
|
||||
for( int i=0; i<m_pNoteData->GetNumTracks(); i++ ) // for each arrow column
|
||||
for( int j=0; j<m_pNoteData->GetNumTracks(); j++ ) // for each arrow column
|
||||
{
|
||||
const int c = pStyle->m_iColumnDrawOrder[i];
|
||||
const int c = pStyle->m_iColumnDrawOrder[j];
|
||||
|
||||
bool bAnyUpcomingInThisCol = false;
|
||||
|
||||
@@ -1158,7 +1189,7 @@ void NoteField::DrawPrimitives()
|
||||
|
||||
for( ; begin != end; ++begin )
|
||||
{
|
||||
const TapNote &tn = begin->second; //m_pNoteData->GetTapNote(c, i);
|
||||
const TapNote &tn = begin->second; //m_pNoteData->GetTapNote(c, j);
|
||||
if( tn.type != TapNote::hold_head )
|
||||
continue; // skip
|
||||
|
||||
@@ -1257,13 +1288,31 @@ void NoteField::DrawPrimitives()
|
||||
{
|
||||
for( int c2=0; c2<m_pNoteData->GetNumTracks(); c2++ )
|
||||
{
|
||||
if( m_pNoteData->GetTapNote(c2, q).type == TapNote::hold_head)
|
||||
const TapNote &tmp = m_pNoteData->GetTapNote(c2, q);
|
||||
if(tmp.type == TapNote::hold_head &&
|
||||
tmp.subType == TapNote::hold_head_hold)
|
||||
{
|
||||
bHoldNoteBeginsOnThisBeat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do the same for a roll.
|
||||
bool bRollNoteBeginsOnThisBeat = false;
|
||||
if (m_pCurDisplay->display[c].DrawRollHeadForTapsOnSameRow() )
|
||||
{
|
||||
for( int c2=0; c2<m_pNoteData->GetNumTracks(); c2++ )
|
||||
{
|
||||
const TapNote &tmp = m_pNoteData->GetTapNote(c2, q);
|
||||
if(tmp.type == TapNote::hold_head &&
|
||||
tmp.subType == TapNote::hold_head_roll)
|
||||
{
|
||||
bRollNoteBeginsOnThisBeat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bIsInSelectionRange = false;
|
||||
if( m_iBeginMarker!=-1 && m_iEndMarker!=-1 )
|
||||
@@ -1273,7 +1322,8 @@ 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, NoteRowToVisibleBeat(m_pPlayerState, q), bHoldNoteBeginsOnThisBeat,
|
||||
displayCols->display[c].DrawTap(tn, c, NoteRowToVisibleBeat(m_pPlayerState, q),
|
||||
bHoldNoteBeginsOnThisBeat, bRollNoteBeginsOnThisBeat,
|
||||
bUseAdditionColoring, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail,
|
||||
m_fYReverseOffsetPixels, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels,
|
||||
FADE_BEFORE_TARGETS_PERCENT );
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ protected:
|
||||
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 DrawComboText( const float fBeat, int iCombo, int iMiss );
|
||||
void DrawLabelText( const float fBeat, RString sLabel );
|
||||
void DrawSpeedText( const float fBeat, float fPercent, float fWait, unsigned short usMode );
|
||||
void DrawScrollText( const float fBeat, float fPercent );
|
||||
|
||||
@@ -200,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( sSkinName.EqualsNoCase(asSkinNames[i]) )
|
||||
if( 0==stricmp(sSkinName, asSkinNames[i]) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
+18
-2
@@ -164,7 +164,7 @@ struct TapNote
|
||||
{
|
||||
if (type_ > TapNote::fake )
|
||||
{
|
||||
LOG->Trace(ssprintf("Invalid tap note type %d (most likely) due to random vanish issues. Assume it doesn't need judging.", type_ ) );
|
||||
LOG->Trace("Invalid tap note type %d (most likely) due to random vanish issues. Assume it doesn't need judging.", (int)type_ );
|
||||
type = TapNote::empty;
|
||||
}
|
||||
}
|
||||
@@ -334,7 +334,23 @@ static inline float ToBeat(int row) { return NoteRowToBeat(row); }
|
||||
*/
|
||||
static inline float ToBeat(float beat) { return beat; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Scales the position.
|
||||
* @param T start - the starting row of the scaling region
|
||||
* @param T length - the length of the scaling region
|
||||
* @param T newLength - the new length of the scaling region
|
||||
* @param T position - the position to scale
|
||||
* @return T the scaled position
|
||||
*/
|
||||
template<typename T>
|
||||
inline T ScalePosition( T start, T length, T newLength, T position )
|
||||
{
|
||||
if( position < start )
|
||||
return position;
|
||||
if( position >= start + length )
|
||||
return position - length + newLength;
|
||||
return start + (position - start) * newLength / length;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+10
-7
@@ -32,20 +32,23 @@ bool NotesLoader::LoadFromDir( const RString &sPath, Song &out, set<RString> &Bl
|
||||
vector<RString> list;
|
||||
|
||||
BlacklistedImages.clear();
|
||||
SSCLoader::GetApplicableFiles( sPath, list );
|
||||
SSCLoader loaderSSC;
|
||||
loaderSSC.GetApplicableFiles( sPath, list );
|
||||
if( !list.empty() )
|
||||
{
|
||||
if( !SSCLoader::LoadFromDir( sPath, out ) )
|
||||
if( !loaderSSC.LoadFromDir( sPath, out ) )
|
||||
return false;
|
||||
SSCLoader::TidyUpData( out, false );
|
||||
loaderSSC.TidyUpData( out, false );
|
||||
return true;
|
||||
}
|
||||
SMALoader::GetApplicableFiles( sPath, list );
|
||||
SMALoader loaderSMA;
|
||||
loaderSMA.GetApplicableFiles( sPath, list );
|
||||
if (!list.empty() )
|
||||
return SMALoader::LoadFromDir( sPath, out );
|
||||
SMLoader::GetApplicableFiles( sPath, list );
|
||||
return loaderSMA.LoadFromDir( sPath, out );
|
||||
SMLoader loaderSM;
|
||||
loaderSM.GetApplicableFiles( sPath, list );
|
||||
if (!list.empty() )
|
||||
return SMLoader::LoadFromDir( sPath, out );
|
||||
return loaderSM.LoadFromDir( sPath, out );
|
||||
DWILoader::GetApplicableFiles( sPath, list );
|
||||
if( !list.empty() )
|
||||
return DWILoader::LoadFromDir( sPath, out, BlacklistedImages );
|
||||
|
||||
+102
-13
@@ -459,13 +459,16 @@ static bool SearchForKeysound( const RString &sPath, RString nDataOriginal, map<
|
||||
* 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) )
|
||||
RString dir = out.GetSongDir();
|
||||
if (dir.empty())
|
||||
dir = Dirname(sPath);
|
||||
if( !IsAFile(dir+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) )
|
||||
if( IsAFile(dir+fn) )
|
||||
{
|
||||
nData = fn;
|
||||
break;
|
||||
@@ -473,9 +476,9 @@ static bool SearchForKeysound( const RString &sPath, RString nDataOriginal, map<
|
||||
}
|
||||
}
|
||||
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
if( !IsAFile(dir+nData) )
|
||||
{
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "references key \"%s\" that can't be found", nData.c_str() );
|
||||
LOG->UserLog( "Song file", dir, "references key \"%s\" that can't be found", nData.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -548,6 +551,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
{
|
||||
|
||||
map<RString, int> mapIdToKeysoundIndex;
|
||||
map<int, float> mapNoteRowToBPM;
|
||||
MeasureToTimeSig_t sigAdjustments;
|
||||
|
||||
LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() );
|
||||
@@ -572,8 +576,8 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( 0, fBPM );
|
||||
out.m_Timing.AddBPMSegment( newSeg );
|
||||
BPMSegment * newSeg = new BPMSegment( 0, fBPM );
|
||||
out.m_Timing.AddSegment(SEGMENT_BPM, newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
else
|
||||
@@ -622,7 +626,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
case BMS_TRACK_BPM:
|
||||
if( iVal > 0 )
|
||||
{
|
||||
out.m_Timing.SetBPMAtBeat( fBeat, (float) iVal );
|
||||
mapNoteRowToBPM[ BeatToNoteRow(fBeat) ] = iVal;
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %i", fBeat, iVal );
|
||||
}
|
||||
else
|
||||
@@ -639,7 +643,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBPM ) )
|
||||
{
|
||||
float fBPM = StringToFloat( sBPM );
|
||||
out.m_Timing.SetBPMAtBeat( fBeat, fBPM );
|
||||
mapNoteRowToBPM[ BeatToNoteRow(fBeat) ] = fBPM;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -662,9 +666,9 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
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.GetPause() );
|
||||
StopSegment * newSeg = new StopSegment( fBeat, fFreezeSecs );
|
||||
out.m_Timing.AddSegment( SEGMENT_STOP_DELAY, newSeg );
|
||||
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg->GetPause() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -676,6 +680,11 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for( map<int, float>::iterator it = mapNoteRowToBPM.begin(); it != mapNoteRowToBPM.end(); it ++ )
|
||||
{
|
||||
out.m_Timing.SetBPMAtRow( it->first, it->second );
|
||||
}
|
||||
|
||||
// Now that we're done reading BPMs, factor out weird time signatures.
|
||||
SetTimeSigAdjustments( mapMeasureToTimeSig, outSong, sigAdjustments );
|
||||
@@ -690,6 +699,8 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
}
|
||||
|
||||
NameToData_t::const_iterator it;
|
||||
|
||||
bool hasBGM = false;
|
||||
for( it = mapNameToData.lower_bound("#00000"); it != mapNameToData.end(); ++it )
|
||||
{
|
||||
const RString &sName = it->first;
|
||||
@@ -739,6 +750,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
{
|
||||
if( bmsTrack == BMS_AUTO_KEYSOUND_1 )
|
||||
{
|
||||
hasBGM = true;
|
||||
// shift the auto keysound as far right as possible
|
||||
int iLastEmptyTrack = -1;
|
||||
if( ndNotes.GetTapLastEmptyTrack(row, iLastEmptyTrack) &&
|
||||
@@ -766,6 +778,12 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasBGM)
|
||||
{
|
||||
LOG->Warn("The song at %s is missing a #XXX01 tag! We're unable to load.", sPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Handles hold notes like uBMPlay.
|
||||
* Different BMS simulators support hold notes differently.
|
||||
* See http://nvyu.net/rdm/ex.php for more info.
|
||||
@@ -958,7 +976,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
iTransformNewToOld[15] = BMS_P2_TURN;
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
ASSERT_M(0, ssprintf("Invalid StepsType when parsing BMS file %s!", sPath.c_str()));
|
||||
}
|
||||
|
||||
// shift all of the autokeysound tracks onto the main tracks
|
||||
@@ -984,7 +1002,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has too much simultenous autokeysound tracks." );
|
||||
LOG->UserLog( "Song file", sPath, "has too much simultaneous autokeysound tracks." );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1070,75 @@ void BMSLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.bml"), out );
|
||||
}
|
||||
|
||||
bool BMSLoader::LoadNoteDataFromSimfile( const RString & cachePath, Steps & out )
|
||||
{
|
||||
Song dummy;
|
||||
// TODO: Simplify this copy/paste from LoadFromDir.
|
||||
|
||||
vector<NameToData_t> BMSData;
|
||||
BMSData.push_back(NameToData_t());
|
||||
ReadBMSFile(cachePath, BMSData.back());
|
||||
|
||||
RString commonSubstring;
|
||||
GetCommonTagFromMapList( BMSData, "#title", commonSubstring );
|
||||
|
||||
Steps *copy = dummy.CreateSteps();
|
||||
|
||||
copy->SetDifficulty( Difficulty_Medium );
|
||||
RString sTag;
|
||||
if( GetTagFromMap( BMSData[0], "#title", sTag ) && sTag.size() != commonSubstring.size() )
|
||||
{
|
||||
sTag = sTag.substr( commonSubstring.size(), sTag.size() - commonSubstring.size() );
|
||||
sTag.MakeLower();
|
||||
|
||||
if( sTag.find('l') != sTag.npos )
|
||||
{
|
||||
unsigned lPos = sTag.find('l');
|
||||
if( lPos > 2 && sTag.substr(lPos-2,4) == "solo" )
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Edit );
|
||||
}
|
||||
else
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Easy );
|
||||
}
|
||||
}
|
||||
else if( sTag.find('a') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Hard );
|
||||
else if( sTag.find('b') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Beginner );
|
||||
}
|
||||
if( commonSubstring == "" )
|
||||
{
|
||||
copy->SetDifficulty(Difficulty_Medium);
|
||||
RString sTag;
|
||||
if (GetTagFromMap(BMSData[0], "#title#", sTag))
|
||||
SearchForDifficulty(sTag, copy);
|
||||
}
|
||||
ReadGlobalTags( BMSData[0], dummy );
|
||||
if( commonSubstring.size() > 2 && commonSubstring[commonSubstring.size() - 2] == ' ' )
|
||||
{
|
||||
switch( commonSubstring[commonSubstring.size() - 1] )
|
||||
{
|
||||
case '[':
|
||||
case '(':
|
||||
case '<':
|
||||
commonSubstring = commonSubstring.substr(0, commonSubstring.size() - 2);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
map<RString, int> mapFilenameToKeysoundIndex;
|
||||
|
||||
|
||||
const bool ok = LoadFromBMSFile( cachePath, BMSData[0], *copy, dummy, mapFilenameToKeysoundIndex );
|
||||
if( ok )
|
||||
{
|
||||
out.SetNoteData(copy->GetNoteData());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromBMSDir(%s)", sDir.c_str() );
|
||||
@@ -1156,6 +1243,7 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
iMainDataIndex = i;
|
||||
|
||||
ReadGlobalTags( aBMSData[iMainDataIndex], out );
|
||||
out.m_sSongFileName = out.GetSongDir() + arrayBMSFileNames[iMainDataIndex];
|
||||
|
||||
// The brackets before the difficulty are in common substring, so remove them if it's found.
|
||||
if( commonSubstring.size() > 2 && commonSubstring[commonSubstring.size() - 2] == ' ' )
|
||||
@@ -1188,6 +1276,7 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
if( i == static_cast<unsigned>(iMainDataIndex) )
|
||||
out.m_SongTiming = pNewNotes->m_Timing;
|
||||
|
||||
pNewNotes->SetFilename(out.GetSongDir() + arrayBMSFileNames[i]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#define NOTES_LOADER_BMS_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
/** @brief Reads a Song from a set of .BMS files. */
|
||||
namespace BMSLoader
|
||||
{
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadFromDir( const RString &sDir, Song &out );
|
||||
bool LoadNoteDataFromSimfile( const RString & cachePath, Steps &out );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+292
-213
@@ -14,6 +14,8 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
Difficulty DwiCompatibleStringToDifficulty( const RString& sDC );
|
||||
|
||||
static std::map<int,int> g_mapDanceNoteToNoteDataColumn;
|
||||
|
||||
/** @brief The different types of core DWI arrows and pads. */
|
||||
@@ -168,6 +170,238 @@ Difficulty DwiCompatibleStringToDifficulty( const RString& sDC )
|
||||
else return Difficulty_Invalid;
|
||||
}
|
||||
|
||||
static StepsType GetTypeFromMode(const RString &mode)
|
||||
{
|
||||
if( mode == "SINGLE" )
|
||||
return StepsType_dance_single;
|
||||
else if( mode == "DOUBLE" )
|
||||
return StepsType_dance_double;
|
||||
else if( mode == "COUPLE" )
|
||||
return StepsType_dance_couple;
|
||||
else if( mode == "SOLO" )
|
||||
return StepsType_dance_solo;
|
||||
ASSERT_M(0, "Unrecognized DWI notes format " + mode + "!");
|
||||
return StepsType_Invalid; // just in case.
|
||||
}
|
||||
|
||||
static NoteData ParseNoteData(RString &step1, RString &step2,
|
||||
Steps &out, const RString &path)
|
||||
{
|
||||
g_mapDanceNoteToNoteDataColumn.clear();
|
||||
switch( out.m_StepsType )
|
||||
{
|
||||
case StepsType_dance_single:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
break;
|
||||
case StepsType_dance_double:
|
||||
case StepsType_dance_couple:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_LEFT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_DOWN] = 5;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_UP] = 6;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_RIGHT] = 7;
|
||||
break;
|
||||
case StepsType_dance_solo:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPLEFT] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPRIGHT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 5;
|
||||
break;
|
||||
DEFAULT_FAIL( out.m_StepsType );
|
||||
}
|
||||
|
||||
NoteData newNoteData;
|
||||
newNoteData.SetNumTracks( g_mapDanceNoteToNoteDataColumn.size() );
|
||||
|
||||
for( int pad=0; pad<2; pad++ ) // foreach pad
|
||||
{
|
||||
RString sStepData;
|
||||
switch( pad )
|
||||
{
|
||||
case 0:
|
||||
sStepData = step1;
|
||||
break;
|
||||
case 1:
|
||||
if( step2 == "" ) // no data
|
||||
continue; // skip
|
||||
sStepData = step2;
|
||||
break;
|
||||
DEFAULT_FAIL( pad );
|
||||
}
|
||||
|
||||
sStepData.Replace("\n", "");
|
||||
sStepData.Replace("\r", "");
|
||||
sStepData.Replace("\t", "");
|
||||
sStepData.Replace(" ", "");
|
||||
|
||||
double fCurrentBeat = 0;
|
||||
double fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
|
||||
for( size_t i=0; i<sStepData.size(); )
|
||||
{
|
||||
char c = sStepData[i++];
|
||||
switch( c )
|
||||
{
|
||||
// begins a series
|
||||
case '(':
|
||||
fCurrentIncrementer = 1.0/16 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '[':
|
||||
fCurrentIncrementer = 1.0/24 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '{':
|
||||
fCurrentIncrementer = 1.0/64 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '`':
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
// ends a series
|
||||
case ')':
|
||||
case ']':
|
||||
case '}':
|
||||
case '\'':
|
||||
case '>':
|
||||
fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
default: // this is a note character
|
||||
{
|
||||
if( c == '!' )
|
||||
{
|
||||
LOG->UserLog(
|
||||
"Song file",
|
||||
path,
|
||||
"has an unexpected character: '!'." );
|
||||
continue;
|
||||
}
|
||||
|
||||
bool jump = false;
|
||||
if( c == '<' )
|
||||
{
|
||||
/* Arr. Is this a jump or a 1/192 marker? */
|
||||
if( Is192( sStepData, i ) )
|
||||
{
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* It's a jump.
|
||||
* We need to keep reading notes until we hit a >. */
|
||||
jump = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
const int iIndex = BeatToNoteRow( (float)fCurrentBeat );
|
||||
i--;
|
||||
do {
|
||||
c = sStepData[i++];
|
||||
|
||||
if( jump && c == '>' )
|
||||
break;
|
||||
|
||||
int iCol1, iCol2;
|
||||
DWIcharToNoteCol(
|
||||
c,
|
||||
(GameController)pad,
|
||||
iCol1,
|
||||
iCol2,
|
||||
path );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_TAP);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_TAP);
|
||||
|
||||
if(i>=sStepData.length())
|
||||
{
|
||||
break;
|
||||
//we ran out of data
|
||||
//while looking for the ending > mark
|
||||
}
|
||||
|
||||
if( sStepData[i] == '!' )
|
||||
{
|
||||
i++;
|
||||
const char holdChar = sStepData[i++];
|
||||
|
||||
DWIcharToNoteCol(holdChar,
|
||||
(GameController)pad,
|
||||
iCol1,
|
||||
iCol2,
|
||||
path );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_HOLD_HEAD);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_HOLD_HEAD);
|
||||
}
|
||||
}
|
||||
while( jump );
|
||||
fCurrentBeat += fCurrentIncrementer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in iDuration. */
|
||||
for( int t=0; t<newNoteData.GetNumTracks(); ++t )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK( newNoteData, t, iHeadRow )
|
||||
{
|
||||
TapNote tn = newNoteData.GetTapNote( t, iHeadRow );
|
||||
if( tn.type != TapNote::hold_head )
|
||||
continue;
|
||||
|
||||
int iTailRow = iHeadRow;
|
||||
bool bFound = false;
|
||||
while( !bFound && newNoteData.GetNextTapNoteRowForTrack(t, iTailRow) )
|
||||
{
|
||||
const TapNote &TailTap = newNoteData.GetTapNote( t, iTailRow );
|
||||
if( TailTap.type == TapNote::empty )
|
||||
continue;
|
||||
|
||||
newNoteData.SetTapNote( t, iTailRow, TAP_EMPTY );
|
||||
tn.iDuration = iTailRow - iHeadRow;
|
||||
newNoteData.SetTapNote( t, iHeadRow, tn );
|
||||
bFound = true;
|
||||
}
|
||||
|
||||
if( !bFound )
|
||||
{
|
||||
/* The hold was never closed. */
|
||||
LOG->UserLog("Song file",
|
||||
path,
|
||||
"failed to close a hold note in \"%s\" on track %i",
|
||||
DifficultyToString(out.GetDifficulty()).c_str(),
|
||||
t);
|
||||
|
||||
newNoteData.SetTapNote( t, iHeadRow, TAP_EMPTY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT( newNoteData.GetNumTracks() > 0 );
|
||||
return newNoteData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Look through the notes tag to extract the data.
|
||||
* @param sMode the steps type.
|
||||
@@ -190,216 +424,17 @@ static bool LoadFromDWITokens(
|
||||
{
|
||||
CHECKPOINT_M( "DWILoader::LoadFromDWITokens()" );
|
||||
|
||||
out.m_StepsType = StepsType_Invalid;
|
||||
out.m_StepsType = GetTypeFromMode(sMode);
|
||||
|
||||
if( sMode == "SINGLE" ) out.m_StepsType = StepsType_dance_single;
|
||||
else if( sMode == "DOUBLE" ) out.m_StepsType = StepsType_dance_double;
|
||||
else if( sMode == "COUPLE" ) out.m_StepsType = StepsType_dance_couple;
|
||||
else if( sMode == "SOLO" ) out.m_StepsType = StepsType_dance_solo;
|
||||
else
|
||||
{
|
||||
ASSERT_M(0, "Unrecognized DWI notes format " + sMode + "!");
|
||||
out.m_StepsType = StepsType_dance_single;
|
||||
}
|
||||
out.SetMeter(StringToInt(sNumFeet));
|
||||
|
||||
|
||||
g_mapDanceNoteToNoteDataColumn.clear();
|
||||
switch( out.m_StepsType )
|
||||
{
|
||||
case StepsType_dance_single:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
break;
|
||||
case StepsType_dance_double:
|
||||
case StepsType_dance_couple:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_LEFT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_DOWN] = 5;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_UP] = 6;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_RIGHT] = 7;
|
||||
break;
|
||||
case StepsType_dance_solo:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPLEFT] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPRIGHT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 5;
|
||||
break;
|
||||
DEFAULT_FAIL( out.m_StepsType );
|
||||
}
|
||||
|
||||
int iNumFeet = StringToInt(sNumFeet);
|
||||
// out.SetDescription(sDescription); // Don't put garbage in the description.
|
||||
out.SetMeter(iNumFeet);
|
||||
out.SetDifficulty( DwiCompatibleStringToDifficulty(sDescription) );
|
||||
|
||||
NoteData newNoteData;
|
||||
newNoteData.SetNumTracks( g_mapDanceNoteToNoteDataColumn.size() );
|
||||
|
||||
for( int pad=0; pad<2; pad++ ) // foreach pad
|
||||
{
|
||||
RString sStepData;
|
||||
switch( pad )
|
||||
{
|
||||
case 0:
|
||||
sStepData = sStepData1;
|
||||
break;
|
||||
case 1:
|
||||
if( sStepData2 == "" ) // no data
|
||||
continue; // skip
|
||||
sStepData = sStepData2;
|
||||
break;
|
||||
DEFAULT_FAIL( pad );
|
||||
}
|
||||
|
||||
sStepData.Replace("\n", "");
|
||||
sStepData.Replace("\r", "");
|
||||
sStepData.Replace("\t", "");
|
||||
sStepData.Replace(" ", "");
|
||||
|
||||
double fCurrentBeat = 0;
|
||||
double fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
|
||||
for( size_t i=0; i<sStepData.size(); )
|
||||
{
|
||||
char c = sStepData[i++];
|
||||
switch( c )
|
||||
{
|
||||
// begins a series
|
||||
case '(':
|
||||
fCurrentIncrementer = 1.0/16 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '[':
|
||||
fCurrentIncrementer = 1.0/24 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '{':
|
||||
fCurrentIncrementer = 1.0/64 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '`':
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
// ends a series
|
||||
case ')':
|
||||
case ']':
|
||||
case '}':
|
||||
case '\'':
|
||||
case '>':
|
||||
fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
default: // this is a note character
|
||||
{
|
||||
if( c == '!' )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has an unexpected character: '!'." );
|
||||
continue;
|
||||
}
|
||||
|
||||
bool jump = false;
|
||||
if( c == '<' )
|
||||
{
|
||||
/* Arr. Is this a jump or a 1/192 marker? */
|
||||
if( Is192( sStepData, i ) )
|
||||
{
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* It's a jump. We need to keep reading notes until we hit a >. */
|
||||
jump = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
const int iIndex = BeatToNoteRow( (float)fCurrentBeat );
|
||||
i--;
|
||||
do {
|
||||
c = sStepData[i++];
|
||||
|
||||
if( jump && c == '>' )
|
||||
break;
|
||||
|
||||
int iCol1, iCol2;
|
||||
DWIcharToNoteCol( c, (GameController)pad, iCol1, iCol2, sPath );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1, iIndex, TAP_ORIGINAL_TAP);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2, iIndex, TAP_ORIGINAL_TAP);
|
||||
|
||||
if(i>=sStepData.length()) {
|
||||
break;//we ran out of data while looking for the ending > mark
|
||||
}
|
||||
|
||||
if( sStepData[i] == '!' )
|
||||
{
|
||||
i++;
|
||||
const char holdChar = sStepData[i++];
|
||||
|
||||
DWIcharToNoteCol( holdChar, (GameController)pad, iCol1, iCol2, sPath );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1, iIndex, TAP_ORIGINAL_HOLD_HEAD);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2, iIndex, TAP_ORIGINAL_HOLD_HEAD);
|
||||
}
|
||||
}
|
||||
while( jump );
|
||||
fCurrentBeat += fCurrentIncrementer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in iDuration. */
|
||||
for( int t=0; t<newNoteData.GetNumTracks(); ++t )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK( newNoteData, t, iHeadRow )
|
||||
{
|
||||
TapNote tn = newNoteData.GetTapNote( t, iHeadRow );
|
||||
if( tn.type != TapNote::hold_head )
|
||||
continue;
|
||||
|
||||
int iTailRow = iHeadRow;
|
||||
bool bFound = false;
|
||||
while( !bFound && newNoteData.GetNextTapNoteRowForTrack(t, iTailRow) )
|
||||
{
|
||||
const TapNote &TailTap = newNoteData.GetTapNote( t, iTailRow );
|
||||
if( TailTap.type == TapNote::empty )
|
||||
continue;
|
||||
|
||||
newNoteData.SetTapNote( t, iTailRow, TAP_EMPTY );
|
||||
tn.iDuration = iTailRow - iHeadRow;
|
||||
newNoteData.SetTapNote( t, iHeadRow, tn );
|
||||
bFound = true;
|
||||
}
|
||||
|
||||
if( !bFound )
|
||||
{
|
||||
/* The hold was never closed. */
|
||||
LOG->UserLog( "Song file", sPath, "failed to close a hold note in \"%s\" on track %i",
|
||||
sDescription.c_str(), t );
|
||||
|
||||
newNoteData.SetTapNote( t, iHeadRow, TAP_EMPTY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT( newNoteData.GetNumTracks() > 0 );
|
||||
|
||||
out.SetNoteData( newNoteData );
|
||||
|
||||
out.SetNoteData( ParseNoteData(sStepData1, sStepData2, out, sPath) );
|
||||
|
||||
out.TidyUpData();
|
||||
|
||||
out.SetSavedToDisk( true ); // we're loading from disk, so this is by definintion already saved
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -442,17 +477,55 @@ void DWILoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.dwi"), out );
|
||||
}
|
||||
|
||||
bool DWILoader::LoadNoteDataFromSimfile( const RString &path, Steps &out )
|
||||
{
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( path, false ) ) // don't unescape
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
path,
|
||||
"couldn't be opened: %s",
|
||||
msd.GetError().c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
int iNumParams = msd.GetNumParams(i);
|
||||
const MsdFile::value_t ¶ms = msd.GetValue(i);
|
||||
RString valueName = params[0];
|
||||
|
||||
if(valueName.EqualsNoCase("SINGLE") ||
|
||||
valueName.EqualsNoCase("DOUBLE") ||
|
||||
valueName.EqualsNoCase("COUPLE") ||
|
||||
valueName.EqualsNoCase("SOLO") )
|
||||
{
|
||||
if (out.m_StepsType != GetTypeFromMode(valueName))
|
||||
continue;
|
||||
if (out.GetDifficulty() != DwiCompatibleStringToDifficulty(params[1]))
|
||||
continue;
|
||||
if (out.GetMeter() != StringToInt(params[2]))
|
||||
continue;
|
||||
RString step1 = params[3];
|
||||
RString step2 = (iNumParams==5) ? params[4] : RString("");
|
||||
out.SetNoteData(ParseNoteData(step1, step2, out, path));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &BlacklistedImages )
|
||||
{
|
||||
vector<RString> aFileNames;
|
||||
GetApplicableFiles( sPath_, aFileNames );
|
||||
|
||||
|
||||
if( aFileNames.size() > 1 )
|
||||
{
|
||||
LOG->UserLog( "Song", sPath_, "has more than one DWI file. There should be only one!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* We should have exactly one; if we had none, we shouldn't have been called to begin with. */
|
||||
ASSERT( aFileNames.size() == 1 );
|
||||
const RString sPath = sPath_ + aFileNames[0];
|
||||
@@ -466,6 +539,8 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
return false;
|
||||
}
|
||||
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
int iNumParams = msd.GetNumParams(i);
|
||||
@@ -513,14 +588,16 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
|
||||
if( PREFSMAN->m_bQuirksMode )
|
||||
{
|
||||
out.m_SongTiming.AddBPMSegment( BPMSegment(0, fBPM) );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, fBPM) );
|
||||
}
|
||||
else{
|
||||
if( fBPM > 0.0f )
|
||||
out.m_SongTiming.AddBPMSegment( BPMSegment(0, fBPM) );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, fBPM) );
|
||||
else
|
||||
LOG->UserLog( "Song file", sPath, "has an invalid BPM change at beat %f, BPM %f.",
|
||||
NoteRowToBeat(0), fBPM );
|
||||
LOG->UserLog("Song file",
|
||||
sPath,
|
||||
"has an invalid BPM change at beat %f, BPM %f.",
|
||||
0.0f, fBPM );
|
||||
}
|
||||
}
|
||||
else if( sValueName.EqualsNoCase("DISPLAYBPM") )
|
||||
@@ -574,7 +651,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
int iFreezeRow = BeatToNoteRow( StringToFloat(arrayFreezeValues[0]) / 4.0f );
|
||||
float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] ) / 1000.0f;
|
||||
|
||||
out.m_SongTiming.AddStopSegment( StopSegment(iFreezeRow, fFreezeSeconds) );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_STOP_DELAY, new StopSegment(iFreezeRow, fFreezeSeconds) );
|
||||
// LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", fFreezeBeat, fFreezeSeconds );
|
||||
}
|
||||
}
|
||||
@@ -598,8 +675,8 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
float fBPM = StringToFloat( arrayBPMChangeValues[1] );
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment bs( iStartIndex, fBPM );
|
||||
out.m_SongTiming.AddBPMSegment( bs );
|
||||
BPMSegment * bs = new BPMSegment( iStartIndex, fBPM );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_BPM, bs );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -625,7 +702,10 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
sPath
|
||||
);
|
||||
if( pNewNotes->m_StepsType != StepsType_Invalid )
|
||||
{
|
||||
pNewNotes->SetFilename( sPath );
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else
|
||||
delete pNewNotes;
|
||||
}
|
||||
@@ -660,7 +740,6 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <set>
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
|
||||
/** @brief The DWILoader handles parsing the .dwi file. */
|
||||
namespace DWILoader
|
||||
@@ -24,6 +25,8 @@ namespace DWILoader
|
||||
* @return its success or failure.
|
||||
*/
|
||||
bool LoadFromDir( const RString &sPath, Song &out, set<RString> &BlacklistedImages );
|
||||
|
||||
bool LoadNoteDataFromSimfile( const RString &path, Steps &out );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+22
-13
@@ -15,22 +15,33 @@ void NotesLoaderJson::GetApplicableFiles( const RString &sPath, vector<RString>
|
||||
GetDirListing( sPath + RString("*.json"), out );
|
||||
}
|
||||
|
||||
void Deserialize(BPMSegment &seg, const Json::Value &root)
|
||||
static void Deserialize(TimingSegment &seg_, const Json::Value &root)
|
||||
{
|
||||
seg.SetBeat((float)(root["Beat"].asDouble()));
|
||||
seg.SetBPM((float)(root["BPM"].asDouble()));
|
||||
}
|
||||
|
||||
static void Deserialize(StopSegment &seg, const Json::Value &root)
|
||||
{
|
||||
seg.SetBeat((float)(root["Beat"].asDouble()));
|
||||
seg.SetPause((float)(root["Seconds"].asDouble()));
|
||||
TimingSegment *seg = &seg_;
|
||||
|
||||
float fBeat = root["Beat"].asDouble();
|
||||
seg->SetBeat(fBeat);
|
||||
switch (seg->GetType())
|
||||
{
|
||||
case SEGMENT_BPM:
|
||||
{
|
||||
float fBPM = root["BPM"].asDouble();
|
||||
static_cast<BPMSegment *>(seg)->SetBPM(fBPM);
|
||||
break;
|
||||
}
|
||||
case SEGMENT_STOP_DELAY:
|
||||
{
|
||||
float fStop = root["Seconds"].asDouble();
|
||||
static_cast<StopSegment *>(seg)->SetPause(fStop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"] );
|
||||
JsonUtil::DeserializeVectorPointers( td.allTimingSegments[SEGMENT_BPM], Deserialize, root["BpmSegments"] );
|
||||
JsonUtil::DeserializeVectorPointers( td.allTimingSegments[SEGMENT_STOP_DELAY], Deserialize, root["StopSegments"] );
|
||||
}
|
||||
|
||||
static void Deserialize(LyricSegment &o, const Json::Value &root)
|
||||
@@ -148,8 +159,6 @@ static void Deserialize( Song &out, const Json::Value &root )
|
||||
else if( sSelectable.EqualsNoCase("NO") )
|
||||
out.m_SelectionDisplay = out.SHOW_NEVER;
|
||||
|
||||
out.m_fFirstBeat = (float)root["FirstBeat"].asDouble();
|
||||
out.m_fLastBeat = (float)root["LastBeat"].asDouble();
|
||||
out.m_sSongFileName = root["SongFileName"].asString();
|
||||
out.m_bHasMusic = root["HasMusic"].asBool();
|
||||
out.m_bHasBanner = root["HasBanner"].asBool();
|
||||
|
||||
+163
-88
@@ -17,44 +17,10 @@ static void HandleBunki( TimingData &timing, const float fEarlyBPM,
|
||||
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) );
|
||||
timing.AddSegment( SEGMENT_BPM, new BPMSegment(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 )
|
||||
static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool bKIUCompliant )
|
||||
{
|
||||
LOG->Trace( "Steps::LoadFromKSFFile( '%s' )", sPath.c_str() );
|
||||
|
||||
@@ -65,7 +31,10 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
return false;
|
||||
}
|
||||
|
||||
int iTickCount = -1; // this is the value we read for TICKCOUNT
|
||||
// this is the value we read for TICKCOUNT
|
||||
int iTickCount = -1;
|
||||
// used to adapt weird tickcounts
|
||||
//float fScrollRatio = 1.0f; -- uncomment when ready to use.
|
||||
vector<RString> vNoteRows;
|
||||
|
||||
// According to Aldo_MX, there is a default BPM and it's 60. -aj
|
||||
@@ -85,13 +54,12 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
if (sValueName=="TITLE" || EndsWith(sValueName, "INTRO")
|
||||
|| EndsWith(sValueName, "FILE") )
|
||||
{
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
else if( sValueName=="BPM" )
|
||||
{
|
||||
BPM1 = StringToFloat(sParams[1]);
|
||||
stepsTiming.AddBPMSegment( BPMSegment(0, BPM1) );
|
||||
stepsTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, BPM1) );
|
||||
}
|
||||
else if( sValueName=="BPM2" )
|
||||
{
|
||||
@@ -168,7 +136,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
LOG->UserLog( "Song file", sPath, "has an invalid tick count: %d.", iTickCount );
|
||||
return false;
|
||||
}
|
||||
stepsTiming.AddTickcountSegment(TickcountSegment(0, iTickCount));
|
||||
stepsTiming.AddSegment( SEGMENT_TICKCOUNT, new TickcountSegment(0, iTickCount));
|
||||
}
|
||||
|
||||
else if( sValueName=="DIFFICULTY" )
|
||||
@@ -226,28 +194,43 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
out.SetDifficulty( Difficulty_Edit );
|
||||
if( !out.GetMeter() ) out.SetMeter( 25 );
|
||||
}
|
||||
else if( sFName.find("wild") != string::npos || sFName.find("wd") != string::npos || sFName.find("crazy+") != string::npos || sFName.find("cz+") != string::npos || sFName.find("hardcore") != string::npos )
|
||||
else if(sFName.find("wild") != string::npos ||
|
||||
sFName.find("wd") != string::npos ||
|
||||
sFName.find("crazy+") != string::npos ||
|
||||
sFName.find("cz+") != string::npos ||
|
||||
sFName.find("hardcore") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Challenge );
|
||||
if( !out.GetMeter() ) out.SetMeter( 20 );
|
||||
}
|
||||
else if( sFName.find("crazy") != string::npos || sFName.find("cz") != string::npos || sFName.find("nightmare") != string::npos || sFName.find("nm") != string::npos || sFName.find("crazydouble") != string::npos )
|
||||
else if(sFName.find("crazy") != string::npos ||
|
||||
sFName.find("cz") != string::npos ||
|
||||
sFName.find("nightmare") != string::npos ||
|
||||
sFName.find("nm") != string::npos ||
|
||||
sFName.find("crazydouble") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Hard );
|
||||
if( !out.GetMeter() ) out.SetMeter( 14 ); // Set the meters to the Pump scale, not DDR.
|
||||
}
|
||||
else if( sFName.find("hard") != string::npos || sFName.find("hd") != string::npos || sFName.find("freestyle") != string::npos || sFName.find("fs") != string::npos || sFName.find("double") != string::npos )
|
||||
else if(sFName.find("hard") != string::npos ||
|
||||
sFName.find("hd") != string::npos ||
|
||||
sFName.find("freestyle") != string::npos ||
|
||||
sFName.find("fs") != string::npos ||
|
||||
sFName.find("double") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Medium );
|
||||
if( !out.GetMeter() ) out.SetMeter( 8 );
|
||||
}
|
||||
else if( sFName.find("easy") != string::npos || sFName.find("ez") != string::npos || sFName.find("normal") != string::npos )
|
||||
else if(sFName.find("easy") != string::npos ||
|
||||
sFName.find("ez") != string::npos ||
|
||||
sFName.find("normal") != string::npos )
|
||||
{
|
||||
// I wonder if I should leave easy fall into the Beginner difficulty... -DaisuMaster
|
||||
out.SetDifficulty( Difficulty_Easy );
|
||||
if( !out.GetMeter() ) out.SetMeter( 4 );
|
||||
}
|
||||
else if( sFName.find("beginner") != string::npos || sFName.find("practice") != string::npos || sFName.find("pr") != string::npos )
|
||||
else if(sFName.find("beginner") != string::npos ||
|
||||
sFName.find("practice") != string::npos || sFName.find("pr") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Beginner );
|
||||
if( !out.GetMeter() ) out.SetMeter( 4 );
|
||||
@@ -261,10 +244,18 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
out.m_StepsType = StepsType_pump_single;
|
||||
|
||||
// Check for "halfdouble" before "double".
|
||||
if( sFName.find("halfdouble") != string::npos || sFName.find("half-double") != string::npos || sFName.find("h_double") != string::npos || sFName.find("hdb") != string::npos )
|
||||
if(sFName.find("halfdouble") != string::npos ||
|
||||
sFName.find("half-double") != string::npos ||
|
||||
sFName.find("h_double") != string::npos ||
|
||||
sFName.find("hdb") != string::npos )
|
||||
out.m_StepsType = StepsType_pump_halfdouble;
|
||||
// Handle bDoublesChart from above as well. -aj
|
||||
else if( sFName.find("double") != string::npos || sFName.find("nightmare") != string::npos || sFName.find("freestyle") != string::npos || sFName.find("db") != string::npos || sFName.find("nm") != string::npos || sFName.find("fs") != string::npos || bDoublesChart )
|
||||
else if(sFName.find("double") != string::npos ||
|
||||
sFName.find("nightmare") != string::npos ||
|
||||
sFName.find("freestyle") != string::npos ||
|
||||
sFName.find("db") != string::npos ||
|
||||
sFName.find("nm") != string::npos ||
|
||||
sFName.find("fs") != string::npos || bDoublesChart )
|
||||
out.m_StepsType = StepsType_pump_double;
|
||||
else if( sFName.find("_1") != string::npos )
|
||||
out.m_StepsType = StepsType_pump_single;
|
||||
@@ -311,34 +302,89 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
if( iHoldStartRow[t] == BeatToNoteRow(prevBeat) )
|
||||
notedata.SetTapNote( t, iHoldStartRow[t], TAP_ORIGINAL_TAP );
|
||||
else
|
||||
notedata.AddHoldNote( t, iHoldStartRow[t], BeatToNoteRow(prevBeat) , TAP_ORIGINAL_HOLD_HEAD );
|
||||
notedata.AddHoldNote(t,
|
||||
iHoldStartRow[t],
|
||||
BeatToNoteRow(prevBeat),
|
||||
TAP_ORIGINAL_HOLD_HEAD );
|
||||
}
|
||||
}
|
||||
/* have this row be the last moment in the song, unless
|
||||
* a future step ends later. */
|
||||
float curTime = stepsTiming.GetElapsedTimeFromBeat(fCurBeat);
|
||||
if (curTime > song.GetSpecifiedLastSecond())
|
||||
{
|
||||
song.SetSpecifiedLastSecond(curTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
else if( BeginsWith(sRowString, "|") )
|
||||
{
|
||||
/*
|
||||
if (bKIUCompliant)
|
||||
{
|
||||
// Log an error, ignore the line.
|
||||
continue;
|
||||
}
|
||||
if ( !HandlePipeChars( stepsTiming, sRowString, fCurBeat, iTickCount ) )
|
||||
{
|
||||
// LOG it first.
|
||||
}
|
||||
continue;
|
||||
*/
|
||||
// gotta do something tricky here: if the bpm is below one then a couple of calculations
|
||||
// for scrollsegments will be made, example, bpm 0.2, tick 4000, the scrollsegment will
|
||||
// be 0. if the tickcount is non a stepmania standard then it will be adapted, a scroll
|
||||
// segment will then be added based on approximations. -DaisuMaster
|
||||
// eh better do it considering the tickcount (high tickcounts)
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Is this why improper ksf or some kiucompilant ksf mixed with dm05 ksf are ignored?? -DaisuMaster
|
||||
//LOG->UserLog( "Song file", sPath, "has a RowString with an improper length \"%s\"; corrupt notes ignored.",
|
||||
// sRowString.c_str() );
|
||||
//return false;
|
||||
// I'm making some experiments, please spare me...
|
||||
//continue;
|
||||
|
||||
RString temp = sRowString.substr(2,sRowString.size()-3);
|
||||
float numTemp = StringToFloat(temp);
|
||||
if (BeginsWith(sRowString, "|T"))
|
||||
{
|
||||
// duh
|
||||
iTickCount = static_cast<int>(numTemp);
|
||||
// I have been owned by the man -DaisuMaster
|
||||
stepsTiming.SetTickcountAtBeat( fCurBeat, clamp(iTickCount, 0, ROWS_PER_BEAT) );
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|B"))
|
||||
{
|
||||
// BPM
|
||||
stepsTiming.SetBPMAtBeat( fCurBeat, numTemp );
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|E"))
|
||||
{
|
||||
// DelayBeat
|
||||
float fCurDelay = 60 / stepsTiming.GetBPMAtBeat(fCurBeat) * numTemp / iTickCount;
|
||||
fCurDelay += stepsTiming.GetDelayAtRow(BeatToNoteRow(fCurBeat) );
|
||||
stepsTiming.SetStopAtBeat( fCurBeat, fCurDelay, true );
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|D"))
|
||||
{
|
||||
// Delays
|
||||
float fCurDelay = stepsTiming.GetStopAtRow(BeatToNoteRow(fCurBeat) );
|
||||
fCurDelay += numTemp / 1000;
|
||||
stepsTiming.SetStopAtBeat( fCurBeat, fCurDelay, true );
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|M") || BeginsWith(sRowString, "|C"))
|
||||
{
|
||||
// multipliers/combo
|
||||
stepsTiming.SetHitComboAtBeat( fCurBeat, static_cast<int>(numTemp) );
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|S"))
|
||||
{
|
||||
// speed segments
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|F"))
|
||||
{
|
||||
// fakes
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|X"))
|
||||
{
|
||||
// scroll segments
|
||||
stepsTiming.SetScrollAtBeat( fCurBeat, numTemp );
|
||||
//return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Half-doubles is offset; "0011111100000".
|
||||
@@ -459,6 +505,20 @@ static void LoadTags( const RString &str, Song &out )
|
||||
out.m_sArtist = artist;
|
||||
}
|
||||
|
||||
static void ProcessTickcounts( const RString & value, int & ticks, TimingData & timing )
|
||||
{
|
||||
/* 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. */
|
||||
ticks = StringToInt( value );
|
||||
ticks = ticks > 0 ? ticks : 4;
|
||||
// add a tickcount for those using the [Player]
|
||||
// CheckpointsUseTimeSignatures metric. -aj
|
||||
// It's not with timesigs now -DaisuMaster
|
||||
TickcountSegment * tcs = new TickcountSegment(0, ticks > ROWS_PER_BEAT ? ROWS_PER_BEAT : ticks);
|
||||
timing.AddSegment( SEGMENT_TICKCOUNT, tcs );
|
||||
}
|
||||
|
||||
static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant )
|
||||
{
|
||||
MsdFile msd;
|
||||
@@ -497,7 +557,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
else if( sValueName=="BPM" )
|
||||
{
|
||||
BPM1 = StringToFloat(sParams[1]);
|
||||
out.m_SongTiming.AddBPMSegment( BPMSegment(0, BPM1) );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, BPM1) );
|
||||
}
|
||||
else if( sValueName=="BPM2" )
|
||||
{
|
||||
@@ -533,21 +593,11 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
else if ( sValueName=="STARTTIME3" )
|
||||
{
|
||||
// STARTTIME3 only ensures this is a KIU compliant simfile.
|
||||
bKIUCompliant = true;
|
||||
//bKIUCompliant = true;
|
||||
}
|
||||
else if ( sValueName=="TICKCOUNT" )
|
||||
{
|
||||
/* 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 = StringToInt( sParams[1] );
|
||||
iTickCount = iTickCount > 0 ? iTickCount : 4;
|
||||
// add a tickcount for those using the [Player]
|
||||
// CheckpointsUseTimeSignatures metric. -aj
|
||||
// It's not with timesigs now -DaisuMaster
|
||||
TickcountSegment tcs(0);
|
||||
tcs.SetTicks(iTickCount > ROWS_PER_BEAT ? ROWS_PER_BEAT : iTickCount);
|
||||
out.m_SongTiming.AddTickcountSegment( tcs );
|
||||
ProcessTickcounts(sParams[1], iTickCount, out.m_SongTiming);
|
||||
}
|
||||
else if ( sValueName=="STEP" )
|
||||
{
|
||||
@@ -557,10 +607,10 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
TrimLeft( theSteps );
|
||||
split( theSteps, "\n", vNoteRows, true );
|
||||
}
|
||||
|
||||
else if( sValueName=="DIFFICULTY" )
|
||||
else if( sValueName=="DIFFICULTY" || sValueName=="PLAYER" )
|
||||
{
|
||||
/* DIFFICULTY is handled only in LoadFromKSFFile. Ignore it here. */
|
||||
/* DIFFICULTY and PLAYER are handled only in LoadFromKSFFile.
|
||||
Ignore those here. */
|
||||
continue;
|
||||
}
|
||||
// New cases noted in Aldo_MX's code:
|
||||
@@ -632,14 +682,10 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
|
||||
}
|
||||
|
||||
// This is where the DMRequired test will take place.
|
||||
if (BeginsWith(NoteRowString, "|T") || BeginsWith(NoteRowString, "|B") ||
|
||||
BeginsWith(NoteRowString, "|D") || BeginsWith(NoteRowString, "|E") )
|
||||
if ( BeginsWith( NoteRowString, "|" ) )
|
||||
{
|
||||
// have a static timing for everything
|
||||
bDMRequired = true;
|
||||
if ( !HandlePipeChars( out.m_SongTiming, NoteRowString, fCurBeat, iTickCount ) )
|
||||
{
|
||||
// LOG it first.
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -669,6 +715,22 @@ void KSFLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.ksf"), out );
|
||||
}
|
||||
|
||||
bool KSFLoader::LoadNoteDataFromSimfile( const RString & cachePath, Steps &out )
|
||||
{
|
||||
bool KIUCompliant = false;
|
||||
Song dummy;
|
||||
if (!LoadGlobalData(cachePath, dummy, KIUCompliant))
|
||||
return false;
|
||||
Steps *notes = dummy.CreateSteps();
|
||||
if (LoadFromKSFFile(cachePath, *notes, dummy, KIUCompliant))
|
||||
{
|
||||
KIUCompliant = true; // yeah, reusing a variable.
|
||||
out.SetNoteData(notes->GetNoteData());
|
||||
}
|
||||
delete notes;
|
||||
return KIUCompliant;
|
||||
}
|
||||
|
||||
bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
{
|
||||
LOG->Trace( "KSFLoader::LoadFromDir(%s)", sDir.c_str() );
|
||||
@@ -685,23 +747,36 @@ bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
* order (hopefully), it is best to use the LAST file for timing
|
||||
* purposes, for that is the "normal", or easiest difficulty.
|
||||
* Usually. */
|
||||
// Nevermind, kiu compilancy is screwing things up:
|
||||
// IE, I have two simfiles, oh wich each have four ksf files, the first one has
|
||||
// the first ksf with directmove timing changes, and the rest are not, everything
|
||||
// goes fine. In the other hand I have my second simfile with the first ksf file
|
||||
// without directmove timing changes and the rest have changes, changes are not
|
||||
// loaded due to kiucompilancy in the first ksf file.
|
||||
// About the "normal" thing, my simfiles' ksfs uses non-standard naming so
|
||||
// the last chart is usually nightmare or normal, I use easy and normal
|
||||
// indistinctly for SM so it shouldn't matter, I use piu fiesta/ex naming
|
||||
// for directmove though, and we're just gathering basic info anyway, and
|
||||
// most of the time all the KSF files have the same info in the #TITLE:; section
|
||||
unsigned files = arrayKSFFileNames.size();
|
||||
if( !LoadGlobalData(out.GetSongDir() + arrayKSFFileNames[files - 1], out, bKIUCompliant) )
|
||||
RString dir = out.GetSongDir();
|
||||
if( !LoadGlobalData(dir + arrayKSFFileNames[files - 1], out, bKIUCompliant) )
|
||||
return false;
|
||||
|
||||
out.m_sSongFileName = dir + arrayKSFFileNames[files - 1];
|
||||
// load the Steps from the rest of the KSF files
|
||||
for( unsigned i=0; i<files; i++ )
|
||||
{
|
||||
Steps* pNewNotes = out.CreateSteps();
|
||||
if( !LoadFromKSFFile(out.GetSongDir() + arrayKSFFileNames[i], *pNewNotes, out, bKIUCompliant) )
|
||||
if( !LoadFromKSFFile(dir + arrayKSFFileNames[i], *pNewNotes, out, bKIUCompliant) )
|
||||
{
|
||||
delete pNewNotes;
|
||||
continue;
|
||||
}
|
||||
|
||||
pNewNotes->SetFilename(dir + arrayKSFFileNames[i]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
out.TidyUpData();
|
||||
out.TidyUpData(false, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#define NOTES_LOADER_KSF_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
/** @brief Reads a Song from a set of .KSF files. */
|
||||
namespace KSFLoader
|
||||
{
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadFromDir( const RString &sDir, Song &out );
|
||||
bool LoadNoteDataFromSimfile( const RString & cachePath, Steps &out );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+10
-9
@@ -677,21 +677,22 @@ static bool LoadFromMidi( const RString &sPath, Song &songOut )
|
||||
|
||||
FOREACH_CONST( MidiFileIn::TempoChange, midi.tempoEvents_, iter )
|
||||
{
|
||||
BPMSegment bpmSeg;
|
||||
bpmSeg.SetRow( MidiCountToNoteRow( iter->count ) );
|
||||
BPMSegment * bpmSeg = NULL;
|
||||
bpmSeg->SetRow( MidiCountToNoteRow( iter->count ) );
|
||||
double fSecondsPerBeat = (iter->tickSeconds * GUITAR_MIDI_COUNTS_PER_BEAT);
|
||||
bpmSeg.SetBPS( float( 1. / fSecondsPerBeat ) );
|
||||
bpmSeg->SetBPS( float( 1. / fSecondsPerBeat ) );
|
||||
|
||||
songOut.m_SongTiming.AddBPMSegment( bpmSeg );
|
||||
songOut.m_SongTiming.AddSegment( SEGMENT_BPM, bpmSeg );
|
||||
}
|
||||
|
||||
FOREACH_CONST( MidiFileIn::TimeSignatureChange, midi.timeSignatureEvents_, iter )
|
||||
{
|
||||
TimeSignatureSegment seg(MidiCountToNoteRow( iter->count ),
|
||||
iter->numerator,
|
||||
iter->denominator);
|
||||
TimeSignatureSegment * seg =
|
||||
new TimeSignatureSegment(MidiCountToNoteRow( iter->count ),
|
||||
iter->numerator,
|
||||
iter->denominator);
|
||||
|
||||
songOut.m_SongTiming.AddTimeSignatureSegment( seg );
|
||||
songOut.m_SongTiming.AddSegment( SEGMENT_TIME_SIG, seg );
|
||||
}
|
||||
|
||||
|
||||
@@ -955,7 +956,7 @@ bool MidiLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
if( !LoadFromMidi(sDir+vsFiles[0], out) )
|
||||
return false;
|
||||
|
||||
out.TidyUpData();
|
||||
out.TidyUpData(false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+84
-18
@@ -573,7 +573,7 @@ static bool LoadFromPMSFile( 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 RString &sPath, const NameToData_t &mapNameToData, Song &out, MeasureToTimeSig_t &sigAdjustmentsOut, map<RString,int> &idToKeySoundIndexOut )
|
||||
{
|
||||
RString sData;
|
||||
if( GetTagFromMap(mapNameToData, "#title", sData) )
|
||||
@@ -590,8 +590,7 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( 0, fBPM );
|
||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, fBPM) );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||
}
|
||||
else
|
||||
@@ -612,26 +611,29 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
// this is keysound file name. Looks like "#WAV1A"
|
||||
RString nData = it->second;
|
||||
RString sWavID = sName.Right(2);
|
||||
RString dir = out.GetSongDir();
|
||||
if (dir.empty())
|
||||
dir = Dirname(sPath);
|
||||
|
||||
/* Due to bugs in some programs, many PMS files have a "WAV" extension
|
||||
* on files in the PMS 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) )
|
||||
if( !IsAFile(dir+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) )
|
||||
if( IsAFile(dir+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() );
|
||||
if( !IsAFile(dir+nData) )
|
||||
LOG->UserLog( "Song file", dir, "references key \"%s\" that can't be found", nData.c_str() );
|
||||
|
||||
sWavID.MakeUpper(); // HACK: undo the MakeLower()
|
||||
out.m_vsKeysoundFile.push_back( nData );
|
||||
@@ -693,9 +695,9 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( BeatToNoteRow(fBeat), fBPM );
|
||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", fBeat, newSeg.GetBPM() );
|
||||
BPMSegment * newSeg = new BPMSegment( fBeat, fBPM );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_BPM, newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", fBeat, newSeg->GetBPM() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -720,9 +722,9 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
float fBeats = StringToFloat( sBeats ) / 48.0f;
|
||||
float fFreezeSecs = fBeats / fBPS;
|
||||
|
||||
StopSegment newSeg( BeatToNoteRow(fBeat), fFreezeSecs );
|
||||
out.m_SongTiming.AddStopSegment( newSeg );
|
||||
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg.GetPause() );
|
||||
StopSegment * newSeg = new StopSegment( fBeat, fFreezeSecs );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_STOP_DELAY, newSeg );
|
||||
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg->GetPause() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -749,9 +751,11 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
|
||||
if( fBPM > 0.0f )
|
||||
{
|
||||
BPMSegment newSeg( iStepIndex, fBPM );
|
||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", newSeg.GetBeat(), newSeg.GetBPM() );
|
||||
BPMSegment * newSeg = new BPMSegment( iStepIndex, fBPM );
|
||||
out.m_SongTiming.AddSegment( SEGMENT_BPM, newSeg );
|
||||
LOG->Trace("Inserting new BPM change at beat %f, BPM %f",
|
||||
newSeg->GetBeat(),
|
||||
newSeg->GetBPM() );
|
||||
|
||||
}
|
||||
else
|
||||
@@ -809,6 +813,64 @@ void PMSLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.pms"), out );
|
||||
}
|
||||
|
||||
bool PMSLoader::LoadNoteDataFromSimfile(const RString &cachePath, Steps &out)
|
||||
{
|
||||
Song dummy;
|
||||
// TODO: Simplify this copy/paste from LoadFromDir.
|
||||
|
||||
vector<NameToData_t> BMSData;
|
||||
BMSData.push_back(NameToData_t());
|
||||
ReadPMSFile(cachePath, BMSData.back());
|
||||
|
||||
RString commonSubstring;
|
||||
GetCommonTagFromMapList( BMSData, "#title", commonSubstring );
|
||||
|
||||
Steps *copy = dummy.CreateSteps();
|
||||
|
||||
copy->SetDifficulty( Difficulty_Medium );
|
||||
RString sTag;
|
||||
if( GetTagFromMap( BMSData[0], "#title", sTag ) && sTag.size() != commonSubstring.size() )
|
||||
{
|
||||
sTag = sTag.substr( commonSubstring.size(), sTag.size() - commonSubstring.size() );
|
||||
sTag.MakeLower();
|
||||
|
||||
if( sTag.find('l') != sTag.npos )
|
||||
{
|
||||
unsigned lPos = sTag.find('l');
|
||||
if( lPos > 2 && sTag.substr(lPos-2,4) == "solo" )
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Edit );
|
||||
}
|
||||
else
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Easy );
|
||||
}
|
||||
}
|
||||
else if( sTag.find('a') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Hard );
|
||||
else if( sTag.find('b') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Beginner );
|
||||
}
|
||||
if( commonSubstring == "" )
|
||||
{
|
||||
copy->SetDifficulty(Difficulty_Medium);
|
||||
RString unused;
|
||||
if (GetTagFromMap(BMSData[0], "#title#", sTag))
|
||||
SearchForDifficulty(unused, copy);
|
||||
}
|
||||
MeasureToTimeSig_t sigAdjustments;
|
||||
map<RString,int> idToKeysoundIndex;
|
||||
ReadGlobalTags( cachePath, BMSData[0], dummy, sigAdjustments, idToKeysoundIndex );
|
||||
|
||||
const bool ok = LoadFromPMSFile( cachePath, BMSData[0], *copy, sigAdjustments, idToKeysoundIndex );
|
||||
if( ok )
|
||||
{
|
||||
out.SetNoteData(copy->GetNoteData());
|
||||
}
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromPMSDir(%s)", sDir.c_str() );
|
||||
@@ -914,7 +976,8 @@ bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
|
||||
MeasureToTimeSig_t sigAdjustments;
|
||||
map<RString,int> idToKeysoundIndex;
|
||||
ReadGlobalTags( aPMSData[iMainDataIndex], out, sigAdjustments, idToKeysoundIndex );
|
||||
ReadGlobalTags( sDir, aPMSData[iMainDataIndex], out, sigAdjustments, idToKeysoundIndex );
|
||||
out.m_sSongFileName = out.GetSongDir() + arrayPMSFileNames[iMainDataIndex];
|
||||
|
||||
// Override what that global tag said about the title if we have a good substring.
|
||||
// Prevents clobbering and catches "MySong (7keys)" / "MySong (Another) (7keys)"
|
||||
@@ -929,7 +992,10 @@ bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
Steps* pNewNotes = apSteps[i];
|
||||
const bool ok = LoadFromPMSFile( out.GetSongDir() + arrayPMSFileNames[i], aPMSData[i], *pNewNotes, sigAdjustments, idToKeysoundIndex );
|
||||
if( ok )
|
||||
{
|
||||
pNewNotes->SetFilename(out.GetSongDir() + arrayPMSFileNames[i]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else
|
||||
delete pNewNotes;
|
||||
}
|
||||
@@ -940,7 +1006,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();
|
||||
out.TidyUpData(false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#define NOTES_LOADER_PMS_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
/** @brief Reads a Song from a set of .PMS files. */
|
||||
namespace PMSLoader
|
||||
{
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadFromDir( const RString &sDir, Song &out );
|
||||
bool LoadNoteDataFromSimfile(const RString & cachePath, Steps & out);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+320
-153
@@ -13,10 +13,49 @@
|
||||
#include "Attack.h"
|
||||
#include "PrefsManager.h"
|
||||
|
||||
/** @brief The maximum file size for edits. */
|
||||
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB
|
||||
void SMLoader::SetSongTitle(const RString & title)
|
||||
{
|
||||
this->songTitle = title;
|
||||
}
|
||||
|
||||
void SMLoader::LoadFromSMTokens(
|
||||
RString SMLoader::GetSongTitle() const
|
||||
{
|
||||
return this->songTitle;
|
||||
}
|
||||
|
||||
bool SMLoader::LoadFromDir( const RString &sPath, Song &out )
|
||||
{
|
||||
vector<RString> aFileNames;
|
||||
GetApplicableFiles( sPath, aFileNames );
|
||||
|
||||
if( aFileNames.size() > 1 )
|
||||
{
|
||||
// Need to break this up first.
|
||||
RString tmp = "Song " + sPath + " has more than one";
|
||||
LOG->UserLog(tmp, this->GetFileExtension(), "file. There can only be one!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT( aFileNames.size() == 1 );
|
||||
return LoadFromSimfile( sPath + aFileNames[0], out );
|
||||
}
|
||||
|
||||
float SMLoader::RowToBeat( RString line, const int rowsPerBeat )
|
||||
{
|
||||
RString backup = line;
|
||||
Trim(line, "r");
|
||||
Trim(line, "R");
|
||||
if( backup != line )
|
||||
{
|
||||
return StringToFloat( line ) / rowsPerBeat;
|
||||
}
|
||||
else
|
||||
{
|
||||
return StringToFloat( line );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::LoadFromTokens(
|
||||
RString sStepsType,
|
||||
RString sDescription,
|
||||
RString sDifficulty,
|
||||
@@ -34,12 +73,13 @@ void SMLoader::LoadFromSMTokens(
|
||||
Trim( sDifficulty );
|
||||
Trim( sNoteData );
|
||||
|
||||
// LOG->Trace( "Steps::LoadFromSMTokens()" );
|
||||
// LOG->Trace( "Steps::LoadFromTokens()" );
|
||||
|
||||
// insert stepstype hacks from GameManager.cpp here? -aj
|
||||
out.m_StepsType = GAMEMAN->StringToStepsType( sStepsType );
|
||||
out.SetDescription( sDescription );
|
||||
out.SetCredit( sDescription ); // this is often used for both.
|
||||
out.SetChartName(sDescription); // yeah, one more for good measure.
|
||||
out.SetDifficulty( StringToDifficulty(sDifficulty) );
|
||||
|
||||
// Handle hacks that originated back when StepMania didn't have
|
||||
@@ -56,34 +96,12 @@ void SMLoader::LoadFromSMTokens(
|
||||
}
|
||||
|
||||
out.SetMeter( StringToInt(sMeter) );
|
||||
vector<RString> saValues;
|
||||
split( sRadarValues, ",", saValues, true );
|
||||
int categories = NUM_RadarCategory - 1; // Fakes aren't counted in the radar values.
|
||||
if( saValues.size() == (unsigned)categories * NUM_PLAYERS )
|
||||
{
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
// Can't use the foreach anymore due to flexible radar lines.
|
||||
for( RadarCategory rc = (RadarCategory)0; rc < categories;
|
||||
enum_add<RadarCategory>( rc, 1 ) )
|
||||
{
|
||||
v[pn][rc] = StringToFloat( saValues[pn*categories + rc] );
|
||||
}
|
||||
}
|
||||
out.SetCachedRadarValues( v );
|
||||
}
|
||||
|
||||
out.SetSMNoteData( sNoteData );
|
||||
|
||||
out.TidyUpData();
|
||||
}
|
||||
|
||||
void SMLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
{
|
||||
GetDirListing( sPath + RString("*.sm"), out );
|
||||
}
|
||||
|
||||
void SMLoader::ProcessBGChanges( Song &out, const RString &sValueName, const RString &sPath, const RString &sParam )
|
||||
{
|
||||
BackgroundLayer iLayer = BACKGROUND_LAYER_1;
|
||||
@@ -109,26 +127,32 @@ void SMLoader::ProcessBGChanges( Song &out, const RString &sValueName, const RSt
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessAttacks( Song &out, MsdFile::value_t sParams )
|
||||
void SMLoader::ProcessAttackString( vector<RString> & attacks, MsdFile::value_t params )
|
||||
{
|
||||
for( unsigned s=1; s < params.params.size(); ++s )
|
||||
{
|
||||
RString tmp = params[s];
|
||||
Trim(tmp);
|
||||
if (tmp.size() > 0)
|
||||
attacks.push_back( tmp );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessAttacks( AttackArray &attacks, MsdFile::value_t params )
|
||||
{
|
||||
// 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 )
|
||||
for( unsigned j=1; j < params.params.size(); ++j )
|
||||
{
|
||||
vector<RString> sBits;
|
||||
split( sParams[j], "=", sBits, false );
|
||||
split( params[j], "=", sBits, false );
|
||||
|
||||
// Need an identifer and a value for this to work
|
||||
if( sBits.size() < 2 )
|
||||
continue;
|
||||
|
||||
TrimLeft( sBits[0] );
|
||||
TrimRight( sBits[0] );
|
||||
Trim( sBits[0] );
|
||||
|
||||
if( !sBits[0].CompareNoCase("TIME") )
|
||||
attack.fStartSecond = strtof( sBits[1], NULL );
|
||||
@@ -138,6 +162,7 @@ void SMLoader::ProcessAttacks( Song &out, MsdFile::value_t sParams )
|
||||
end = strtof( sBits[1], NULL );
|
||||
else if( !sBits[0].CompareNoCase("MODS") )
|
||||
{
|
||||
Trim(sBits[1]);
|
||||
attack.sModifiers = sBits[1];
|
||||
|
||||
if( end != -9999 )
|
||||
@@ -149,7 +174,7 @@ void SMLoader::ProcessAttacks( Song &out, MsdFile::value_t sParams )
|
||||
if( attack.fSecsRemaining < 0.0f )
|
||||
attack.fSecsRemaining = 0.0f;
|
||||
|
||||
out.m_Attacks.push_back( attack );
|
||||
attacks.push_back( attack );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,10 +196,10 @@ void SMLoader::ProcessInstrumentTracks( Song &out, const RString &sParam )
|
||||
}
|
||||
}
|
||||
|
||||
bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam )
|
||||
bool SMLoader::ProcessBPMs( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> arrayBPMChangeExpressions;
|
||||
split( sParam, ",", arrayBPMChangeExpressions );
|
||||
split( line, ",", arrayBPMChangeExpressions );
|
||||
|
||||
// prepare storage variables for negative BPMs -> Warps.
|
||||
float negBeat = -1;
|
||||
@@ -186,22 +211,22 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam )
|
||||
{
|
||||
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.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"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 fBeat = RowToBeat( arrayBPMChangeValues[0], rowsPerBeat );
|
||||
const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] );
|
||||
|
||||
if( fNewBPM < 0.0f )
|
||||
{
|
||||
out.m_bHasNegativeBpms = true;
|
||||
negBeat = fBeat;
|
||||
negBPM = fNewBPM;
|
||||
}
|
||||
@@ -211,8 +236,8 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam )
|
||||
if( negBPM < 0 )
|
||||
{
|
||||
float endBeat = fBeat + (fNewBPM / -negBPM) * (fBeat - negBeat);
|
||||
WarpSegment new_seg(negBeat, endBeat - negBeat);
|
||||
out.AddWarpSegment( new_seg );
|
||||
out.AddSegment(SEGMENT_WARP,
|
||||
new WarpSegment(negBeat, endBeat - negBeat));
|
||||
|
||||
negBeat = -1;
|
||||
negBPM = 1;
|
||||
@@ -227,13 +252,13 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam )
|
||||
// add in a warp.
|
||||
if( highspeedBeat > 0 )
|
||||
{
|
||||
WarpSegment new_seg(highspeedBeat, fBeat - highspeedBeat);
|
||||
out.AddWarpSegment( new_seg );
|
||||
out.AddSegment(SEGMENT_WARP,
|
||||
new WarpSegment(highspeedBeat, fBeat - highspeedBeat) );
|
||||
highspeedBeat = -1;
|
||||
}
|
||||
{
|
||||
BPMSegment new_seg( BeatToNoteRow( fBeat ), fNewBPM );
|
||||
out.AddBPMSegment( new_seg );
|
||||
out.AddSegment(SEGMENT_BPM,
|
||||
new BPMSegment(fBeat, fNewBPM));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,10 +267,10 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString sParam )
|
||||
return bNotEmpty;
|
||||
}
|
||||
|
||||
void SMLoader::ProcessStops( TimingData &out, const RString sParam )
|
||||
void SMLoader::ProcessStops( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> arrayFreezeExpressions;
|
||||
split( sParam, ",", arrayFreezeExpressions );
|
||||
split( line, ",", arrayFreezeExpressions );
|
||||
|
||||
// Prepare variables for negative stop conversion.
|
||||
float negBeat = -1;
|
||||
@@ -257,27 +282,27 @@ void SMLoader::ProcessStops( TimingData &out, const RString sParam )
|
||||
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.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #STOPS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayFreezeExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] );
|
||||
const float fFreezeBeat = RowToBeat( arrayFreezeValues[0], rowsPerBeat );
|
||||
const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] );
|
||||
|
||||
// Process the prior stop.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
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 );
|
||||
out.AddSegment(SEGMENT_WARP, new WarpSegment(negBeat, fSkipBeats));
|
||||
|
||||
negBeat = -1;
|
||||
negPause = 0;
|
||||
@@ -290,8 +315,8 @@ void SMLoader::ProcessStops( TimingData &out, const RString sParam )
|
||||
}
|
||||
else if( fFreezeSeconds > 0.0f )
|
||||
{
|
||||
StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
|
||||
out.AddStopSegment( ss );
|
||||
out.AddSegment(SEGMENT_STOP_DELAY,
|
||||
new StopSegment(fFreezeBeat, fFreezeSeconds));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -299,19 +324,18 @@ void SMLoader::ProcessStops( TimingData &out, const RString sParam )
|
||||
// Process the prior stop if there was one.
|
||||
if( negPause > 0 )
|
||||
{
|
||||
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
|
||||
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
|
||||
BPMSegment * oldBPM = out.GetBPMSegmentAtBeat(negBeat);
|
||||
float fSecondsPerBeat = 60 / oldBPM->GetBPM();
|
||||
float fSkipBeats = negPause / fSecondsPerBeat;
|
||||
|
||||
WarpSegment ws( negBeat, fSkipBeats);
|
||||
out.AddWarpSegment( ws );
|
||||
out.AddSegment(SEGMENT_WARP, new WarpSegment(negBeat, fSkipBeats));
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessDelays( TimingData &out, const RString sParam )
|
||||
void SMLoader::ProcessDelays( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> arrayDelayExpressions;
|
||||
split( sParam, ",", arrayDelayExpressions );
|
||||
split( line, ",", arrayDelayExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayDelayExpressions.size(); f++ )
|
||||
{
|
||||
@@ -319,33 +343,37 @@ void SMLoader::ProcessDelays( TimingData &out, const RString sParam )
|
||||
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.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #DELAYS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayDelayExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fFreezeBeat = StringToFloat( arrayDelayValues[0] );
|
||||
const float fFreezeBeat = RowToBeat( arrayDelayValues[0], rowsPerBeat );
|
||||
const float fFreezeSeconds = StringToFloat( arrayDelayValues[1] );
|
||||
|
||||
StopSegment new_seg( fFreezeBeat, fFreezeSeconds, true );
|
||||
// XXX: Remove Negatives Bug?
|
||||
new_seg.SetBeat(fFreezeBeat);
|
||||
new_seg.SetPause(fFreezeSeconds);
|
||||
StopSegment * new_seg = new StopSegment(fFreezeBeat,
|
||||
fFreezeSeconds,
|
||||
true);
|
||||
|
||||
// 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 );
|
||||
out.AddSegment( SEGMENT_STOP_DELAY, new_seg );
|
||||
else
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid delay at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
|
||||
LOG->UserLog(
|
||||
"Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid delay at beat %f, length %f.",
|
||||
fFreezeBeat, fFreezeSeconds );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessTimeSignatures( TimingData &out, const RString sParam )
|
||||
void SMLoader::ProcessTimeSignatures( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParam, ",", vs1 );
|
||||
split( line, ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
@@ -354,40 +382,55 @@ void SMLoader::ProcessTimeSignatures( TimingData &out, const RString sParam )
|
||||
|
||||
if( vs2.size() < 3 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with %i values.", (int)vs2.size() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid time signature change with %i values.",
|
||||
static_cast<int>(vs2.size()) );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( vs2[0] );
|
||||
const float fBeat = RowToBeat( vs2[0], rowsPerBeat );
|
||||
|
||||
TimeSignatureSegment seg( BeatToNoteRow( fBeat ), StringToInt( vs2[1] ), StringToInt( vs2[2] ));
|
||||
TimeSignatureSegment * seg =
|
||||
new TimeSignatureSegment(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 );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid time signature change with beat %f.",
|
||||
fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.GetNum() < 1 )
|
||||
if( seg->GetNum() < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iNumerator %i.", fBeat, seg.GetNum() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid time signature change with beat %f, iNumerator %i.",
|
||||
fBeat, seg->GetNum() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.GetDen() < 1 )
|
||||
if( seg->GetDen() < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iDenominator %i.", fBeat, seg.GetDen() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid time signature change with beat %f, iDenominator %i.",
|
||||
fBeat, seg->GetDen() );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddTimeSignatureSegment( seg );
|
||||
out.AddSegment( SEGMENT_TIME_SIG, seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessTickcounts( TimingData &out, const RString sParam )
|
||||
void SMLoader::ProcessTickcounts( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> arrayTickcountExpressions;
|
||||
split( sParam, ",", arrayTickcountExpressions );
|
||||
split( line, ",", arrayTickcountExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayTickcountExpressions.size(); f++ )
|
||||
{
|
||||
@@ -395,17 +438,109 @@ void SMLoader::ProcessTickcounts( TimingData &out, const RString sParam )
|
||||
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.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #TICKCOUNTS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayTickcountExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fTickcountBeat = StringToFloat( arrayTickcountValues[0] );
|
||||
const float fTickcountBeat = RowToBeat( arrayTickcountValues[0], rowsPerBeat );
|
||||
int iTicks = clamp(atoi( arrayTickcountValues[1] ), 0, ROWS_PER_BEAT);
|
||||
|
||||
TickcountSegment new_seg( BeatToNoteRow(fTickcountBeat), iTicks );
|
||||
out.AddTickcountSegment( new_seg );
|
||||
out.AddSegment( SEGMENT_TICKCOUNT,
|
||||
new TickcountSegment(fTickcountBeat, iTicks) );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessSpeeds( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( line, ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s1, "=", vs2 );
|
||||
|
||||
if( vs2[0] == 0 && vs2.size() == 2 ) // First one always seems to have 2.
|
||||
{
|
||||
vs2.push_back("0");
|
||||
}
|
||||
|
||||
if( vs2.size() == 3 ) // use beats by default.
|
||||
{
|
||||
vs2.push_back("0");
|
||||
}
|
||||
|
||||
if( vs2.size() < 4 )
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an speed change with %i values.",
|
||||
static_cast<int>(vs2.size()) );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = RowToBeat( vs2[0], rowsPerBeat );
|
||||
|
||||
SpeedSegment * seg = new SpeedSegment(fBeat,
|
||||
StringToFloat( vs2[1] ),
|
||||
StringToFloat( vs2[2] ));
|
||||
seg->SetUnit(StringToInt(vs2[3]));
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an speed change with beat %f.",
|
||||
fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg->GetLength() < 0 )
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an speed change with beat %f, length %f.",
|
||||
fBeat, seg->GetLength() );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddSegment( SEGMENT_SPEED, seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SMLoader::ProcessFakes( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> arrayFakeExpressions;
|
||||
split( line, ",", arrayFakeExpressions );
|
||||
|
||||
for( unsigned b=0; b<arrayFakeExpressions.size(); b++ )
|
||||
{
|
||||
vector<RString> arrayFakeValues;
|
||||
split( arrayFakeExpressions[b], "=", arrayFakeValues );
|
||||
if( arrayFakeValues.size() != 2 )
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #FAKES value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayFakeExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = RowToBeat( arrayFakeValues[0], rowsPerBeat );
|
||||
const float fNewBeat = StringToFloat( arrayFakeValues[1] );
|
||||
|
||||
if(fNewBeat > 0)
|
||||
out.AddSegment( SEGMENT_FAKE, new FakeSegment(fBeat, fNewBeat) );
|
||||
else
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid Fake at beat %f, BPM %f.",
|
||||
fBeat, fNewBeat );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,7 +630,66 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString
|
||||
return aBGChangeValues.size() >= 2;
|
||||
}
|
||||
|
||||
bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache )
|
||||
bool SMLoader::LoadNoteDataFromSimfile( const RString &path, Steps &out )
|
||||
{
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( path, true ) ) // unescape
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
path,
|
||||
"couldn't be opened: %s",
|
||||
msd.GetError().c_str() );
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0; i<msd.GetNumValues(); i++)
|
||||
{
|
||||
int iNumParams = msd.GetNumParams(i);
|
||||
const MsdFile::value_t &sParams = msd.GetValue(i);
|
||||
RString sValueName = sParams[0];
|
||||
sValueName.MakeUpper();
|
||||
|
||||
// The only tag we care about is the #NOTES tag.
|
||||
if( sValueName=="NOTES" || sValueName=="NOTES2" )
|
||||
{
|
||||
if( iNumParams < 7 )
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
path,
|
||||
"has %d fields in a #NOTES tag, but should have at least 7.",
|
||||
iNumParams );
|
||||
continue;
|
||||
}
|
||||
|
||||
RString stepsType = sParams[1];
|
||||
RString description = sParams[2];
|
||||
RString difficulty = sParams[3];
|
||||
Trim(stepsType);
|
||||
Trim(description);
|
||||
Trim(difficulty);
|
||||
// Remember our old versions.
|
||||
if (difficulty.CompareNoCase("smaniac") == 0)
|
||||
{
|
||||
difficulty = "Challenge";
|
||||
}
|
||||
|
||||
if(!(out.m_StepsType == GAMEMAN->StringToStepsType( stepsType ) &&
|
||||
out.GetDescription() == description &&
|
||||
out.GetDifficulty() == StringToDifficulty(difficulty)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RString noteData = sParams[6];
|
||||
Trim( noteData );
|
||||
out.SetSMNoteData( noteData );
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromSMFile(%s)", sPath.c_str() );
|
||||
|
||||
@@ -507,6 +701,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
}
|
||||
|
||||
out.m_SongTiming.m_sFile = sPath;
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
@@ -519,7 +714,10 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
/* Don't use GetMainAndSubTitlesFromFullTitle; that's only for heuristically
|
||||
* splitting other formats that *don't* natively support #SUBTITLE. */
|
||||
if( sValueName=="TITLE" )
|
||||
{
|
||||
out.m_sMainTitle = sParams[1];
|
||||
this->SetSongTitle(sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="SUBTITLE" )
|
||||
out.m_sSubTitle = sParams[1];
|
||||
@@ -601,37 +799,19 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
}
|
||||
|
||||
else if( sValueName=="LASTBEATHINT" )
|
||||
out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] );
|
||||
{
|
||||
// unable to identify at this point: ignore
|
||||
}
|
||||
|
||||
else if( sValueName=="MUSICBYTES" )
|
||||
; /* ignore */
|
||||
|
||||
/* We calculate these. Some SMs in circulation have bogus values for
|
||||
* these, so make sure we always calculate it ourself. */
|
||||
else if( sValueName=="FIRSTBEAT" )
|
||||
// cache tags from older SM files: ignore.
|
||||
else if(sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" ||
|
||||
sValueName=="SONGFILENAME" || sValueName=="HASMUSIC" ||
|
||||
sValueName=="HASBANNER")
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_fFirstBeat = StringToFloat( sParams[1] );
|
||||
}
|
||||
else if( sValueName=="LASTBEAT" )
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_fLastBeat = StringToFloat( sParams[1] );
|
||||
}
|
||||
else if( sValueName=="SONGFILENAME" )
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_sSongFileName = sParams[1];
|
||||
}
|
||||
else if( sValueName=="HASMUSIC" )
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_bHasMusic = StringToInt( sParams[1] ) != 0;
|
||||
}
|
||||
else if( sValueName=="HASBANNER" )
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_bHasBanner = StringToInt( sParams[1] ) != 0;
|
||||
;
|
||||
}
|
||||
|
||||
else if( sValueName=="SAMPLESTART" )
|
||||
@@ -707,7 +887,8 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
// Attacks loaded from file
|
||||
else if( sValueName=="ATTACKS" )
|
||||
{
|
||||
ProcessAttacks( out, sParams );
|
||||
ProcessAttackString(out.m_sAttackString, sParams);
|
||||
ProcessAttacks(out.m_Attacks, sParams);
|
||||
}
|
||||
|
||||
else if( sValueName=="NOTES" || sValueName=="NOTES2" )
|
||||
@@ -719,7 +900,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
}
|
||||
|
||||
Steps* pNewNotes = out.CreateSteps();
|
||||
LoadFromSMTokens(
|
||||
LoadFromTokens(
|
||||
sParams[1],
|
||||
sParams[2],
|
||||
sParams[3],
|
||||
@@ -728,6 +909,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
sParams[6],
|
||||
*pNewNotes );
|
||||
|
||||
pNewNotes->SetFilename(sPath);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
// XXX: Does anyone know what LEADTRACK is for? -Wolfman2000
|
||||
@@ -738,38 +920,12 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache
|
||||
}
|
||||
|
||||
// Ensure all warps from negative time changes are in order.
|
||||
sort(out.m_SongTiming.m_WarpSegments.begin(), out.m_SongTiming.m_WarpSegments.end());
|
||||
vector<TimingSegment *> &warps = out.m_SongTiming.allTimingSegments[SEGMENT_WARP];
|
||||
sort(warps.begin(), warps.end());
|
||||
TidyUpData( out, bFromCache );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SMLoader::LoadFromDir( const RString &sPath, Song &out )
|
||||
{
|
||||
vector<RString> aFileNames;
|
||||
GetApplicableFiles( sPath, aFileNames );
|
||||
|
||||
if( aFileNames.size() > 1 )
|
||||
{
|
||||
LOG->UserLog( "Song", sPath, "has more than one SM file. There can be only one (unless you are using TougaKiryuu's AnimeMix files somehow, which assume a different version of StepMania)!" );
|
||||
return false;
|
||||
/*
|
||||
for( unsigned i=0; i<aFileNames.size(); i++ )
|
||||
{
|
||||
if(!LoadFromSMFile( sPath + aFileNames[i], out ))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
ASSERT( aFileNames.size() == 1 );
|
||||
/* We should have at least one; if we had none, we shouldn't have been
|
||||
* called to begin with. */
|
||||
//ASSERT( aFileNames.size() >= 1 );
|
||||
|
||||
return LoadFromSMFile( sPath + aFileNames[0], out );
|
||||
}
|
||||
|
||||
bool SMLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong )
|
||||
{
|
||||
LOG->Trace( "SMLoader::LoadEditFromFile(%s)", sEditFilePath.c_str() );
|
||||
@@ -819,6 +975,7 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath
|
||||
}
|
||||
|
||||
RString sSongFullTitle = sParams[1];
|
||||
this->SetSongTitle(sParams[1]);
|
||||
sSongFullTitle.Replace( '\\', '/' );
|
||||
|
||||
pSong = SONGMAN->FindSong( sSongFullTitle );
|
||||
@@ -853,7 +1010,7 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath
|
||||
return true;
|
||||
|
||||
Steps* pNewNotes = pSong->CreateSteps();
|
||||
LoadFromSMTokens(
|
||||
LoadFromTokens(
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6],
|
||||
*pNewNotes);
|
||||
|
||||
@@ -881,6 +1038,11 @@ bool SMLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath
|
||||
|
||||
}
|
||||
|
||||
void SMLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
{
|
||||
GetDirListing( sPath + RString("*" + this->GetFileExtension() ), out );
|
||||
}
|
||||
|
||||
void SMLoader::TidyUpData( Song &song, bool bFromCache )
|
||||
{
|
||||
/*
|
||||
@@ -917,9 +1079,10 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache )
|
||||
if( bFromCache )
|
||||
break;
|
||||
|
||||
float lastBeat = song.GetLastBeat();
|
||||
/* If BGChanges already exist after the last beat, don't add the
|
||||
* background in the middle. */
|
||||
if( !bg.empty() && bg.back().m_fStartBeat-0.0001f >= song.m_fLastBeat )
|
||||
if( !bg.empty() && bg.back().m_fStartBeat-0.0001f >= lastBeat )
|
||||
break;
|
||||
|
||||
// If the last BGA is already the song BGA, don't add a duplicate.
|
||||
@@ -929,10 +1092,14 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache )
|
||||
if( !IsAFile( song.GetBackgroundPath() ) )
|
||||
break;
|
||||
|
||||
bg.push_back( BackgroundChange(song.m_fLastBeat,song.m_sBackgroundFile) );
|
||||
|
||||
bg.push_back( BackgroundChange(lastBeat,song.m_sBackgroundFile) );
|
||||
} while(0);
|
||||
}
|
||||
song.TidyUpData( bFromCache );
|
||||
if (bFromCache)
|
||||
{
|
||||
song.TidyUpData( bFromCache, true );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+176
-22
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "BackgroundUtil.h"
|
||||
#include "Attack.h"
|
||||
#include "MsdFile.h" // we require the struct from here.
|
||||
|
||||
class Song;
|
||||
@@ -15,33 +16,186 @@ class TimingData;
|
||||
* This was brought in from StepMania 4's recent betas. */
|
||||
const float FAST_BPM_WARP = 9999999.f;
|
||||
|
||||
/** @brief Reads a Song from an .SM file. */
|
||||
namespace SMLoader
|
||||
{
|
||||
void LoadFromSMTokens( RString sStepsType, RString sDescription, RString sDifficulty,
|
||||
RString sMeter, RString sRadarValues, RString sNoteData, Steps &out );
|
||||
|
||||
bool LoadFromDir( const RString &sPath, Song &out );
|
||||
void TidyUpData( Song &song, bool bFromCache );
|
||||
/** @brief The maximum file size for edits. */
|
||||
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB
|
||||
|
||||
bool LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache = false );
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot );
|
||||
bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChangeExpression );
|
||||
/** @brief Reads a Song from an .SM file. */
|
||||
struct SMLoader
|
||||
{
|
||||
SMLoader() : fileExt(".sm"), songTitle() {}
|
||||
|
||||
SMLoader(RString ext) : fileExt(ext), songTitle() {}
|
||||
|
||||
bool ProcessBPMs( TimingData &, const RString );
|
||||
void ProcessStops( TimingData &, const RString );
|
||||
void ProcessDelays( TimingData &, const RString );
|
||||
void ProcessTimeSignatures( TimingData &, const RString );
|
||||
void ProcessTickcounts( TimingData &, const RString );
|
||||
void ProcessBGChanges( Song &out, const RString &sValueName,
|
||||
virtual ~SMLoader() {}
|
||||
|
||||
/**
|
||||
* @brief Attempt to load a song from a specified path.
|
||||
* @param sPath a const reference to the path on the hard drive to check.
|
||||
* @param out a reference to the Song that will retrieve the song information.
|
||||
* @return its success or failure.
|
||||
*/
|
||||
virtual bool LoadFromDir( const RString &sPath, Song &out );
|
||||
/**
|
||||
* @brief Perform some cleanup on the loaded song.
|
||||
* @param song a reference to the song that may need cleaning up.
|
||||
* @param bFromCache a flag to determine if this song is loaded from a cache file.
|
||||
*/
|
||||
virtual void TidyUpData( Song &song, bool bFromCache );
|
||||
|
||||
/**
|
||||
* @brief Retrieve the relevant notedata from the simfile.
|
||||
* @param path the path where the simfile lives.
|
||||
* @param out the Steps we are loading the data into. */
|
||||
virtual bool LoadNoteDataFromSimfile(const RString &path, Steps &out );
|
||||
|
||||
/**
|
||||
* @brief Attempt to load the specified sm file.
|
||||
* @param sPath a const reference to the path on the hard drive to check.
|
||||
* @param out a reference to the Song that will retrieve the song information.
|
||||
* @param bFromCache a check to see if we are getting certain information from the cache file.
|
||||
* @return its success or failure.
|
||||
*/
|
||||
virtual bool LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache = false );
|
||||
/**
|
||||
* @brief Retrieve the list of .sm files.
|
||||
* @param sPath a const reference to the path on the hard drive to check.
|
||||
* @param out a vector of files found in the path.
|
||||
*/
|
||||
virtual void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
virtual bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
virtual bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot );
|
||||
virtual bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
virtual bool LoadFromBGChangesString(BackgroundChange &change,
|
||||
const RString &sBGChangeExpression );
|
||||
|
||||
/**
|
||||
* @brief Process the BPM Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose.
|
||||
* @return true if there was at least one segment found, false otherwise. */
|
||||
bool ProcessBPMs(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1);
|
||||
/**
|
||||
* @brief Process the Stop Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose. */
|
||||
void ProcessStops(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1);
|
||||
/**
|
||||
* @brief Process the Delay Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose. */
|
||||
void ProcessDelays(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1);
|
||||
/**
|
||||
* @brief Process the Time Signature Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose. */
|
||||
void ProcessTimeSignatures(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1);
|
||||
/**
|
||||
* @brief Process the Tickcount Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose. */
|
||||
void ProcessTickcounts(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1);
|
||||
|
||||
/**
|
||||
* @brief Process the Speed Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose. */
|
||||
virtual void ProcessSpeeds(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1);
|
||||
|
||||
virtual void ProcessCombos(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1) {}
|
||||
|
||||
/**
|
||||
* @brief Process the Fake Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose. */
|
||||
virtual void ProcessFakes(TimingData & out,
|
||||
const RString line,
|
||||
const int rowsPerBeat = -1);
|
||||
|
||||
virtual void ProcessBGChanges( Song &out, const RString &sValueName,
|
||||
const RString &sPath, const RString &sParam );
|
||||
void ProcessAttacks( Song &out, MsdFile::value_t sParams );
|
||||
|
||||
/**
|
||||
* @brief Put the attacks in the attacks string.
|
||||
* @param attacks the attack string.
|
||||
* @param params the params from the simfile. */
|
||||
virtual void ProcessAttackString(vector<RString> &attacks, MsdFile::value_t params);
|
||||
|
||||
/**
|
||||
* @brief Put the attacks in the attacks array.
|
||||
* @param attacks the attacks array.
|
||||
* @param params the params from the simfile. */
|
||||
void ProcessAttacks( AttackArray &attacks, MsdFile::value_t params );
|
||||
void ProcessInstrumentTracks( Song &out, const RString &sParam );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a row value to the proper beat value.
|
||||
*
|
||||
* This is primarily used for assistance with converting SMA files.
|
||||
* @param line The line that contains the value.
|
||||
* @param rowsPerBeat the number of rows per beat according to the original file.
|
||||
* @return the converted beat value. */
|
||||
float RowToBeat(RString line, const int rowsPerBeat);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Process the different tokens we have available to get NoteData.
|
||||
* @param stepsType The current StepsType.
|
||||
* @param description The description of the chart.
|
||||
* @param difficulty The difficulty (in words) of the chart.
|
||||
* @param meter the difficulty (in numbers) of the chart.
|
||||
* @param radarValues the calculated radar values.
|
||||
* @param noteData the note data itself.
|
||||
* @param out the Steps getting the data. */
|
||||
virtual void LoadFromTokens(RString sStepsType,
|
||||
RString sDescription,
|
||||
RString sDifficulty,
|
||||
RString sMeter,
|
||||
RString sRadarValues,
|
||||
RString sNoteData,
|
||||
Steps &out);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the file extension associated with this loader.
|
||||
* @return the file extension. */
|
||||
RString GetFileExtension() const { return fileExt; }
|
||||
|
||||
/**
|
||||
* @brief Set the song title.
|
||||
* @param t the song title. */
|
||||
virtual void SetSongTitle(const RString & title);
|
||||
|
||||
/**
|
||||
* @brief Get the song title.
|
||||
* @return the song title. */
|
||||
virtual RString GetSongTitle() const;
|
||||
|
||||
private:
|
||||
/** @brief The file extension in use. */
|
||||
const RString fileExt;
|
||||
/** @brief The song title that is being processed. */
|
||||
RString songTitle;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+85
-460
@@ -14,240 +14,6 @@
|
||||
#include "Steps.h"
|
||||
#include "Attack.h"
|
||||
|
||||
/**
|
||||
* @brief A custom .edit file can only be so big before we have to reject it.
|
||||
*/
|
||||
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60 KB
|
||||
|
||||
bool SMALoader::LoadFromBGChangesString( BackgroundChange &change,
|
||||
const RString &sBGChangeExpression )
|
||||
{
|
||||
return SMLoader::LoadFromBGChangesString(change, sBGChangeExpression);
|
||||
}
|
||||
|
||||
bool SMALoader::LoadFromDir( const RString &sPath, Song &out )
|
||||
{
|
||||
vector<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( fFreezeBeat, fFreezeSeconds, true );
|
||||
// XXX: Remove Negatives Bug?
|
||||
new_seg.SetBeat(fFreezeBeat);
|
||||
new_seg.SetPause(fFreezeSeconds);
|
||||
|
||||
// LOG->Trace( "Adding a delay segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
|
||||
|
||||
if(fFreezeSeconds > 0.0f)
|
||||
out.AddStopSegment( new_seg );
|
||||
else
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid delay at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
|
||||
}
|
||||
}
|
||||
|
||||
void SMALoader::ProcessTickcounts( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
{
|
||||
vector<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;
|
||||
@@ -257,16 +23,23 @@ void SMALoader::ProcessMultipliers( TimingData &out, const int iRowsPerBeat, con
|
||||
{
|
||||
vector<RString> arrayMultiplierValues;
|
||||
split( arrayMultiplierExpressions[f], "=", arrayMultiplierValues );
|
||||
if( arrayMultiplierValues.size() != 2 )
|
||||
unsigned size = arrayMultiplierValues.size();
|
||||
if( size < 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #MULTIPLIER value \"%s\" (must have exactly one '='), ignored.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #MULTIPLIER value \"%s\" (must have at least 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 );
|
||||
const int iCombos = StringToInt( arrayMultiplierValues[1] ); // always true.
|
||||
// hoping I'm right here: SMA files can use 6 values after the row/beat.
|
||||
const int iMisses = (size == 2 || size == 4 ?
|
||||
iCombos :
|
||||
StringToInt(arrayMultiplierValues[2]));
|
||||
out.AddSegment(SEGMENT_COMBO,
|
||||
new ComboSegment( fComboBeat, iCombos, iMisses ));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,39 +55,45 @@ void SMALoader::ProcessBeatsPerMeasure( TimingData &out, const RString sParam )
|
||||
|
||||
if( vs2.size() < 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid beats per measure change with %i values.", (int)vs2.size() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid beats per measure change with %i values.",
|
||||
static_cast<int>(vs2.size()) );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( vs2[0] );
|
||||
|
||||
TimeSignatureSegment seg( BeatToNoteRow( fBeat ), StringToInt( vs2[1] ), 4 );
|
||||
TimeSignatureSegment * seg = new TimeSignatureSegment(fBeat,
|
||||
StringToInt(vs2[1]),
|
||||
4 );
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f.", fBeat );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid time signature change with beat %f.",
|
||||
fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.GetNum() < 1 )
|
||||
if( seg->GetNum() < 1 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iNumerator %i.", fBeat, seg.GetNum() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid time signature change with beat %f, iNumerator %i.",
|
||||
fBeat, seg->GetNum() );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddTimeSignatureSegment( seg );
|
||||
out.AddSegment( SEGMENT_TIME_SIG, seg );
|
||||
}
|
||||
}
|
||||
|
||||
float BeatToSeconds(float fromBeat, RString toSomething)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SMALoader::ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RString sParam )
|
||||
void SMALoader::ProcessSpeeds( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParam, ",", vs1 );
|
||||
split( line, ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
@@ -331,11 +110,14 @@ void SMALoader::ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RS
|
||||
|
||||
if( vs2.size() < 3 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with %i values.", (int)vs2.size() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an speed change with %i values.",
|
||||
static_cast<int>(vs2.size()) );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = RowToBeat( vs2[0], iRowsPerBeat );
|
||||
const float fBeat = RowToBeat( vs2[0], rowsPerBeat );
|
||||
|
||||
RString backup = vs2[2];
|
||||
Trim(vs2[2], "s");
|
||||
@@ -343,76 +125,34 @@ void SMALoader::ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RS
|
||||
|
||||
unsigned short tmp = ((backup != vs2[2]) ? 1 : 0);
|
||||
|
||||
SpeedSegment seg(fBeat, StringToFloat( vs2[1] ), StringToFloat(vs2[2]), tmp);
|
||||
//seg.SetUnit(tmp);
|
||||
SpeedSegment * seg = new SpeedSegment(fBeat,
|
||||
StringToFloat( vs2[1] ),
|
||||
StringToFloat(vs2[2]),
|
||||
tmp);
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f.", fBeat );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an speed change with beat %f.",
|
||||
fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.GetLength() < 0 )
|
||||
if( seg->GetLength() < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f, length %f.", fBeat, seg.GetLength() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an speed change with beat %f, length %f.",
|
||||
fBeat, seg->GetLength() );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddSpeedSegment( seg );
|
||||
out.AddSegment( SEGMENT_SPEED, 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,
|
||||
RString sDifficulty,
|
||||
RString sMeter,
|
||||
RString sRadarValues,
|
||||
RString sNoteData,
|
||||
Steps &out
|
||||
)
|
||||
{
|
||||
SMLoader::LoadFromSMTokens( sStepsType, sDescription,
|
||||
sDifficulty, sMeter, sRadarValues,
|
||||
sNoteData, out );
|
||||
}
|
||||
|
||||
void SMALoader::TidyUpData( Song &song, bool bFromCache )
|
||||
{
|
||||
SMLoader::TidyUpData( song, bFromCache );
|
||||
}
|
||||
|
||||
bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromSMAFile(%s)", sPath.c_str() );
|
||||
|
||||
@@ -424,6 +164,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
}
|
||||
|
||||
out.m_SongTiming.m_sFile = sPath; // songs still have their fallback timing.
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
int state = SMA_GETTING_SONG_INFO;
|
||||
Steps* pNewNotes = NULL;
|
||||
@@ -441,7 +182,10 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
/* Don't use GetMainAndSubTitlesFromFullTitle; that's only for heuristically
|
||||
* splitting other formats that *don't* natively support #SUBTITLE. */
|
||||
if( sValueName=="TITLE" )
|
||||
{
|
||||
out.m_sMainTitle = sParams[1];
|
||||
this->SetSongTitle(sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="SUBTITLE" )
|
||||
out.m_sSubTitle = sParams[1];
|
||||
@@ -491,30 +235,16 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
}
|
||||
|
||||
else if( sValueName=="LASTBEATHINT" )
|
||||
out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] );
|
||||
|
||||
{
|
||||
// can't identify at this position: ignore.
|
||||
}
|
||||
else if( sValueName=="MUSICBYTES" )
|
||||
; /* ignore */
|
||||
|
||||
/* We calculate these. Some SMs in circulation have bogus values for
|
||||
* these, so make sure we always calculate it ourself. */
|
||||
else if( sValueName=="FIRSTBEAT" )
|
||||
{
|
||||
;
|
||||
}
|
||||
else if( sValueName=="LASTBEAT" )
|
||||
{
|
||||
;
|
||||
}
|
||||
else if( sValueName=="SONGFILENAME" )
|
||||
{
|
||||
;
|
||||
}
|
||||
else if( sValueName=="HASMUSIC" )
|
||||
{
|
||||
;
|
||||
}
|
||||
else if( sValueName=="HASBANNER" )
|
||||
// Cache tags: ignore.
|
||||
else if (sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" ||
|
||||
sValueName=="SONGFILENAME" || sValueName=="HASMUSIC" ||
|
||||
sValueName=="HASBANNER" )
|
||||
{
|
||||
;
|
||||
}
|
||||
@@ -603,7 +333,10 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
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() );
|
||||
LOG->UserLog("Song file",
|
||||
sPath,
|
||||
"has an unknown #SELECTABLE value, \"%s\"; ignored.",
|
||||
sParams[1].c_str() );
|
||||
}
|
||||
|
||||
else if( sValueName.Left(strlen("BGCHANGES"))=="BGCHANGES" || sValueName=="ANIMATIONS" )
|
||||
@@ -635,28 +368,28 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessBPMs( timing, iRowsPerBeat, sParams[1] );
|
||||
ProcessBPMs( timing, sParams[1], iRowsPerBeat );
|
||||
}
|
||||
|
||||
else if( sValueName=="STOPS" || sValueName=="FREEZES" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessStops( timing, iRowsPerBeat, sParams[1] );
|
||||
ProcessStops( timing, sParams[1], iRowsPerBeat );
|
||||
}
|
||||
|
||||
else if( sValueName=="DELAYS" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessDelays( timing, iRowsPerBeat, sParams[1] );
|
||||
ProcessDelays( timing, sParams[1], iRowsPerBeat );
|
||||
}
|
||||
|
||||
else if( sValueName=="TICKCOUNT" )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessTickcounts( timing, iRowsPerBeat, sParams[1] );
|
||||
ProcessTickcounts( timing, sParams[1], iRowsPerBeat );
|
||||
}
|
||||
|
||||
else if( sValueName=="SPEED" )
|
||||
@@ -665,7 +398,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
RString tmp = sParams[1];
|
||||
Trim( tmp );
|
||||
ProcessSpeeds( timing, iRowsPerBeat, tmp );
|
||||
ProcessSpeeds( timing, tmp, iRowsPerBeat );
|
||||
}
|
||||
|
||||
else if( sValueName=="MULTIPLIER" )
|
||||
@@ -679,7 +412,7 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
{
|
||||
TimingData &timing = (state == SMA_GETTING_STEP_INFO
|
||||
? pNewNotes->m_Timing : out.m_SongTiming);
|
||||
ProcessFakes( timing, iRowsPerBeat, sParams[1] );
|
||||
ProcessFakes( timing, sParams[1], iRowsPerBeat );
|
||||
}
|
||||
|
||||
else if( sValueName=="METERTYPE" )
|
||||
@@ -695,18 +428,22 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
// Attacks loaded from file
|
||||
else if( sValueName=="ATTACKS" )
|
||||
{
|
||||
SMLoader::ProcessAttacks( out, sParams );
|
||||
ProcessAttackString(out.m_sAttackString, sParams);
|
||||
ProcessAttacks(out.m_Attacks, sParams);
|
||||
}
|
||||
|
||||
else if( sValueName=="NOTES" || sValueName=="NOTES2" )
|
||||
{
|
||||
if( iNumParams < 7 )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has %d fields in a #NOTES tag, but should have at least 7.", iNumParams );
|
||||
LOG->UserLog("Song file",
|
||||
sPath,
|
||||
"has %d fields in a #NOTES tag, but should have at least 7.",
|
||||
iNumParams );
|
||||
continue;
|
||||
}
|
||||
|
||||
LoadFromSMATokens(
|
||||
LoadFromTokens(
|
||||
sParams[1],
|
||||
sParams[2],
|
||||
sParams[3],
|
||||
@@ -714,131 +451,19 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out )
|
||||
sParams[5],
|
||||
sParams[6],
|
||||
*pNewNotes );
|
||||
|
||||
pNewNotes->SetFilename(sPath);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else if( sValueName=="TIMESIGNATURES" || sValueName=="LEADTRACK" )
|
||||
;
|
||||
else
|
||||
LOG->UserLog( "Song file", sPath, "has an unexpected value named \"%s\".", sValueName.c_str() );
|
||||
LOG->UserLog("Song file",
|
||||
sPath,
|
||||
"has an unexpected value named \"%s\".",
|
||||
sValueName.c_str() );
|
||||
}
|
||||
TidyUpData(out, false);
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SMALoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
{
|
||||
GetDirListing( sPath + RString("*.sma"), out );
|
||||
}
|
||||
|
||||
bool SMALoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong )
|
||||
{
|
||||
LOG->Trace( "SMALoader::LoadEditFromFile(%s)", sEditFilePath.c_str() );
|
||||
|
||||
int iBytes = FILEMAN->GetFileSizeInBytes( sEditFilePath );
|
||||
if( iBytes > MAX_EDIT_STEPS_SIZE_BYTES )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "is unreasonably large. It won't be loaded." );
|
||||
return false;
|
||||
}
|
||||
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( sEditFilePath, true ) ) // unescape
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "couldn't be opened: %s", msd.GetError().c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadEditFromMsd( msd, sEditFilePath, slot, bAddStepsToSong );
|
||||
}
|
||||
|
||||
bool SMALoader::LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot )
|
||||
{
|
||||
MsdFile msd;
|
||||
msd.ReadFromString( sBuffer, true ); // unescape
|
||||
return LoadEditFromMsd( msd, sEditFilePath, slot, true );
|
||||
}
|
||||
|
||||
bool SMALoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong )
|
||||
{
|
||||
Song* pSong = NULL;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
int iNumParams = msd.GetNumParams(i);
|
||||
const MsdFile::value_t &sParams = msd.GetValue(i);
|
||||
RString sValueName = sParams[0];
|
||||
sValueName.MakeUpper();
|
||||
|
||||
// handle the data
|
||||
if( sValueName=="SONG" )
|
||||
{
|
||||
if( pSong )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "has more than one #SONG tag." );
|
||||
return false;
|
||||
}
|
||||
|
||||
RString sSongFullTitle = sParams[1];
|
||||
sSongFullTitle.Replace( '\\', '/' );
|
||||
|
||||
pSong = SONGMAN->FindSong( sSongFullTitle );
|
||||
if( pSong == NULL )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "requires a song \"%s\" that isn't present.", sSongFullTitle.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( pSong->GetNumStepsLoadedFromProfile(slot) >= MAX_EDITS_PER_SONG_PER_PROFILE )
|
||||
{
|
||||
LOG->UserLog( "Song file", sSongFullTitle, "already has the maximum number of edits allowed for ProfileSlotP%d.", slot+1 );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="NOTES" )
|
||||
{
|
||||
if( pSong == NULL )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "doesn't have a #SONG tag preceeding the first #NOTES tag." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( iNumParams < 7 )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "has %d fields in a #NOTES tag, but should have at least 7.", iNumParams );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !bAddStepsToSong )
|
||||
return true;
|
||||
|
||||
Steps* pNewNotes = pSong->CreateSteps();
|
||||
LoadFromSMATokens(
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6],
|
||||
*pNewNotes);
|
||||
|
||||
pNewNotes->SetLoadedFromProfile( slot );
|
||||
pNewNotes->SetDifficulty( Difficulty_Edit );
|
||||
pNewNotes->SetFilename( sEditFilePath );
|
||||
|
||||
if( pSong->IsEditAlreadyLoaded(pNewNotes) )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "is a duplicate of another edit that was already loaded." );
|
||||
SAFE_DELETE( pNewNotes );
|
||||
return false;
|
||||
}
|
||||
|
||||
pSong->AddSteps( pNewNotes );
|
||||
return true; // Only allow one Steps per edit file!
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "has an unexpected value \"%s\".", sValueName.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
out.TidyUpData(false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+12
-27
@@ -2,6 +2,7 @@
|
||||
#define NOTES_LOADER_SMA_H
|
||||
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
#include "BackgroundUtil.h"
|
||||
|
||||
class MsdFile;
|
||||
@@ -20,37 +21,21 @@ enum SMALoadingStates
|
||||
};
|
||||
|
||||
/** @brief Reads a Song from a .SMA file. */
|
||||
namespace SMALoader
|
||||
{
|
||||
void LoadFromSMATokens( RString sStepsType,
|
||||
RString sDescription,
|
||||
RString sDifficulty,
|
||||
RString sMeter,
|
||||
RString sRadarValues,
|
||||
RString sNoteData,
|
||||
Steps &out );
|
||||
struct SMALoader : public SMLoader
|
||||
{
|
||||
SMALoader() : SMLoader(".sma") {}
|
||||
|
||||
bool LoadFromDir( const RString &sPath, Song &out );
|
||||
void TidyUpData( Song &song, bool bFromCache );
|
||||
|
||||
bool LoadFromSMAFile( const RString &sPath, Song &out );
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
|
||||
bool LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot );
|
||||
bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
bool LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChangeExpression );
|
||||
virtual bool LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache = false );
|
||||
|
||||
|
||||
void ProcessBeatsPerMeasure( TimingData &out, const RString sParam );
|
||||
bool ProcessBPMs( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessStops( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessDelays( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessTickcounts( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessMultipliers( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessSpeeds( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
void ProcessFakes( TimingData &out, const int iRowsPerBeat, const RString sParam );
|
||||
|
||||
float RowToBeat( RString sLine, const int iRowsPerBeat );
|
||||
/**
|
||||
* @brief Process the Speed Segments from the string.
|
||||
* @param out the TimingData being modified.
|
||||
* @param line the string in question.
|
||||
* @param rowsPerBeat the number of rows per beat for this purpose. */
|
||||
virtual void ProcessSpeeds( TimingData &out, const RString line, const int rowsPerBeat );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+239
-165
@@ -14,38 +14,6 @@
|
||||
#include "Attack.h"
|
||||
#include "PrefsManager.h"
|
||||
|
||||
/**
|
||||
* @brief A custom .edit file can only be so big before we have to reject it.
|
||||
*/
|
||||
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60 KB
|
||||
|
||||
/**
|
||||
* @brief Attempt to load any background changes in use by this song.
|
||||
* @param change a reference to the background change.
|
||||
* @param sBGChangeExpression a reference to the list of changes to be made.
|
||||
* @return its success or failure.
|
||||
*/
|
||||
bool LoadFromBGSSCChangesString( BackgroundChange &change, const RString &sBGChangeExpression )
|
||||
{
|
||||
return SMLoader::LoadFromBGChangesString( change, sBGChangeExpression );
|
||||
}
|
||||
|
||||
bool SSCLoader::LoadFromDir( const RString &sPath, Song &out )
|
||||
{
|
||||
vector<RString> aFileNames;
|
||||
GetApplicableFiles( sPath, aFileNames );
|
||||
|
||||
if( aFileNames.size() > 1 )
|
||||
{
|
||||
LOG->UserLog( "Song", sPath, "has more than one SSC file. Only one SSC file is allowed per song." );
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT( aFileNames.size() == 1 ); // Ensure one was found entirely.
|
||||
|
||||
return LoadFromSSCFile( sPath + aFileNames[0], out );
|
||||
}
|
||||
|
||||
void SSCLoader::ProcessWarps( TimingData &out, const RString sParam, const float fVersion )
|
||||
{
|
||||
vector<RString> arrayWarpExpressions;
|
||||
@@ -55,10 +23,11 @@ void SSCLoader::ProcessWarps( TimingData &out, const RString sParam, const float
|
||||
{
|
||||
vector<RString> arrayWarpValues;
|
||||
split( arrayWarpExpressions[b], "=", arrayWarpValues );
|
||||
// XXX: Hard to tell which file caused this.
|
||||
if( arrayWarpValues.size() != 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #WARPS value \"%s\" (must have exactly one '='), ignored.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #WARPS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayWarpExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
@@ -68,13 +37,16 @@ void SSCLoader::ProcessWarps( TimingData &out, const RString sParam, const float
|
||||
// Early versions were absolute in beats. They should be relative.
|
||||
if( ( fVersion < VERSION_SPLIT_TIMING && fNewBeat > fBeat ) )
|
||||
{
|
||||
out.AddWarpSegment( WarpSegment(fBeat, fNewBeat - fBeat) );
|
||||
out.AddSegment( SEGMENT_WARP, new WarpSegment(fBeat, fNewBeat - fBeat) );
|
||||
}
|
||||
else if( fNewBeat > 0 )
|
||||
out.AddWarpSegment( WarpSegment(fBeat, fNewBeat) );
|
||||
out.AddSegment( SEGMENT_WARP, new WarpSegment(fBeat, fNewBeat) );
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Warp at beat %f, BPM %f.", fBeat, fNewBeat );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid Warp at beat %f, BPM %f.",
|
||||
fBeat, fNewBeat );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +62,9 @@ void SSCLoader::ProcessLabels( TimingData &out, const RString sParam )
|
||||
split( arrayLabelExpressions[b], "=", arrayLabelValues );
|
||||
if( arrayLabelValues.size() != 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #LABELS value \"%s\" (must have exactly one '='), ignored.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #LABELS value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayLabelExpressions[b].c_str() );
|
||||
continue;
|
||||
}
|
||||
@@ -99,81 +73,40 @@ void SSCLoader::ProcessLabels( TimingData &out, const RString sParam )
|
||||
RString sLabel = arrayLabelValues[1];
|
||||
TrimRight(sLabel);
|
||||
if( fBeat >= 0.0f )
|
||||
out.AddLabelSegment( LabelSegment(fBeat, sLabel) );
|
||||
out.AddSegment( SEGMENT_LABEL, new LabelSegment(fBeat, sLabel) );
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Label at beat %f called %s.", fBeat, sLabel.c_str() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid Label at beat %f called %s.",
|
||||
fBeat, sLabel.c_str() );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void SSCLoader::ProcessCombos( TimingData &out, const RString sParam )
|
||||
void SSCLoader::ProcessCombos( TimingData &out, const RString line, const int rowsPerBeat )
|
||||
{
|
||||
vector<RString> arrayComboExpressions;
|
||||
split( sParam, ",", arrayComboExpressions );
|
||||
split( line, ",", arrayComboExpressions );
|
||||
|
||||
for( unsigned f=0; f<arrayComboExpressions.size(); f++ )
|
||||
{
|
||||
vector<RString> arrayComboValues;
|
||||
split( arrayComboExpressions[f], "=", arrayComboValues );
|
||||
if( arrayComboValues.size() != 2 )
|
||||
unsigned size = arrayComboValues.size();
|
||||
if( size < 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #COMBOS value \"%s\" (must have exactly one '='), ignored.",
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an invalid #COMBOS value \"%s\" (must have at least one '='), ignored.",
|
||||
arrayComboExpressions[f].c_str() );
|
||||
continue;
|
||||
}
|
||||
const float fComboBeat = StringToFloat( arrayComboValues[0] );
|
||||
const int iCombos = StringToInt( arrayComboValues[1] );
|
||||
ComboSegment new_seg( BeatToNoteRow( fComboBeat ), iCombos );
|
||||
out.AddComboSegment( new_seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SSCLoader::ProcessSpeeds( TimingData &out, const RString sParam )
|
||||
{
|
||||
vector<RString> vs1;
|
||||
split( sParam, ",", vs1 );
|
||||
|
||||
FOREACH_CONST( RString, vs1, s1 )
|
||||
{
|
||||
vector<RString> vs2;
|
||||
split( *s1, "=", vs2 );
|
||||
|
||||
if( vs2[0] == 0 && vs2.size() == 2 ) // First one always seems to have 2.
|
||||
{
|
||||
vs2.push_back("0");
|
||||
}
|
||||
|
||||
if( vs2.size() == 3 ) // use beats by default.
|
||||
{
|
||||
vs2.push_back("0");
|
||||
}
|
||||
|
||||
if( vs2.size() < 4 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with %i values.", (int)vs2.size() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( vs2[0] );
|
||||
|
||||
SpeedSegment seg( fBeat, StringToFloat( vs2[1] ), StringToFloat( vs2[2] ));
|
||||
seg.SetUnit(StringToInt(vs2[3]));
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f.", fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( seg.GetLength() < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an speed change with beat %f, length %f.", fBeat, seg.GetLength() );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddSpeedSegment( seg );
|
||||
const int iMisses = (size == 2 ? iCombos : StringToInt(arrayComboValues[2]));
|
||||
out.AddSegment( SEGMENT_COMBO, new ComboSegment( fComboBeat, iCombos, iMisses ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,55 +122,120 @@ void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam )
|
||||
|
||||
if( vs2.size() < 2 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an scroll change with %i values.", (int)vs2.size() );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an scroll change with %i values.",
|
||||
static_cast<int>(vs2.size()) );
|
||||
continue;
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( vs2[0] );
|
||||
|
||||
ScrollSegment seg( fBeat, StringToFloat( vs2[1] ) );
|
||||
ScrollSegment * seg = new ScrollSegment(fBeat, StringToFloat( vs2[1] ) );
|
||||
|
||||
if( fBeat < 0 )
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an scroll change with beat %f.", fBeat );
|
||||
LOG->UserLog("Song file",
|
||||
this->GetSongTitle(),
|
||||
"has an scroll change with beat %f.",
|
||||
fBeat );
|
||||
continue;
|
||||
}
|
||||
|
||||
out.AddScrollSegment( seg );
|
||||
out.AddSegment( SEGMENT_SCROLL, seg );
|
||||
}
|
||||
}
|
||||
|
||||
void SSCLoader::ProcessFakes( TimingData &out, const RString sParam )
|
||||
bool SSCLoader::LoadNoteDataFromSimfile( const RString & cachePath, Steps &out )
|
||||
{
|
||||
vector<RString> arrayFakeExpressions;
|
||||
split( sParam, ",", arrayFakeExpressions );
|
||||
LOG->Trace( "Loading notes from %s", cachePath.c_str() );
|
||||
|
||||
for( unsigned b=0; b<arrayFakeExpressions.size(); b++ )
|
||||
MsdFile msd;
|
||||
if (!msd.ReadFile(cachePath, true))
|
||||
{
|
||||
vector<RString> arrayFakeValues;
|
||||
split( arrayFakeExpressions[b], "=", arrayFakeValues );
|
||||
// XXX: Hard to tell which file caused this.
|
||||
if( arrayFakeValues.size() != 2 )
|
||||
LOG->UserLog("Unable to load any notes from",
|
||||
cachePath,
|
||||
"for this reason: %s",
|
||||
msd.GetError().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tryingSteps = false;
|
||||
float storedVersion = 0;
|
||||
const unsigned values = msd.GetNumValues();
|
||||
|
||||
for (unsigned i = 0; i < values; i++)
|
||||
{
|
||||
const MsdFile::value_t ¶ms = msd.GetValue(i);
|
||||
RString valueName = params[0];
|
||||
valueName.MakeUpper();
|
||||
RString matcher = params[1]; // mainly for debugging.
|
||||
Trim(matcher);
|
||||
|
||||
if (valueName=="VERSION")
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #FAKES value \"%s\" (must have exactly one '='), ignored.",
|
||||
arrayFakeExpressions[b].c_str() );
|
||||
continue;
|
||||
storedVersion = StringToFloat(matcher);
|
||||
}
|
||||
if (tryingSteps)
|
||||
{
|
||||
if( valueName=="STEPSTYPE" )
|
||||
{
|
||||
if (out.m_StepsType != GAMEMAN->StringToStepsType(matcher))
|
||||
tryingSteps = false;
|
||||
}
|
||||
else if( valueName=="CHARTNAME")
|
||||
{
|
||||
if (storedVersion >= VERSION_CHART_NAME_TAG && out.GetChartName() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
else if( valueName=="DESCRIPTION" )
|
||||
{
|
||||
if (storedVersion < VERSION_CHART_NAME_TAG)
|
||||
{
|
||||
if (out.GetChartName() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
else if (out.GetDescription() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="DIFFICULTY" )
|
||||
{
|
||||
if (out.GetDifficulty() != StringToDifficulty(matcher))
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="METER" )
|
||||
{
|
||||
if (out.GetMeter() != StringToInt(matcher))
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="CREDIT" )
|
||||
{
|
||||
if (out.GetCredit() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="NOTES" || valueName=="NOTES2" )
|
||||
{
|
||||
out.SetSMNoteData(matcher);
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const float fBeat = StringToFloat( arrayFakeValues[0] );
|
||||
const float fNewBeat = StringToFloat( arrayFakeValues[1] );
|
||||
|
||||
if(fNewBeat > 0)
|
||||
out.AddFakeSegment( FakeSegment(fBeat, fNewBeat) );
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Fake at beat %f, BPM %f.", fBeat, fNewBeat );
|
||||
if(valueName == "NOTEDATA")
|
||||
{
|
||||
tryingSteps = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCache )
|
||||
bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromSSCFile(%s)", sPath.c_str() );
|
||||
|
||||
@@ -249,6 +247,7 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
}
|
||||
|
||||
out.m_SongTiming.m_sFile = sPath; // songs still have their fallback timing.
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
int state = GETTING_SONG_INFO;
|
||||
const unsigned values = msd.GetNumValues();
|
||||
@@ -274,6 +273,7 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
else if( sValueName=="TITLE" )
|
||||
{
|
||||
out.m_sMainTitle = sParams[1];
|
||||
this->SetSongTitle(sParams[1]);
|
||||
}
|
||||
|
||||
else if( sValueName=="SUBTITLE" )
|
||||
@@ -355,7 +355,12 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
|
||||
else if( sValueName=="LASTBEATHINT" )
|
||||
{
|
||||
out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] );
|
||||
// unable to parse due to tag position. Ignore.
|
||||
}
|
||||
|
||||
else if (sValueName == "LASTSECONDHINT")
|
||||
{
|
||||
out.SetSpecifiedLastSecond(StringToFloat(sParams[1]));
|
||||
}
|
||||
|
||||
else if( sValueName=="MUSICBYTES" )
|
||||
@@ -421,7 +426,7 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
for( unsigned b=0; b<aFGChangeExpressions.size(); b++ )
|
||||
{
|
||||
BackgroundChange change;
|
||||
if( LoadFromBGSSCChangesString( change, aFGChangeExpressions[b] ) )
|
||||
if( LoadFromBGChangesString( change, aFGChangeExpressions[b] ) )
|
||||
out.AddForegroundChange( change );
|
||||
}
|
||||
}
|
||||
@@ -434,7 +439,8 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
// Attacks loaded from file
|
||||
else if( sValueName=="ATTACKS" )
|
||||
{
|
||||
SMLoader::ProcessAttacks( out, sParams );
|
||||
ProcessAttackString(out.m_sAttackString, sParams);
|
||||
ProcessAttacks(out.m_Attacks, sParams);
|
||||
}
|
||||
|
||||
else if( sValueName=="OFFSET" )
|
||||
@@ -484,16 +490,20 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
|
||||
/* The following are cache tags. Never fill their values
|
||||
* directly: only from the cached version. */
|
||||
else if( sValueName=="FIRSTBEAT" )
|
||||
else if( sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" )
|
||||
{
|
||||
// no longer used.
|
||||
}
|
||||
else if (sValueName=="FIRSTSECOND")
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_fFirstBeat = StringToFloat( sParams[1] );
|
||||
out.SetFirstSecond(StringToFloat(sParams[1]));
|
||||
}
|
||||
|
||||
else if( sValueName=="LASTBEAT" )
|
||||
else if( sValueName=="LASTSECOND" )
|
||||
{
|
||||
if( bFromCache )
|
||||
out.m_fLastBeat = StringToFloat( sParams[1] );
|
||||
out.SetLastSecond(StringToFloat(sParams[1]));
|
||||
}
|
||||
|
||||
else if( sValueName=="SONGFILENAME" )
|
||||
@@ -526,6 +536,10 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
}
|
||||
case GETTING_STEP_INFO:
|
||||
{
|
||||
if (sValueName == "CHARTNAME")
|
||||
{
|
||||
pNewNotes->SetChartName(sParams[1]);
|
||||
}
|
||||
if( sValueName=="STEPSTYPE" )
|
||||
{
|
||||
pNewNotes->m_StepsType = GAMEMAN->StringToStepsType( sParams[1] );
|
||||
@@ -538,7 +552,14 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
|
||||
else if( sValueName=="DESCRIPTION" )
|
||||
{
|
||||
pNewNotes->SetDescription( sParams[1] );
|
||||
if (out.m_fVersion < VERSION_CHART_NAME_TAG)
|
||||
{
|
||||
pNewNotes->SetChartName(sParams[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
pNewNotes->SetDescription(sParams[1]);
|
||||
}
|
||||
}
|
||||
|
||||
else if( sValueName=="DIFFICULTY" )
|
||||
@@ -553,27 +574,35 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
|
||||
else if( sValueName=="RADARVALUES" )
|
||||
{
|
||||
vector<RString> saValues;
|
||||
split( sParams[1], ",", saValues, true );
|
||||
|
||||
int categories = NUM_RadarCategory;
|
||||
if( out.m_fVersion < VERSION_RADAR_FAKE )
|
||||
categories -= 1;
|
||||
|
||||
if( saValues.size() == (unsigned)categories * NUM_PLAYERS )
|
||||
if (bFromCache)
|
||||
{
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
vector<RString> saValues;
|
||||
split( sParams[1], ",", saValues, true );
|
||||
|
||||
int categories = NUM_RadarCategory;
|
||||
if( out.m_fVersion < VERSION_RADAR_FAKE )
|
||||
categories -= 1;
|
||||
|
||||
if( saValues.size() == (unsigned)categories * NUM_PLAYERS )
|
||||
{
|
||||
// Can't use the foreach anymore due to flexible radar lines.
|
||||
for( RadarCategory rc = (RadarCategory)0; rc < categories;
|
||||
enum_add<RadarCategory>( rc, +1 ) )
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
v[pn][rc] = StringToFloat( saValues[pn*categories + rc] );
|
||||
// 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] );
|
||||
}
|
||||
}
|
||||
pNewNotes->SetCachedRadarValues( v );
|
||||
}
|
||||
pNewNotes->SetCachedRadarValues( v );
|
||||
}
|
||||
else
|
||||
{
|
||||
// just recalc at time.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if( sValueName=="CREDIT" )
|
||||
@@ -588,6 +617,7 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
pNewNotes->m_Timing = stepsTiming;
|
||||
pNewNotes->SetSMNoteData( sParams[1] );
|
||||
pNewNotes->TidyUpData();
|
||||
pNewNotes->SetFilename(sPath);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
|
||||
@@ -649,13 +679,39 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
|
||||
else if( sValueName=="ATTACKS" )
|
||||
{
|
||||
// Step Attacks aren't in yet.
|
||||
ProcessAttackString(pNewNotes->m_sAttackString, sParams);
|
||||
ProcessAttacks(pNewNotes->m_Attacks, sParams);
|
||||
}
|
||||
|
||||
else if( sValueName=="OFFSET" )
|
||||
{
|
||||
stepsTiming.m_fBeat0OffsetInSeconds = StringToFloat( sParams[1] );
|
||||
}
|
||||
|
||||
else if( sValueName=="DISPLAYBPM" )
|
||||
{
|
||||
// #DISPLAYBPM:[xxx][xxx:xxx]|[*];
|
||||
if( sParams[1] == "*" )
|
||||
pNewNotes->SetDisplayBPM(DISPLAY_BPM_RANDOM);
|
||||
else
|
||||
{
|
||||
pNewNotes->SetDisplayBPM(DISPLAY_BPM_SPECIFIED);
|
||||
float min = StringToFloat(sParams[1]);
|
||||
pNewNotes->SetMinBPM(min);
|
||||
if(sParams[2].empty())
|
||||
pNewNotes->SetMaxBPM(min);
|
||||
else
|
||||
pNewNotes->SetMaxBPM(StringToFloat(sParams[2]));
|
||||
}
|
||||
}
|
||||
else if( sValueName=="STEPFILENAME" )
|
||||
{
|
||||
state = GETTING_SONG_INFO;
|
||||
if( bHasOwnTiming )
|
||||
pNewNotes->m_Timing = stepsTiming;
|
||||
pNewNotes->SetFilename(sParams[1]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -665,11 +721,6 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSCLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
{
|
||||
GetDirListing( sPath + RString("*.ssc"), out );
|
||||
}
|
||||
|
||||
bool SSCLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong )
|
||||
{
|
||||
LOG->Trace( "SSCLoader::LoadEditFromFile(%s)", sEditFilePath.c_str() );
|
||||
@@ -677,21 +728,28 @@ bool SSCLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool
|
||||
int iBytes = FILEMAN->GetFileSizeInBytes( sEditFilePath );
|
||||
if( iBytes > MAX_EDIT_STEPS_SIZE_BYTES )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "is unreasonably large. It won't be loaded." );
|
||||
LOG->UserLog("Edit file",
|
||||
sEditFilePath,
|
||||
"is unreasonably large. It won't be loaded." );
|
||||
return false;
|
||||
}
|
||||
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( sEditFilePath, true ) ) // unescape
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "couldn't be opened: %s", msd.GetError().c_str() );
|
||||
LOG->UserLog("Edit file",
|
||||
sEditFilePath,
|
||||
"couldn't be opened: %s", msd.GetError().c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadEditFromMsd( msd, sEditFilePath, slot, bAddStepsToSong );
|
||||
}
|
||||
|
||||
bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong )
|
||||
bool SSCLoader::LoadEditFromMsd(const MsdFile &msd,
|
||||
const RString &sEditFilePath,
|
||||
ProfileSlot slot,
|
||||
bool bAddStepsToSong )
|
||||
{
|
||||
Song* pSong = NULL;
|
||||
Steps* pNewNotes = NULL;
|
||||
@@ -721,18 +779,25 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
}
|
||||
|
||||
RString sSongFullTitle = sParams[1];
|
||||
this->SetSongTitle(sParams[1]);
|
||||
sSongFullTitle.Replace( '\\', '/' );
|
||||
|
||||
pSong = SONGMAN->FindSong( sSongFullTitle );
|
||||
if( pSong == NULL )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "requires a song \"%s\" that isn't present.", sSongFullTitle.c_str() );
|
||||
LOG->UserLog("Edit file",
|
||||
sEditFilePath,
|
||||
"requires a song \"%s\" that isn't present.",
|
||||
sSongFullTitle.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( pSong->GetNumStepsLoadedFromProfile(slot) >= MAX_EDITS_PER_SONG_PER_PROFILE )
|
||||
{
|
||||
LOG->UserLog( "Song file", sSongFullTitle, "already has the maximum number of edits allowed for ProfileSlotP%d.", slot+1 );
|
||||
LOG->UserLog("Song file",
|
||||
sSongFullTitle,
|
||||
"already has the maximum number of edits allowed for ProfileSlotP%d.",
|
||||
slot+1 );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -780,8 +845,8 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
{
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
FOREACH_ENUM( RadarCategory, rc )
|
||||
v[pn][rc] = StringToFloat( saValues[pn*NUM_RadarCategory + rc] );
|
||||
FOREACH_ENUM( RadarCategory, rc )
|
||||
v[pn][rc] = StringToFloat( saValues[pn*NUM_RadarCategory + rc] );
|
||||
pNewNotes->SetCachedRadarValues( v );
|
||||
}
|
||||
bSSCFormat = true;
|
||||
@@ -857,13 +922,18 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
{
|
||||
if( pSong == NULL )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "doesn't have a #SONG tag preceeding the first #NOTES tag." );
|
||||
LOG->UserLog("Edit file",
|
||||
sEditFilePath,
|
||||
"doesn't have a #SONG tag preceeding the first #NOTES tag." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !bSSCFormat && iNumParams < 7 )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "has %d fields in a #NOTES tag, but should have at least 7.", iNumParams );
|
||||
LOG->UserLog("Edit file",
|
||||
sEditFilePath,
|
||||
"has %d fields in a #NOTES tag, but should have at least 7.",
|
||||
iNumParams );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -883,9 +953,13 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
else
|
||||
{
|
||||
pNewNotes = pSong->CreateSteps();
|
||||
SMLoader::LoadFromSMTokens(
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6],
|
||||
*pNewNotes);
|
||||
LoadFromTokens(sParams[1],
|
||||
sParams[2],
|
||||
sParams[3],
|
||||
sParams[4],
|
||||
sParams[5],
|
||||
sParams[6],
|
||||
*pNewNotes);
|
||||
}
|
||||
|
||||
pNewNotes->SetLoadedFromProfile( slot );
|
||||
@@ -894,7 +968,9 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
|
||||
if( pSong->IsEditAlreadyLoaded(pNewNotes) )
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "is a duplicate of another edit that was already loaded." );
|
||||
LOG->UserLog("Edit file",
|
||||
sEditFilePath,
|
||||
"is a duplicate of another edit that was already loaded." );
|
||||
SAFE_DELETE( pNewNotes );
|
||||
return false;
|
||||
}
|
||||
@@ -904,7 +980,10 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->UserLog( "Edit file", sEditFilePath, "has an unexpected value \"%s\".", sValueName.c_str() );
|
||||
LOG->UserLog("Edit file",
|
||||
sEditFilePath,
|
||||
"has an unexpected value \"%s\".",
|
||||
sValueName.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,11 +992,6 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat
|
||||
return bSSCFormat;
|
||||
}
|
||||
|
||||
void SSCLoader::TidyUpData( Song &song, bool bFromCache )
|
||||
{
|
||||
SMLoader::TidyUpData(song, bFromCache);
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2011 Jason Felds
|
||||
* All rights reserved.
|
||||
|
||||
+21
-25
@@ -3,6 +3,7 @@
|
||||
#define NotesLoaderSSC_H
|
||||
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "NotesLoaderSM.h"
|
||||
|
||||
class MsdFile;
|
||||
class Song;
|
||||
@@ -25,19 +26,20 @@ const float VERSION_RADAR_FAKE = 0.53f;
|
||||
const float VERSION_WARP_SEGMENT = 0.56f;
|
||||
/** @brief The version that formally introduced Split Timing. */
|
||||
const float VERSION_SPLIT_TIMING = 0.7f;
|
||||
/** @brief The version that moved the step's Offset higher up. */
|
||||
const float VERSION_OFFSET_BEFORE_ATTACK = 0.72f;
|
||||
/** @brief The version that introduced the Chart Name tag. */
|
||||
const float VERSION_CHART_NAME_TAG = 0.74f;
|
||||
/** @brief The version that introduced the cache switch tag. */
|
||||
const float VERSION_CACHE_SWITCH_TAG = 0.77f;
|
||||
|
||||
/**
|
||||
* @brief The SSCLoader handles all of the parsing needed for .ssc files.
|
||||
*/
|
||||
namespace SSCLoader
|
||||
struct SSCLoader : public SMLoader
|
||||
{
|
||||
/**
|
||||
* @brief Attempt to load a song from a specified path.
|
||||
* @param sPath a const reference to the path on the hard drive to check.
|
||||
* @param out a reference to the Song that will retrieve the song information.
|
||||
* @return its success or failure.
|
||||
*/
|
||||
bool LoadFromDir( const RString &sPath, Song &out );
|
||||
SSCLoader() : SMLoader(".ssc") {}
|
||||
|
||||
/**
|
||||
* @brief Attempt to load the specified ssc file.
|
||||
* @param sPath a const reference to the path on the hard drive to check.
|
||||
@@ -45,13 +47,8 @@ namespace SSCLoader
|
||||
* @param bFromCache a check to see if we are getting certain information from the cache file.
|
||||
* @return its success or failure.
|
||||
*/
|
||||
bool LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCache = false );
|
||||
/**
|
||||
* @brief Retrieve the list of .ssc files.
|
||||
* @param sPath a const reference to the path on the hard drive to check.
|
||||
* @param out a vector of files found in the path.
|
||||
*/
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
virtual bool LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache = false );
|
||||
|
||||
/**
|
||||
* @brief Attempt to load an edit from the hard drive.
|
||||
* @param sEditFilePath a path on the hard drive to check.
|
||||
@@ -69,21 +66,20 @@ namespace SSCLoader
|
||||
* @return its success or failure.
|
||||
*/
|
||||
bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
/**
|
||||
* @brief Perform some cleanup on the loaded song.
|
||||
* @param song a reference to the song that may need cleaning up.
|
||||
* @param bFromCache a flag to determine if this song is loaded from a cache file.
|
||||
*/
|
||||
void TidyUpData( Song &song, bool bFromCache );
|
||||
|
||||
/**
|
||||
* @brief Retrieve the specific NoteData from the file.
|
||||
* @param cachePath the path to the cache file.
|
||||
* @param out the Steps to receive just the particular notedata.
|
||||
* @return true if successful, false otherwise. */
|
||||
virtual bool LoadNoteDataFromSimfile( const RString &cachePath, Steps &out );
|
||||
|
||||
void ProcessWarps( TimingData &, const RString, const float );
|
||||
void ProcessLabels( TimingData &, const RString );
|
||||
void ProcessCombos( TimingData &, const RString );
|
||||
void ProcessSpeeds( TimingData &, const RString );
|
||||
virtual void ProcessCombos( TimingData &, const RString, const int = -1 );
|
||||
void ProcessScrolls( TimingData &, const RString );
|
||||
void ProcessFakes( TimingData &, const RString );
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
/**
|
||||
* @file
|
||||
|
||||
+20
-13
@@ -11,6 +11,8 @@
|
||||
#include "Song.h"
|
||||
#include "Steps.h"
|
||||
|
||||
RString OptimizeDWIString( RString holds, RString taps );
|
||||
|
||||
/**
|
||||
* @brief Optimize an individual pair of characters whenever possible.
|
||||
* @param c1 the first character.
|
||||
@@ -350,9 +352,13 @@ 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_SongTiming.m_BPMSegments[0].GetRow() == 0 );
|
||||
|
||||
const vector<TimingSegment *> &bpms = out.m_SongTiming.allTimingSegments[SEGMENT_BPM];
|
||||
|
||||
ASSERT_M(bpms[0]->GetRow() == 0,
|
||||
ssprintf("The first BPM Segment must be defined at row 0, not %d!", bpms[0]->GetRow()) );
|
||||
f.PutLine( ssprintf("#FILE:%s;", DwiEscape(out.m_sMusicFile).c_str()) );
|
||||
f.PutLine( ssprintf("#BPM:%.3f;", out.m_SongTiming.m_BPMSegments[0].GetBPM()) );
|
||||
f.PutLine( ssprintf("#BPM:%.3f;", static_cast<BPMSegment *>(bpms[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) );
|
||||
@@ -374,29 +380,30 @@ bool NotesWriterDWI::Write( RString sPath, const Song &out )
|
||||
break;
|
||||
}
|
||||
|
||||
if( !out.m_SongTiming.m_StopSegments.empty() )
|
||||
const vector<TimingSegment *> &stops = out.m_SongTiming.allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
if( !stops.empty() )
|
||||
{
|
||||
f.Write( "#FREEZE:" );
|
||||
|
||||
for( unsigned i=0; i<out.m_SongTiming.m_StopSegments.size(); i++ )
|
||||
for( unsigned i=0; i<stops.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_SongTiming.m_StopSegments[i];
|
||||
f.Write( ssprintf("%.3f=%.3f", fs.GetRow() * 4.0f / ROWS_PER_BEAT,
|
||||
roundf(fs.GetPause()*1000)) );
|
||||
if( i != out.m_SongTiming.m_StopSegments.size()-1 )
|
||||
const StopSegment *fs = static_cast<StopSegment *>(stops[i]);
|
||||
f.Write( ssprintf("%.3f=%.3f", fs->GetRow() * 4.0f / ROWS_PER_BEAT,
|
||||
roundf(fs->GetPause()*1000)) );
|
||||
if( i != stops.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
if( out.m_SongTiming.m_BPMSegments.size() > 1)
|
||||
if( bpms.size() > 1)
|
||||
{
|
||||
f.Write( "#CHANGEBPM:" );
|
||||
for( unsigned i=1; i<out.m_SongTiming.m_BPMSegments.size(); i++ )
|
||||
for( unsigned i=1; i<bpms.size(); i++ )
|
||||
{
|
||||
const BPMSegment &bs = out.m_SongTiming.m_BPMSegments[i];
|
||||
f.Write( ssprintf("%.3f=%.3f", bs.GetRow() * 4.0f / ROWS_PER_BEAT, bs.GetBPM() ) );
|
||||
if( i != out.m_SongTiming.m_BPMSegments.size()-1 )
|
||||
const BPMSegment *bs = static_cast<BPMSegment *>(bpms[i]);
|
||||
f.Write( ssprintf("%.3f=%.3f", bs->GetRow() * 4.0f / ROWS_PER_BEAT, bs->GetBPM() ) );
|
||||
if( i != bpms.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
+13
-12
@@ -9,22 +9,23 @@
|
||||
#include "NoteData.h"
|
||||
#include "GameManager.h"
|
||||
|
||||
void Serialize(const BPMSegment &seg, Json::Value &root)
|
||||
static void Serialize(const TimingSegment &seg, Json::Value &root)
|
||||
{
|
||||
root["Beat"] = seg.GetBeat();
|
||||
root["BPM"] = seg.GetBPM();
|
||||
}
|
||||
|
||||
static void Serialize(const StopSegment &seg, Json::Value &root)
|
||||
{
|
||||
root["Beat"] = seg.GetBeat();
|
||||
root["Seconds"] = seg.GetPause();
|
||||
if (seg.GetType() == SEGMENT_BPM)
|
||||
{
|
||||
root["BPM"] = static_cast<BPMSegment &>(const_cast<TimingSegment &>(seg)).GetBPM();
|
||||
}
|
||||
else
|
||||
{
|
||||
root["Seconds"] = static_cast<StopSegment &>(const_cast<TimingSegment &>(seg)).GetPause();
|
||||
}
|
||||
}
|
||||
|
||||
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"] );
|
||||
JsonUtil::SerializeVectorPointers( td.allTimingSegments[SEGMENT_BPM], Serialize, root["BpmSegments"] );
|
||||
JsonUtil::SerializeVectorPointers( td.allTimingSegments[SEGMENT_STOP_DELAY], Serialize, root["StopSegments"] );
|
||||
}
|
||||
|
||||
static void Serialize(const LyricSegment &o, Json::Value &root)
|
||||
@@ -147,8 +148,8 @@ bool NotesWriterJson::WriteSong( const RString &sFile, const Song &out, bool bWr
|
||||
else
|
||||
root["Selectable"] = "YES";
|
||||
|
||||
root["FirstBeat"] = out.m_fFirstBeat;
|
||||
root["LastBeat"] = out.m_fLastBeat;
|
||||
root["FirstBeat"] = out.GetFirstBeat();
|
||||
root["LastBeat"] = out.GetLastBeat();
|
||||
root["SongFileName"] = out.m_sSongFileName;
|
||||
root["HasMusic"] = out.m_bHasMusic;
|
||||
root["HasBanner"] = out.m_bHasBanner;
|
||||
|
||||
+29
-59
@@ -19,37 +19,13 @@
|
||||
|
||||
ThemeMetric<bool> USE_CREDIT ( "NotesWriterSM", "DescriptionUsesCreditField" );
|
||||
|
||||
/**
|
||||
* @brief Turn the BackgroundChange into a string.
|
||||
* @param bgc the BackgroundChange in question.
|
||||
* @return the converted string. */
|
||||
static RString BackgroundChangeToString( const BackgroundChange &bgc )
|
||||
{
|
||||
// TODO: Technically we need to double-escape the filename (because it might
|
||||
// contain '=') and then unescape the value returned by the MsdFile.
|
||||
RString s = ssprintf(
|
||||
"%.3f=%s=%.3f=%d=%d=%d=%s=%s=%s=%s=%s",
|
||||
bgc.m_fStartBeat,
|
||||
SmEscape(bgc.m_def.m_sFile1).c_str(),
|
||||
bgc.m_fRate,
|
||||
bgc.m_sTransition == SBT_CrossFade, // backward compat
|
||||
bgc.m_def.m_sEffect == SBE_StretchRewind, // backward compat
|
||||
bgc.m_def.m_sEffect != SBE_StretchNoLoop, // backward compat
|
||||
bgc.m_def.m_sEffect.c_str(),
|
||||
bgc.m_def.m_sFile2.c_str(),
|
||||
bgc.m_sTransition.c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(bgc.m_def.m_sColor1)).c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(bgc.m_def.m_sColor2)).c_str()
|
||||
);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, Song &out )
|
||||
{
|
||||
TimingData &timing = out.m_SongTiming;
|
||||
f.PutLine( ssprintf( "#TITLE:%s;", SmEscape(out.m_sMainTitle).c_str() ) );
|
||||
f.PutLine( ssprintf( "#SUBTITLE:%s;", SmEscape(out.m_sSubTitle).c_str() ) );
|
||||
f.PutLine( ssprintf( "#ARTIST:%s;", SmEscape(out.m_sArtist).c_str() ) );
|
||||
@@ -66,8 +42,9 @@ static void WriteGlobalTags( RageFile &f, Song &out )
|
||||
f.PutLine( ssprintf( "#OFFSET:%.3f;", out.m_SongTiming.m_fBeat0OffsetInSeconds ) );
|
||||
f.PutLine( ssprintf( "#SAMPLESTART:%.3f;", out.m_fMusicSampleStartSeconds ) );
|
||||
f.PutLine( ssprintf( "#SAMPLELENGTH:%.3f;", out.m_fMusicSampleLengthSeconds ) );
|
||||
if( out.m_fSpecifiedLastBeat > 0 )
|
||||
f.PutLine( ssprintf("#LASTBEATHINT:%.3f;", out.m_fSpecifiedLastBeat) );
|
||||
float specBeat = out.GetSpecifiedLastBeat();
|
||||
if( specBeat > 0 )
|
||||
f.PutLine( ssprintf("#LASTBEATHINT:%.3f;", specBeat) );
|
||||
|
||||
f.Write( "#SELECTABLE:" );
|
||||
switch(out.m_SelectionDisplay)
|
||||
@@ -98,44 +75,46 @@ static void WriteGlobalTags( RageFile &f, Song &out )
|
||||
|
||||
|
||||
f.Write( "#BPMS:" );
|
||||
for( unsigned i=0; i<out.m_SongTiming.m_BPMSegments.size(); i++ )
|
||||
vector<TimingSegment *> &bpms = timing.allTimingSegments[SEGMENT_BPM];
|
||||
for( unsigned i=0; i<bpms.size(); i++ )
|
||||
{
|
||||
const BPMSegment &bs = out.m_SongTiming.m_BPMSegments[i];
|
||||
const BPMSegment *bs = static_cast<BPMSegment *>(bpms[i]);
|
||||
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", bs.GetBeat(), bs.GetBPM() ) );
|
||||
if( i != out.m_SongTiming.m_BPMSegments.size()-1 )
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", bs->GetBeat(), bs->GetBPM() ) );
|
||||
if( i != bpms.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
unsigned wSize = out.m_SongTiming.m_WarpSegments.size();
|
||||
vector<TimingSegment *> &warps = timing.allTimingSegments[SEGMENT_WARP];
|
||||
unsigned wSize = warps.size();
|
||||
if( wSize > 0 )
|
||||
{
|
||||
for( unsigned i=0; i < wSize; i++ )
|
||||
{
|
||||
int iRow = out.m_SongTiming.m_WarpSegments[i].GetRow();
|
||||
const WarpSegment *ws = static_cast<WarpSegment *>(warps[i]);
|
||||
int iRow = ws->GetRow();
|
||||
float fBPS = 60 / out.m_SongTiming.GetBPMAtRow(iRow);
|
||||
float fSkip = fBPS * out.m_SongTiming.m_WarpSegments[i].GetLength();
|
||||
StopSegment ss(iRow, -fSkip, false);
|
||||
out.m_SongTiming.AddStopSegment( ss );
|
||||
float fSkip = fBPS * ws->GetLength();
|
||||
out.m_SongTiming.AddSegment(SEGMENT_STOP_DELAY,
|
||||
new StopSegment(iRow, -fSkip, false) );
|
||||
}
|
||||
}
|
||||
|
||||
f.Write( "#STOPS:" );
|
||||
for( unsigned i=0; i<out.m_SongTiming.m_StopSegments.size(); i++ )
|
||||
vector<TimingSegment *> &stops = timing.allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
for( unsigned i=0; i<stops.size(); i++ )
|
||||
{
|
||||
const StopSegment &fs = out.m_SongTiming.m_StopSegments[i];
|
||||
float fBeat = fs.GetBeat();
|
||||
if (fs.GetDelay()) fBeat--;
|
||||
const StopSegment *fs = static_cast<StopSegment *>(stops[i]);
|
||||
float fBeat = fs->GetBeat();
|
||||
if (fs->GetDelay()) fBeat--;
|
||||
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", fBeat, fs.GetPause() ) );
|
||||
if( i != out.m_SongTiming.m_StopSegments.size()-1 )
|
||||
f.PutLine( ssprintf( "%.3f=%.3f", fBeat, fs->GetPause() ) );
|
||||
if( i != stops.size()-1 )
|
||||
f.Write( "," );
|
||||
if( fs.GetPause() < 0 )
|
||||
if( fs->GetPause() < 0 )
|
||||
{
|
||||
out.m_SongTiming.m_StopSegments.erase(
|
||||
out.m_SongTiming.m_StopSegments.begin()+i,
|
||||
out.m_SongTiming.m_StopSegments.begin()+i+1 );
|
||||
stops.erase(stops.begin()+i,stops.begin()+i+1 );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +130,7 @@ static void WriteGlobalTags( RageFile &f, Song &out )
|
||||
f.Write( ssprintf("#BGCHANGES%d:", b+1) );
|
||||
|
||||
FOREACH_CONST( BackgroundChange, out.GetBackgroundChanges(b), bgc )
|
||||
f.PutLine( BackgroundChangeToString(*bgc)+"," );
|
||||
f.PutLine( (*bgc).ToString() +"," );
|
||||
|
||||
/* If there's an animation plan at all, add a dummy "-nosongbg-" tag to indicate that
|
||||
* this file doesn't want a song BG entry added at the end. See SMLoader::TidyUpData.
|
||||
@@ -167,7 +146,7 @@ static void WriteGlobalTags( RageFile &f, Song &out )
|
||||
f.Write( "#FGCHANGES:" );
|
||||
FOREACH_CONST( BackgroundChange, out.GetForegroundChanges(), bgc )
|
||||
{
|
||||
f.PutLine( BackgroundChangeToString(*bgc)+"," );
|
||||
f.PutLine( (*bgc).ToString() +"," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
@@ -181,16 +160,7 @@ static void WriteGlobalTags( RageFile &f, Song &out )
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#ATTACKS:" );
|
||||
for( unsigned a=0; a < out.m_sAttackString.size(); a++ )
|
||||
{
|
||||
RString sData = out.m_sAttackString[a];
|
||||
f.Write( ssprintf( "%s", sData.c_str() ) );
|
||||
|
||||
if( a != (out.m_sAttackString.size() - 1) )
|
||||
f.Write( ":" ); // Not the end, so write a divider ':'
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
f.PutLine( ssprintf("#ATTACKS:%s;", out.GetAttackString().c_str()) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +195,7 @@ static RString GetSMNotesTag( const Song &song, const Steps &in )
|
||||
GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName, SmEscape(in.GetDescription()).c_str()) );
|
||||
lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" );
|
||||
lines.push_back( ssprintf( " %s:", GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName ) );
|
||||
RString desc = (USE_CREDIT ? in.GetCredit() : in.GetDescription());
|
||||
RString desc = (USE_CREDIT ? in.GetCredit() : in.GetChartName());
|
||||
lines.push_back( ssprintf( " %s:", SmEscape(desc).c_str() ) );
|
||||
lines.push_back( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) );
|
||||
lines.push_back( ssprintf( " %d:", in.GetMeter() ) );
|
||||
|
||||
+134
-81
@@ -16,31 +16,6 @@
|
||||
#include "Song.h"
|
||||
#include "Steps.h"
|
||||
|
||||
/**
|
||||
* @brief Turn the BackgroundChange into a string.
|
||||
* @param bgc the BackgroundChange in question.
|
||||
* @return the converted string. */
|
||||
static RString BackgroundChangeToString( const BackgroundChange &bgc )
|
||||
{
|
||||
// TODO: Technically we need to double-escape the filename (because it might contain '=') and then
|
||||
// unescape the value returned by the MsdFile.
|
||||
RString s = ssprintf(
|
||||
"%.3f=%s=%.3f=%d=%d=%d=%s=%s=%s=%s=%s",
|
||||
bgc.m_fStartBeat,
|
||||
SmEscape(bgc.m_def.m_sFile1).c_str(),
|
||||
bgc.m_fRate,
|
||||
bgc.m_sTransition == SBT_CrossFade, // backward compat
|
||||
bgc.m_def.m_sEffect == SBE_StretchRewind, // backward compat
|
||||
bgc.m_def.m_sEffect != SBE_StretchNoLoop, // backward compat
|
||||
bgc.m_def.m_sEffect.c_str(),
|
||||
bgc.m_def.m_sFile2.c_str(),
|
||||
bgc.m_sTransition.c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(bgc.m_def.m_sColor1)).c_str(),
|
||||
SmEscape(RageColor::NormalizeColorString(bgc.m_def.m_sColor2)).c_str()
|
||||
);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn a vector of lines into a single line joined by newline characters.
|
||||
* @param lines the list of lines to join.
|
||||
@@ -90,80 +65,144 @@ static void GetTimingTags( vector<RString> &lines, TimingData timing, bool bIsSo
|
||||
TimingTagWriter w ( &lines );
|
||||
|
||||
timing.TidyUpData();
|
||||
unsigned i = 0;
|
||||
|
||||
w.Init( "BPMS" );
|
||||
FOREACH_CONST( BPMSegment, timing.m_BPMSegments, bs )
|
||||
vector<TimingSegment *> &bpms = timing.allTimingSegments[SEGMENT_BPM];
|
||||
for (; i < bpms.size(); i++)
|
||||
{
|
||||
BPMSegment *bs = static_cast<BPMSegment *>(bpms[i]);
|
||||
w.Write( bs->GetRow(), bs->GetBPM() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
w.Init( "STOPS" );
|
||||
FOREACH_CONST( StopSegment, timing.m_StopSegments, ss )
|
||||
vector<TimingSegment *> &stops = timing.allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
for (i = 0; i < stops.size(); i++)
|
||||
{
|
||||
StopSegment *ss = static_cast<StopSegment *>(stops[i]);
|
||||
if( !ss->GetDelay() )
|
||||
w.Write( ss->GetRow(), ss->GetPause() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
w.Init( "DELAYS" );
|
||||
FOREACH_CONST( StopSegment, timing.m_StopSegments, ss )
|
||||
for (i = 0; i < stops.size(); i++)
|
||||
{
|
||||
StopSegment *ss = static_cast<StopSegment *>(stops[i]);
|
||||
if( ss->GetDelay() )
|
||||
w.Write( ss->GetRow(), ss->GetPause() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
w.Init( "WARPS" );
|
||||
FOREACH_CONST( WarpSegment, timing.m_WarpSegments, ws )
|
||||
vector<TimingSegment *> &warps = timing.allTimingSegments[SEGMENT_WARP];
|
||||
for (i = 0; i < warps.size(); i++)
|
||||
{
|
||||
WarpSegment *ws = static_cast<WarpSegment *>(warps[i]);
|
||||
w.Write( ws->GetRow(), ws->GetLength() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
ASSERT( !timing.m_vTimeSignatureSegments.empty() );
|
||||
vector<TimingSegment *> &tSigs = timing.allTimingSegments[SEGMENT_TIME_SIG];
|
||||
ASSERT( !tSigs.empty() );
|
||||
w.Init( "TIMESIGNATURES" );
|
||||
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, iter )
|
||||
w.Write( iter->GetRow(), iter->GetNum(), iter->GetDen() );
|
||||
for (i = 0; i < tSigs.size(); i++)
|
||||
{
|
||||
TimeSignatureSegment *ts = static_cast<TimeSignatureSegment *>(tSigs[i]);
|
||||
w.Write( ts->GetRow(), ts->GetNum(), ts->GetDen() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
ASSERT( !timing.m_TickcountSegments.empty() );
|
||||
vector<TimingSegment *> &ticks = timing.allTimingSegments[SEGMENT_TICKCOUNT];
|
||||
ASSERT( !ticks.empty() );
|
||||
w.Init( "TICKCOUNTS" );
|
||||
FOREACH_CONST( TickcountSegment, timing.m_TickcountSegments, ts )
|
||||
for (i = 0; i < ticks.size(); i++)
|
||||
{
|
||||
TickcountSegment *ts = static_cast<TickcountSegment *>(ticks[i]);
|
||||
w.Write( ts->GetRow(), ts->GetTicks() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
ASSERT( !timing.m_ComboSegments.empty() );
|
||||
vector<TimingSegment *> &combos = timing.allTimingSegments[SEGMENT_COMBO];
|
||||
ASSERT( !combos.empty() );
|
||||
w.Init( "COMBOS" );
|
||||
FOREACH_CONST( ComboSegment, timing.m_ComboSegments, cs )
|
||||
w.Write( cs->GetRow(), cs->GetCombo() );
|
||||
for (i = 0; i < combos.size(); i++)
|
||||
{
|
||||
ComboSegment *cs = static_cast<ComboSegment *>(combos[i]);
|
||||
if (cs->GetCombo() == cs->GetMissCombo())
|
||||
w.Write( cs->GetRow(), cs->GetCombo() );
|
||||
else
|
||||
w.Write( cs->GetRow(), cs->GetCombo(), cs->GetMissCombo() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
// Song Timing should only have the initial value.
|
||||
vector<TimingSegment *> &speeds = timing.allTimingSegments[SEGMENT_SPEED];
|
||||
w.Init( "SPEEDS" );
|
||||
FOREACH_CONST( SpeedSegment, timing.m_SpeedSegments, ss )
|
||||
for (i = 0; i < speeds.size(); i++)
|
||||
{
|
||||
SpeedSegment *ss = static_cast<SpeedSegment *>(speeds[i]);
|
||||
w.Write( ss->GetRow(), ss->GetRatio(), ss->GetLength(), ss->GetUnit() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
w.Init( "SCROLLS" );
|
||||
FOREACH_CONST( ScrollSegment, timing.m_ScrollSegments, ss )
|
||||
vector<TimingSegment *> &scrolls = timing.allTimingSegments[SEGMENT_SCROLL];
|
||||
for (i = 0; i < scrolls.size(); i++)
|
||||
{
|
||||
ScrollSegment *ss = static_cast<ScrollSegment *>(scrolls[i]);
|
||||
w.Write( ss->GetRow(), ss->GetRatio() );
|
||||
}
|
||||
w.Finish();
|
||||
|
||||
if( !bIsSong )
|
||||
{
|
||||
vector<TimingSegment *> &fakes = timing.allTimingSegments[SEGMENT_FAKE];
|
||||
w.Init( "FAKES" );
|
||||
FOREACH_CONST( FakeSegment, timing.m_FakeSegments, fs )
|
||||
for (i = 0; i < fakes.size(); i++)
|
||||
{
|
||||
FakeSegment *fs = static_cast<FakeSegment *>(fakes[i]);
|
||||
w.Write( fs->GetRow(), fs->GetLength() );
|
||||
}
|
||||
w.Finish();
|
||||
}
|
||||
|
||||
w.Init( "LABELS" );
|
||||
FOREACH_CONST( LabelSegment, timing.m_LabelSegments, ls )
|
||||
vector<TimingSegment *> &labels = timing.allTimingSegments[SEGMENT_LABEL];
|
||||
for (i = 0; i < labels.size(); i++)
|
||||
{
|
||||
LabelSegment *ls = static_cast<LabelSegment *>(labels[i]);
|
||||
w.Write( ls->GetRow(), ls->GetLabel().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 ) );
|
||||
f.PutLine(ssprintf("#BPMS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_BPM)).c_str()));
|
||||
f.PutLine(ssprintf("#STOPS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_STOP_DELAY, false)).c_str()));
|
||||
f.PutLine(ssprintf("#DELAYS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_STOP_DELAY, true)).c_str()));
|
||||
f.PutLine(ssprintf("#WARPS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_WARP)).c_str()));
|
||||
f.PutLine(ssprintf("#TIMESIGNATURES:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_TIME_SIG)).c_str()));
|
||||
f.PutLine(ssprintf("#TICKCOUNTS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_TICKCOUNT)).c_str()));
|
||||
f.PutLine(ssprintf("#COMBOS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_COMBO)).c_str()));
|
||||
f.PutLine(ssprintf("#SPEEDS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_SPEED)).c_str()));
|
||||
f.PutLine(ssprintf("#SCROLLS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_SCROLL)).c_str()));
|
||||
if (!bIsSong)
|
||||
f.PutLine(ssprintf("#FAKES:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_FAKE)).c_str()));
|
||||
f.PutLine(ssprintf("#LABELS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_LABEL)).c_str()));
|
||||
|
||||
}
|
||||
|
||||
@@ -190,10 +229,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
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] );
|
||||
vector<RString> vs = out.GetInstrumentTracksToVectorString();
|
||||
if( !vs.empty() )
|
||||
{
|
||||
RString s = join( ",", vs );
|
||||
@@ -203,13 +239,11 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.PutLine( ssprintf( "#OFFSET:%.6f;", out.m_SongTiming.m_fBeat0OffsetInSeconds ) );
|
||||
f.PutLine( ssprintf( "#SAMPLESTART:%.6f;", out.m_fMusicSampleStartSeconds ) );
|
||||
f.PutLine( ssprintf( "#SAMPLELENGTH:%.6f;", out.m_fMusicSampleLengthSeconds ) );
|
||||
if( out.m_fSpecifiedLastBeat > 0 )
|
||||
f.PutLine( ssprintf("#LASTBEATHINT:%.6f;", out.m_fSpecifiedLastBeat) );
|
||||
|
||||
f.Write( "#SELECTABLE:" );
|
||||
switch(out.m_SelectionDisplay)
|
||||
{
|
||||
default: ASSERT(0); // fall through
|
||||
default: ASSERT_M(0, "An invalid selectable value was found for this song!"); // fall through
|
||||
case Song::SHOW_ALWAYS: f.Write( "YES" ); break;
|
||||
//case Song::SHOW_NONSTOP: f.Write( "NONSTOP" ); break;
|
||||
case Song::SHOW_NEVER: f.Write( "NO" ); break;
|
||||
@@ -234,6 +268,9 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
|
||||
WriteTimingTags( f, out.m_SongTiming, true );
|
||||
|
||||
if( out.GetSpecifiedLastSecond() > 0 )
|
||||
f.PutLine( ssprintf("#LASTSECONDHINT:%.6f;", out.GetSpecifiedLastSecond()) );
|
||||
|
||||
FOREACH_BackgroundLayer( b )
|
||||
{
|
||||
if( b==0 )
|
||||
@@ -244,7 +281,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.Write( ssprintf("#BGCHANGES%d:", b+1) );
|
||||
|
||||
FOREACH_CONST( BackgroundChange, out.GetBackgroundChanges(b), bgc )
|
||||
f.PutLine( BackgroundChangeToString(*bgc)+"," );
|
||||
f.PutLine( (*bgc).ToString() +"," );
|
||||
|
||||
/* If there's an animation plan at all, add a dummy "-nosongbg-" tag to
|
||||
* indicate that this file doesn't want a song BG entry added at the end.
|
||||
@@ -260,7 +297,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.Write( "#FGCHANGES:" );
|
||||
FOREACH_CONST( BackgroundChange, out.GetForegroundChanges(), bgc )
|
||||
{
|
||||
f.PutLine( BackgroundChangeToString(*bgc)+"," );
|
||||
f.PutLine( (*bgc).ToString() +"," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
@@ -274,16 +311,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#ATTACKS:" );
|
||||
for( unsigned a=0; a < out.m_sAttackString.size(); a++ )
|
||||
{
|
||||
RString sData = out.m_sAttackString[a];
|
||||
f.Write( ssprintf( "%s", sData.c_str() ) );
|
||||
|
||||
if( a != (out.m_sAttackString.size() - 1) )
|
||||
f.Write( ":" ); // Not the end, so write a divider ':'
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
f.PutLine( ssprintf("#ATTACKS:%s;", out.GetAttackString().c_str()) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,6 +329,7 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
lines.push_back( ssprintf("//---------------%s - %s----------------",
|
||||
GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName, SmEscape(in.GetDescription()).c_str()) );
|
||||
lines.push_back( "#NOTEDATA:;" ); // our new separator.
|
||||
lines.push_back( ssprintf( "#CHARTNAME:%s;", SmEscape(in.GetChartName()).c_str()));
|
||||
lines.push_back( ssprintf( "#STEPSTYPE:%s;", GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName ) );
|
||||
lines.push_back( ssprintf( "#DESCRIPTION:%s;", SmEscape(in.GetDescription()).c_str() ) );
|
||||
lines.push_back( ssprintf( "#CHARTSTYLE:%s;", SmEscape(in.GetChartStyle()).c_str() ) );
|
||||
@@ -317,22 +346,46 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
lines.push_back( ssprintf( "#RADARVALUES:%s;", join(",",asRadarValues).c_str() ) );
|
||||
|
||||
lines.push_back( ssprintf( "#CREDIT:%s;", SmEscape(in.GetCredit()).c_str() ) );
|
||||
|
||||
GetTimingTags( lines, in.m_Timing );
|
||||
|
||||
// For now, attacks are NOT in use for the step.
|
||||
lines.push_back( "#ATTACKS:;" );
|
||||
lines.push_back( ssprintf( "#OFFSET:%.6f;", in.m_Timing.m_fBeat0OffsetInSeconds ) );
|
||||
|
||||
RString sNoteData;
|
||||
in.GetSMNoteData( sNoteData );
|
||||
GetTimingTags( lines, in.m_Timing );
|
||||
|
||||
lines.push_back( ssprintf("#ATTACKS:%s;", in.GetAttackString().c_str()));
|
||||
|
||||
switch( in.GetDisplayBPM() )
|
||||
{
|
||||
case DISPLAY_BPM_ACTUAL:
|
||||
// write nothing
|
||||
break;
|
||||
case DISPLAY_BPM_SPECIFIED:
|
||||
{
|
||||
float small = in.GetMinBPM();
|
||||
float big = in.GetMaxBPM();
|
||||
if (small == big)
|
||||
lines.push_back( ssprintf( "#DISPLAYBPM:%.6f;", small ) );
|
||||
else
|
||||
lines.push_back( ssprintf( "#DISPLAYBPM:%.6f:%.6f;", small, big ) );
|
||||
break;
|
||||
}
|
||||
case DISPLAY_BPM_RANDOM:
|
||||
lines.push_back( ssprintf( "#DISPLAYBPM:*;" ) );
|
||||
break;
|
||||
}
|
||||
if (bSavingCache)
|
||||
{
|
||||
lines.push_back(ssprintf("#STEPFILENAME:%s;", in.GetFilename().c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
RString sNoteData;
|
||||
in.GetSMNoteData( sNoteData );
|
||||
|
||||
lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" );
|
||||
|
||||
TrimLeft(sNoteData);
|
||||
split( sNoteData, "\n", lines, true );
|
||||
lines.push_back( ";" );
|
||||
lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" );
|
||||
|
||||
TrimLeft(sNoteData);
|
||||
split( sNoteData, "\n", lines, true );
|
||||
lines.push_back( ";" );
|
||||
}
|
||||
return JoinLineList( lines );
|
||||
}
|
||||
|
||||
@@ -358,12 +411,12 @@ bool NotesWriterSSC::Write( RString sPath, const Song &out, const vector<Steps*>
|
||||
if( bSavingCache )
|
||||
{
|
||||
f.PutLine( ssprintf( "// cache tags:" ) );
|
||||
f.PutLine( ssprintf( "#FIRSTBEAT:%.3f;", out.m_fFirstBeat ) );
|
||||
f.PutLine( ssprintf( "#LASTBEAT:%.3f;", out.m_fLastBeat ) );
|
||||
f.PutLine( ssprintf( "#FIRSTSECOND:%.6f;", out.GetFirstSecond() ) );
|
||||
f.PutLine( ssprintf( "#LASTSECOND:%.6f;", out.GetLastSecond() ) );
|
||||
f.PutLine( ssprintf( "#SONGFILENAME:%s;", out.m_sSongFileName.c_str() ) );
|
||||
f.PutLine( ssprintf( "#HASMUSIC:%i;", out.m_bHasMusic ) );
|
||||
f.PutLine( ssprintf( "#HASBANNER:%i;", out.m_bHasBanner ) );
|
||||
f.PutLine( ssprintf( "#MUSICLENGTH:%.3f;", out.m_fMusicLengthSeconds ) );
|
||||
f.PutLine( ssprintf( "#MUSICLENGTH:%.6f;", out.m_fMusicLengthSeconds ) );
|
||||
f.PutLine( ssprintf( "// end cache tags" ) );
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -196,7 +196,7 @@ RString OptionRow::GetRowTitle() const
|
||||
|
||||
if( GAMESTATE->m_pCurCourse )
|
||||
{
|
||||
const Trail* pTrail = GAMESTATE->m_pCurTrail[GAMESTATE->m_MasterPlayerNumber];
|
||||
const Trail* pTrail = GAMESTATE->m_pCurTrail[GAMESTATE->GetMasterPlayerNumber()];
|
||||
ASSERT( pTrail != NULL );
|
||||
const int iNumCourseEntries = pTrail->m_vEntries.size();
|
||||
if( iNumCourseEntries > CommonMetrics::MAX_COURSE_ENTRIES_BEFORE_VARIOUS )
|
||||
@@ -417,8 +417,8 @@ void OptionRow::AfterImportOptions( PlayerNumber pn )
|
||||
// we need to copy p2 to p1, not p1 to p2.
|
||||
if( m_pHand->m_Def.m_bOneChoiceForAllPlayers )
|
||||
{
|
||||
PlayerNumber pnCopyFrom = GAMESTATE->m_MasterPlayerNumber;
|
||||
if( GAMESTATE->m_MasterPlayerNumber == PLAYER_INVALID )
|
||||
PlayerNumber pnCopyFrom = GAMESTATE->GetMasterPlayerNumber();
|
||||
if( GAMESTATE->GetMasterPlayerNumber() == PLAYER_INVALID )
|
||||
pnCopyFrom = PLAYER_1;
|
||||
FOREACH_PlayerNumber( p )
|
||||
m_vbSelected[p] = m_vbSelected[pnCopyFrom];
|
||||
|
||||
@@ -14,6 +14,9 @@ class OptionRowHandler;
|
||||
class GameCommand;
|
||||
struct OptionRowDefinition;
|
||||
|
||||
RString ITEMS_LONG_ROW_X_NAME( size_t p );
|
||||
RString MOD_ICON_X_NAME( size_t p );
|
||||
|
||||
class OptionRowType
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define ENTRY_MODE(s,i) THEME->GetMetric ("ScreenOptionsMaster",ssprintf("%s,%i",(s).c_str(),(i+1)))
|
||||
#define ENTRY_DEFAULT(s) THEME->GetMetric ("ScreenOptionsMaster",(s) + "Default")
|
||||
#define NOTE_SKIN_SORT_ORDER THEME->GetMetric ("ScreenOptionsMaster","NoteSkinSortOrder")
|
||||
#define STEPS_USE_CHART_NAME THEME->GetMetricB("ScreenOptionsMaster","StepsUseChartName")
|
||||
|
||||
static const char *SelectTypeNames[] = {
|
||||
"SelectOne",
|
||||
@@ -449,17 +450,33 @@ class OptionRowHandlerListSteps : public OptionRowHandlerList
|
||||
Steps* pSteps = vpSteps[i];
|
||||
|
||||
RString s;
|
||||
if (STEPS_USE_CHART_NAME)
|
||||
{
|
||||
s = pSteps->GetChartName();
|
||||
// TODO: find a way to make this use lua or metrics.
|
||||
if (!(s == "" || s == "blank" || s == "Blank"))
|
||||
{
|
||||
goto nameGotten;
|
||||
}
|
||||
}
|
||||
if( pSteps->GetDifficulty() == Difficulty_Edit )
|
||||
{
|
||||
s = pSteps->GetDescription();
|
||||
s = pSteps->GetChartName();
|
||||
if (s == "" || s == "blank" || s == "Blank")
|
||||
s = pSteps->GetDescription();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pSteps->IsAnEdit() )
|
||||
s = pSteps->GetDescription();
|
||||
{
|
||||
s = pSteps->GetChartName();
|
||||
if (s == "" || s == "blank" || s == "Blank")
|
||||
s = pSteps->GetDescription();
|
||||
}
|
||||
else
|
||||
s = CustomDifficultyToLocalizedString( GetCustomDifficulty( pSteps->m_StepsType, pSteps->GetDifficulty(), CourseType_Invalid ) );
|
||||
}
|
||||
nameGotten:
|
||||
s += ssprintf( " %d", pSteps->GetMeter() );
|
||||
m_Def.m_vsChoices.push_back( s );
|
||||
GameCommand mc;
|
||||
|
||||
@@ -137,26 +137,6 @@ void PercentageDisplay::Refresh()
|
||||
else
|
||||
{
|
||||
float fPercentDancePoints = m_pPlayerStageStats->GetPercentDancePoints();
|
||||
float fCurMaxPercentDancePoints = m_pPlayerStageStats->GetCurMaxPercentDancePoints();
|
||||
|
||||
if( m_bApplyScoreDisplayOptions )
|
||||
{
|
||||
switch( m_pPlayerState->m_PlayerOptions.GetCurrent().m_ScoreDisplay )
|
||||
{
|
||||
case PlayerOptions::SCORING_ADD:
|
||||
// nothing to do
|
||||
break;
|
||||
case PlayerOptions::SCORING_SUBTRACT:
|
||||
fPercentDancePoints = 1.0f - ( fCurMaxPercentDancePoints - fPercentDancePoints );
|
||||
break;
|
||||
case PlayerOptions::SCORING_AVERAGE:
|
||||
if( fCurMaxPercentDancePoints == 0.0f ) // don't divide by zero fats
|
||||
fPercentDancePoints = 0.0f;
|
||||
else
|
||||
fPercentDancePoints = fPercentDancePoints / fCurMaxPercentDancePoints;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// clamp percentage - feedback is that negative numbers look weird here.
|
||||
CLAMP( fPercentDancePoints, 0.f, 1.f );
|
||||
|
||||
+158
-45
@@ -42,6 +42,9 @@
|
||||
#include "LocalizedString.h"
|
||||
#include "AdjustSync.h"
|
||||
|
||||
RString ATTACK_DISPLAY_X_NAME( size_t p, size_t both_sides );
|
||||
void TimingWindowSecondsInit( size_t /*TimingWindow*/ i, RString &sNameOut, float &defaultValueOut );
|
||||
|
||||
/**
|
||||
* @brief Helper class to ensure that each row is only judged once without taking too much memory.
|
||||
*/
|
||||
@@ -112,7 +115,7 @@ void TimingWindowSecondsInit( size_t /*TimingWindow*/ i, RString &sNameOut, floa
|
||||
case TW_W5: defaultValueOut = 0.180f; break;
|
||||
case TW_Mine: defaultValueOut = 0.090f; break; // same as great
|
||||
case TW_Hold: defaultValueOut = 0.500f; break; // allow enough time to take foot off and put back on
|
||||
case TW_Roll: defaultValueOut = 0.350f; break;
|
||||
case TW_Roll: defaultValueOut = 0.500f; break;
|
||||
case TW_Attack: defaultValueOut = 0.135f; break;
|
||||
}
|
||||
}
|
||||
@@ -198,6 +201,12 @@ ThemeMetric<int> COMBO_STOPPED_AT ( "Player", "ComboStoppedAt" );
|
||||
ThemeMetric<float> ATTACK_RUN_TIME_RANDOM ( "Player", "AttackRunTimeRandom" );
|
||||
ThemeMetric<float> ATTACK_RUN_TIME_MINE ( "Player", "AttackRunTimeMine" );
|
||||
|
||||
/**
|
||||
* @brief What is our highest cap for mMods?
|
||||
*
|
||||
* If set to 0 or less, assume the song takes over. */
|
||||
ThemeMetric<float> M_MOD_HIGH_CAP("Player", "MModHighCap");
|
||||
|
||||
/** @brief Will battle modes have their steps mirrored or kept the same? */
|
||||
ThemeMetric<bool> BATTLE_RAVE_MIRROR ( "Player", "BattleRaveMirror" );
|
||||
|
||||
@@ -398,6 +407,75 @@ void Player::Init(
|
||||
break;
|
||||
}
|
||||
|
||||
// calculate M-mod speed here, so we can adjust properly on a per-song basis.
|
||||
// XXX: can we find a better location for this?
|
||||
if( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fMaxScrollBPM != 0 )
|
||||
{
|
||||
DisplayBpms bpms;
|
||||
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
{
|
||||
ASSERT( GAMESTATE->m_pCurTrail[pn] );
|
||||
GAMESTATE->m_pCurTrail[pn]->GetDisplayBpms( bpms );
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT( GAMESTATE->m_pCurSong );
|
||||
GAMESTATE->m_pCurSong->GetDisplayBpms( bpms );
|
||||
}
|
||||
|
||||
float fMaxBPM = 0;
|
||||
|
||||
/* TODO: Find a way to not go above a certain BPM range
|
||||
* for getting the max BPM. Otherwise, you get songs
|
||||
* like Tsuhsuixamush, M550, 0.18x speed. Even slow
|
||||
* speed readers would not generally find this fun.
|
||||
* -Wolfman2000
|
||||
*/
|
||||
|
||||
// all BPMs are listed and available, so try them first.
|
||||
// get the maximum listed value for the song or course.
|
||||
// if the BPMs are < 0, reset and get the actual values.
|
||||
if( !bpms.IsSecret() )
|
||||
{
|
||||
fMaxBPM = (M_MOD_HIGH_CAP > 0 ?
|
||||
bpms.GetMaxWithin(M_MOD_HIGH_CAP) :
|
||||
bpms.GetMax());
|
||||
fMaxBPM = max( 0, fMaxBPM );
|
||||
}
|
||||
|
||||
// we can't rely on the displayed BPMs, so manually calculate.
|
||||
if( fMaxBPM == 0 )
|
||||
{
|
||||
float fThrowAway = 0;
|
||||
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
{
|
||||
FOREACH_CONST( TrailEntry, GAMESTATE->m_pCurTrail[pn]->m_vEntries, e )
|
||||
{
|
||||
float fMaxForEntry;
|
||||
if (M_MOD_HIGH_CAP > 0)
|
||||
e->pSong->m_SongTiming.GetActualBPM( fThrowAway, fMaxForEntry, M_MOD_HIGH_CAP );
|
||||
else
|
||||
e->pSong->m_SongTiming.GetActualBPM( fThrowAway, fMaxForEntry );
|
||||
fMaxBPM = max( fMaxForEntry, fMaxBPM );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (M_MOD_HIGH_CAP > 0)
|
||||
GAMESTATE->m_pCurSong->m_SongTiming.GetActualBPM( fThrowAway, fMaxBPM, M_MOD_HIGH_CAP );
|
||||
else
|
||||
GAMESTATE->m_pCurSong->m_SongTiming.GetActualBPM( fThrowAway, fMaxBPM );
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT( fMaxBPM > 0 );
|
||||
|
||||
// set an X-mod equal to Mnum / fMaxBPM (e.g. M600 with 150 becomes 4x)
|
||||
PO_GROUP_ASSIGN(m_pPlayerState->m_PlayerOptions, ModsLevel_Preferred, m_fScrollSpeed,
|
||||
m_pPlayerState->m_PlayerOptions.GetPreferred().m_fMaxScrollBPM / fMaxBPM);
|
||||
}
|
||||
|
||||
float fBalance = GameSoundManager::GetPlayerBalance( pn );
|
||||
m_soundMine.SetProperty( "Pan", fBalance );
|
||||
@@ -789,7 +867,7 @@ void Player::Update( float fDeltaTime )
|
||||
Actor::TweenState::MakeWeightedAverage( m_pActorWithComboPosition->DestTweenState(), ts1, ts2, fPercentCentered );
|
||||
}
|
||||
|
||||
float fNoteFieldZoom = 1 - fTinyPercent*0.5f;
|
||||
float fNoteFieldZoom = 1 - fMiniPercent*0.5f;
|
||||
if( m_pNoteField )
|
||||
m_pNoteField->SetZoom( fNoteFieldZoom );
|
||||
if( m_pActorWithJudgmentPosition != NULL )
|
||||
@@ -957,8 +1035,10 @@ void Player::Update( float fDeltaTime )
|
||||
UpdateJudgedRows();
|
||||
|
||||
// Check for TapNote misses
|
||||
UpdateTapNotesMissedOlderThan( GetMaxStepDistanceSeconds() );
|
||||
|
||||
if (!GAMESTATE->m_bInStepEditor)
|
||||
{
|
||||
UpdateTapNotesMissedOlderThan( GetMaxStepDistanceSeconds() );
|
||||
}
|
||||
// process transforms that are waiting to be applied
|
||||
ApplyWaitingTransforms();
|
||||
}
|
||||
@@ -1323,14 +1403,9 @@ void Player::UpdateHoldNotes( int iSongRow, float fDeltaTime, vector<TrackRowTap
|
||||
// Possibly fixed.
|
||||
if( tn.iKeysoundIndex >= 0 && tn.iKeysoundIndex < (int) m_vKeysounds.size() )
|
||||
{
|
||||
if( tn.subType == TapNote::hold_head_roll )
|
||||
{
|
||||
m_vKeysounds[tn.iKeysoundIndex].SetProperty ("Volume", max(0.0, min(1.0, fLifeFraction * 2.0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vKeysounds[tn.iKeysoundIndex].SetProperty ("Volume", max(0.0, min(1.0, fLifeFraction * 10.0 - 8.5)));
|
||||
}
|
||||
float factor = (tn.subType == TapNote::hold_head_roll ? 2 : 10.0f - 8.5f);
|
||||
factor *= fLifeFraction;
|
||||
m_vKeysounds[tn.iKeysoundIndex].SetProperty ("Volume", max(0.0f, min(1.0f, factor)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1375,7 +1450,7 @@ void Player::DrawPrimitives()
|
||||
|
||||
// May have both players in doubles (for battle play); only draw primary player.
|
||||
if( GAMESTATE->GetCurrentStyle()->m_StyleType == StyleType_OnePlayerTwoSides &&
|
||||
pn != GAMESTATE->m_MasterPlayerNumber )
|
||||
pn != GAMESTATE->GetMasterPlayerNumber() )
|
||||
return;
|
||||
|
||||
// Draw these below everything else.
|
||||
@@ -1413,7 +1488,7 @@ void Player::DrawPrimitives()
|
||||
|
||||
float fTiltDegrees = SCALE(fTilt,-1.f,+1.f,+30,-30) * (bReverse?-1:1);
|
||||
|
||||
float fZoom = SCALE( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_TINY], 0.f, 1.f, 1.f, 0.5f );
|
||||
float fZoom = SCALE( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_MINI], 0.f, 1.f, 1.f, 0.5f );
|
||||
if( fTilt > 0 )
|
||||
fZoom *= SCALE( fTilt, 0.f, 1.f, 1.f, 0.9f );
|
||||
else
|
||||
@@ -1437,7 +1512,7 @@ void Player::DrawPrimitives()
|
||||
DISPLAY->PopMatrix();
|
||||
|
||||
// m_pNoteField->m_sprBoard->GetVisible()
|
||||
if( !(bool)COMBO_UNDER_FIELD && m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind == 0 )
|
||||
if( !COMBO_UNDER_FIELD && m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind == 0 )
|
||||
if( m_sprCombo )
|
||||
m_sprCombo->Draw();
|
||||
|
||||
@@ -1538,7 +1613,7 @@ int Player::GetClosestNoteDirectional( int col, int iStartRow, int iEndRow, bool
|
||||
// Is this the row we want?
|
||||
do {
|
||||
const TapNote &tn = begin->second;
|
||||
if( m_Timing->IsWarpAtRow( begin->first ) || m_Timing->IsFakeAtRow( begin->first ) )
|
||||
if (!m_Timing->IsJudgableAtRow( begin->first ))
|
||||
break;
|
||||
if( tn.type == TapNote::empty )
|
||||
break;
|
||||
@@ -1594,7 +1669,7 @@ int Player::GetClosestNonEmptyRowDirectional( int iStartRow, int iEndRow, bool b
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
if( m_Timing->IsWarpAtRow( iter.Row() ) || m_Timing->IsFakeAtRow( iter.Row() ) )
|
||||
if (!m_Timing->IsJudgableAtRow(iter.Row()))
|
||||
{
|
||||
++iter;
|
||||
continue;
|
||||
@@ -2118,7 +2193,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
|
||||
// Stepped too close to mine?
|
||||
if( !bRelease && ( REQUIRE_STEP_ON_MINES == !bHeld ) &&
|
||||
fSecondsFromExact <= GetWindowSeconds(TW_Mine) &&
|
||||
!m_Timing->IsWarpAtRow(iSongRow) && !m_Timing->IsFakeAtRow(iSongRow))
|
||||
m_Timing->IsJudgableAtRow(iSongRow))
|
||||
score = TNS_HitMine;
|
||||
break;
|
||||
|
||||
@@ -2616,7 +2691,7 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
continue;
|
||||
|
||||
// Ignore all notes in WarpSegments or FakeSegments.
|
||||
if( m_Timing->IsWarpAtRow( iter.Row() ) || m_Timing->IsFakeAtRow( iter.Row() ) )
|
||||
if (!m_Timing->IsJudgableAtRow(iter.Row()))
|
||||
continue;
|
||||
|
||||
if( tn.type == TapNote::mine )
|
||||
@@ -2651,7 +2726,7 @@ void Player::UpdateJudgedRows()
|
||||
int iRow = iter.Row();
|
||||
|
||||
// Do not judge arrows in WarpSegments or FakeSegments
|
||||
if( m_Timing->IsWarpAtRow(iRow) || m_Timing->IsFakeAtRow(iRow) )
|
||||
if (!m_Timing->IsJudgableAtRow(iRow))
|
||||
continue;
|
||||
|
||||
if( iLastSeenRow != iRow )
|
||||
@@ -2852,7 +2927,8 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
||||
// check to see if there's a note at the crossed row
|
||||
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
|
||||
{
|
||||
if( tn.type != TapNote::empty && tn.type != TapNote::fake && tn.result.tns == TNS_None )
|
||||
if(tn.type != TapNote::empty && tn.type != TapNote::fake && tn.result.tns == TNS_None
|
||||
&& this->m_Timing->IsJudgableAtRow(iRow) )
|
||||
{
|
||||
Step( iTrack, iRow, now, false, false );
|
||||
if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY )
|
||||
@@ -2866,8 +2942,10 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
||||
}
|
||||
|
||||
|
||||
// Update hold checkpoints
|
||||
if( HOLD_CHECKPOINTS )
|
||||
/* Update hold checkpoints
|
||||
*
|
||||
* TODO: Move this to a separate function. */
|
||||
if( HOLD_CHECKPOINTS && m_pPlayerState->m_PlayerController != PC_AUTOPLAY )
|
||||
{
|
||||
int iCheckpointFrequencyRows = ROWS_PER_BEAT/2;
|
||||
if( CHECKPOINTS_USE_TICKCOUNTS )
|
||||
@@ -2878,19 +2956,21 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
||||
}
|
||||
else if( CHECKPOINTS_USE_TIME_SIGNATURES )
|
||||
{
|
||||
TimeSignatureSegment tSignature = m_Timing->GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iLastRowCrossed ) );
|
||||
TimeSignatureSegment * tSignature = m_Timing->GetTimeSignatureSegmentAtRow( iLastRowCrossed );
|
||||
|
||||
// Most songs are in 4/4 time. The frequency for checking tick counts should reflect that.
|
||||
iCheckpointFrequencyRows = ROWS_PER_BEAT * tSignature.GetDen() / (tSignature.GetNum() * 4);
|
||||
iCheckpointFrequencyRows = ROWS_PER_BEAT * tSignature->GetDen() / (tSignature->GetNum() * 4);
|
||||
}
|
||||
|
||||
if( iCheckpointFrequencyRows > 0 )
|
||||
{
|
||||
// "the first row after the start of the range that lands on a beat"
|
||||
int iFirstCheckpointInRange = ((m_iFirstUncrossedRow+iCheckpointFrequencyRows-1)/iCheckpointFrequencyRows) * iCheckpointFrequencyRows;
|
||||
int iFirstCheckpointInRange = ((m_iFirstUncrossedRow+iCheckpointFrequencyRows-1)
|
||||
/iCheckpointFrequencyRows) * iCheckpointFrequencyRows;
|
||||
|
||||
// "the last row or first row earlier that lands on a beat"
|
||||
int iLastCheckpointInRange = ((iLastRowCrossed)/iCheckpointFrequencyRows) * iCheckpointFrequencyRows;
|
||||
int iLastCheckpointInRange = ((iLastRowCrossed)/iCheckpointFrequencyRows)
|
||||
* iCheckpointFrequencyRows;
|
||||
|
||||
for( int r = iFirstCheckpointInRange; r <= iLastCheckpointInRange; r += iCheckpointFrequencyRows )
|
||||
{
|
||||
@@ -2912,17 +2992,22 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
||||
int iTrack = nIter.Track();
|
||||
|
||||
// "the first row after the hold head that lands on a beat"
|
||||
int iFirstCheckpointOfHold = ((iStartRow+iCheckpointFrequencyRows)/iCheckpointFrequencyRows) * iCheckpointFrequencyRows;
|
||||
int iFirstCheckpointOfHold = ((iStartRow+iCheckpointFrequencyRows)/iCheckpointFrequencyRows)
|
||||
* iCheckpointFrequencyRows;
|
||||
|
||||
// "the end row or the first earlier row that lands on a beat"
|
||||
int iLastCheckpointOfHold = ((iEndRow)/iCheckpointFrequencyRows) * iCheckpointFrequencyRows;
|
||||
int iLastCheckpointOfHold = ((iEndRow)/iCheckpointFrequencyRows)
|
||||
* iCheckpointFrequencyRows;
|
||||
|
||||
// count the end of the hold as a checkpoint
|
||||
bool bHoldOverlapsRow = iFirstCheckpointOfHold <= r && r <= iLastCheckpointOfHold;
|
||||
if( !bHoldOverlapsRow )
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
viColsWithHold.push_back( iTrack );
|
||||
|
||||
if( tn.HoldResult.fLife > 0 )
|
||||
{
|
||||
++iNumHoldsHeldThisRow;
|
||||
@@ -2934,11 +3019,15 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
||||
++tn.HoldResult.iCheckpointsMissed;
|
||||
}
|
||||
}
|
||||
GAMESTATE->SetProcessedTimingData(this->m_Timing);
|
||||
|
||||
// TODO: Find a better way of handling hold checkpoints with other taps.
|
||||
if( !viColsWithHold.empty() && ( CHECKPOINTS_TAPS_SEPARATE_JUDGMENT || m_NoteData.GetNumTapNotesInRow( iLastRowCrossed ) == 0 ) )
|
||||
{
|
||||
HandleHoldCheckpoint( r, iNumHoldsHeldThisRow, iNumHoldsMissedThisRow, viColsWithHold );
|
||||
HandleHoldCheckpoint(r,
|
||||
iNumHoldsHeldThisRow,
|
||||
iNumHoldsMissedThisRow,
|
||||
viColsWithHold );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2965,10 +3054,6 @@ void Player::RandomizeNotes( int iNoteRow )
|
||||
|
||||
const int iSwapWith = RandomInt( iNumOfTracks );
|
||||
|
||||
// Make sure we're not swapping with ourselves.
|
||||
if( t == iSwapWith )
|
||||
continue;
|
||||
|
||||
// Make sure this is empty.
|
||||
if( m_NoteData.FindTapNote(iSwapWith, iNewNoteRow) != m_NoteData.end(iSwapWith) )
|
||||
continue;
|
||||
@@ -2990,7 +3075,7 @@ void Player::HandleTapRowScore( unsigned row )
|
||||
#endif
|
||||
|
||||
// Do not score rows in WarpSegments or FakeSegments
|
||||
if( m_Timing->IsWarpAtRow( row ) || m_Timing->IsFakeAtRow( row ) )
|
||||
if (!m_Timing->IsJudgableAtRow(row))
|
||||
return;
|
||||
|
||||
if( GAMESTATE->m_bDemonstrationOrJukebox )
|
||||
@@ -3086,7 +3171,10 @@ void Player::HandleTapRowScore( unsigned row )
|
||||
ChangeLife( scoreOfLastTap );
|
||||
}
|
||||
|
||||
void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumHoldsMissedThisRow, const vector<int> &viColsWithHold )
|
||||
void Player::HandleHoldCheckpoint(int iRow,
|
||||
int iNumHoldsHeldThisRow,
|
||||
int iNumHoldsMissedThisRow,
|
||||
const vector<int> &viColsWithHold )
|
||||
{
|
||||
bool bNoCheating = true;
|
||||
#ifdef DEBUG
|
||||
@@ -3094,7 +3182,7 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH
|
||||
#endif
|
||||
|
||||
// WarpSegments and FakeSegments aren't judged in any way.
|
||||
if( m_Timing->IsWarpAtRow( iRow ) || m_Timing->IsFakeAtRow( iRow ) )
|
||||
if (!m_Timing->IsJudgableAtRow(iRow))
|
||||
return;
|
||||
|
||||
// don't accumulate combo if AutoPlay is on.
|
||||
@@ -3105,9 +3193,15 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH
|
||||
const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0;
|
||||
|
||||
if( m_pPrimaryScoreKeeper )
|
||||
m_pPrimaryScoreKeeper->HandleHoldCheckpointScore( m_NoteData, iRow, iNumHoldsHeldThisRow, iNumHoldsMissedThisRow );
|
||||
m_pPrimaryScoreKeeper->HandleHoldCheckpointScore(m_NoteData,
|
||||
iRow,
|
||||
iNumHoldsHeldThisRow,
|
||||
iNumHoldsMissedThisRow );
|
||||
if( m_pSecondaryScoreKeeper )
|
||||
m_pSecondaryScoreKeeper->HandleHoldCheckpointScore( m_NoteData, iRow, iNumHoldsHeldThisRow, iNumHoldsMissedThisRow );
|
||||
m_pSecondaryScoreKeeper->HandleHoldCheckpointScore(m_NoteData,
|
||||
iRow,
|
||||
iNumHoldsHeldThisRow,
|
||||
iNumHoldsMissedThisRow );
|
||||
|
||||
if( iNumHoldsMissedThisRow == 0 )
|
||||
{
|
||||
@@ -3116,7 +3210,8 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH
|
||||
{
|
||||
FOREACH_CONST( int, viColsWithHold, i )
|
||||
{
|
||||
bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->m_iCurCombo>(int)BRIGHT_GHOST_COMBO_THRESHOLD;
|
||||
bool bBright = m_pPlayerStageStats
|
||||
&& m_pPlayerStageStats->m_iCurCombo>(int)BRIGHT_GHOST_COMBO_THRESHOLD;
|
||||
if( m_pNoteField )
|
||||
m_pNoteField->DidHoldNote( *i, HNS_Held, bBright );
|
||||
}
|
||||
@@ -3285,12 +3380,14 @@ void Player::SetCombo( int iCombo, int iMisses )
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
{
|
||||
int iSongIndexStartColoring = GAMESTATE->m_pCurCourse->GetEstimatedNumStages();
|
||||
iSongIndexStartColoring = static_cast<int>(floor(iSongIndexStartColoring*PERCENT_UNTIL_COLOR_COMBO));
|
||||
iSongIndexStartColoring =
|
||||
static_cast<int>(floor(iSongIndexStartColoring*PERCENT_UNTIL_COLOR_COMBO));
|
||||
bPastBeginning = GAMESTATE->GetCourseSongIndex() >= iSongIndexStartColoring;
|
||||
}
|
||||
else
|
||||
{
|
||||
bPastBeginning = m_pPlayerState->m_Position.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 )
|
||||
@@ -3332,13 +3429,29 @@ RString Player::ApplyRandomAttack()
|
||||
class LunaPlayer: public Luna<Player>
|
||||
{
|
||||
public:
|
||||
static int SetActorWithJudgmentPosition( T* p, lua_State *L ) { Actor *pActor = Luna<Actor>::check(L, 1); p->SetActorWithJudgmentPosition(pActor); return 0; }
|
||||
static int SetActorWithComboPosition( T* p, lua_State *L ) { Actor *pActor = Luna<Actor>::check(L, 1); p->SetActorWithComboPosition(pActor); return 0; }
|
||||
|
||||
static int SetActorWithJudgmentPosition( T* p, lua_State *L )
|
||||
{
|
||||
Actor *pActor = Luna<Actor>::check(L, 1);
|
||||
p->SetActorWithJudgmentPosition(pActor);
|
||||
return 0;
|
||||
}
|
||||
static int SetActorWithComboPosition( T* p, lua_State *L )
|
||||
{
|
||||
Actor *pActor = Luna<Actor>::check(L, 1);
|
||||
p->SetActorWithComboPosition(pActor);
|
||||
return 0;
|
||||
}
|
||||
static int GetPlayerTimingData( T* p, lua_State *L )
|
||||
{
|
||||
p->GetPlayerTimingData().PushSelf(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LunaPlayer()
|
||||
{
|
||||
ADD_METHOD( SetActorWithJudgmentPosition );
|
||||
ADD_METHOD( SetActorWithComboPosition );
|
||||
ADD_METHOD( GetPlayerTimingData );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -69,6 +69,16 @@ public:
|
||||
void Load();
|
||||
void CrossedRows( int iLastRowCrossed, const RageTimer &now );
|
||||
bool IsOniDead() const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the Player's TimingData.
|
||||
*
|
||||
* This is primarily for a lua hook.
|
||||
* @return the TimingData in question. */
|
||||
TimingData GetPlayerTimingData() const
|
||||
{
|
||||
return *(this->m_Timing);
|
||||
}
|
||||
|
||||
// Called when a fret, step, or strum type button changes
|
||||
void Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease );
|
||||
|
||||
+71
-19
@@ -12,6 +12,9 @@
|
||||
#include "CommonMetrics.h"
|
||||
#include <float.h>
|
||||
|
||||
void NextFloat( float fValues[], int size );
|
||||
void NextBool( bool bValues[], int size );
|
||||
|
||||
ThemeMetric<float> RANDOM_SPEED_CHANCE ( "PlayerOptions", "RandomSpeedChance" );
|
||||
ThemeMetric<float> RANDOM_REVERSE_CHANCE ( "PlayerOptions", "RandomReverseChance" );
|
||||
ThemeMetric<float> RANDOM_DARK_CHANCE ( "PlayerOptions", "RandomDarkChance" );
|
||||
@@ -23,6 +26,7 @@ ThemeMetric<float> RANDOM_SUDDEN_CHANCE ( "PlayerOptions", "RandomSuddenChance"
|
||||
void PlayerOptions::Init()
|
||||
{
|
||||
m_bSetScrollSpeed = false;
|
||||
m_fMaxScrollBPM = 0; m_SpeedfMaxScrollBPM = 1.0f;
|
||||
m_fTimeSpacing = 0; m_SpeedfTimeSpacing = 1.0f;
|
||||
m_fScrollSpeed = 1.0f; m_SpeedfScrollSpeed = 1.0f;
|
||||
m_fScrollBPM = 200; m_SpeedfScrollBPM = 1.0f;
|
||||
@@ -34,7 +38,7 @@ void PlayerOptions::Init()
|
||||
m_fBlind = 0; m_SpeedfBlind = 1.0f;
|
||||
m_fCover = 0; m_SpeedfCover = 1.0f;
|
||||
m_fRandAttack = 0; m_SpeedfRandAttack = 1.0f;
|
||||
m_fSongAttack = 0; m_SpeedfSongAttack = 1.0f;
|
||||
m_fNoAttack = 0; m_SpeedfNoAttack = 1.0f;
|
||||
m_fPlayerAutoPlay = 0; m_SpeedfPlayerAutoPlay = 1.0f;
|
||||
m_bSetTiltOrSkew = false;
|
||||
m_fPerspectiveTilt = 0; m_SpeedfPerspectiveTilt = 1.0f;
|
||||
@@ -45,7 +49,6 @@ void PlayerOptions::Init()
|
||||
ZERO( m_bTransforms );
|
||||
m_bMuteOnError = false;
|
||||
m_FailType = FAIL_IMMEDIATE;
|
||||
m_ScoreDisplay = SCORING_ADD;
|
||||
m_sNoteSkin = "";
|
||||
}
|
||||
|
||||
@@ -58,6 +61,7 @@ void PlayerOptions::Approach( const PlayerOptions& other, float fDeltaSeconds )
|
||||
|
||||
APPROACH( fTimeSpacing );
|
||||
APPROACH( fScrollSpeed );
|
||||
//APPROACH( fMaxScrollBPM );
|
||||
fapproach( m_fScrollBPM, other.m_fScrollBPM, fDeltaSeconds * other.m_SpeedfScrollBPM*150 );
|
||||
for( int i=0; i<NUM_ACCELS; i++ )
|
||||
APPROACH( fAccels[i] );
|
||||
@@ -71,7 +75,7 @@ void PlayerOptions::Approach( const PlayerOptions& other, float fDeltaSeconds )
|
||||
APPROACH( fBlind );
|
||||
APPROACH( fCover );
|
||||
APPROACH( fRandAttack );
|
||||
APPROACH( fSongAttack );
|
||||
APPROACH( fNoAttack );
|
||||
APPROACH( fPlayerAutoPlay );
|
||||
APPROACH( fPerspectiveTilt );
|
||||
APPROACH( fSkew );
|
||||
@@ -85,7 +89,6 @@ void PlayerOptions::Approach( const PlayerOptions& other, float fDeltaSeconds )
|
||||
for( int i=0; i<NUM_TRANSFORMS; i++ )
|
||||
DO_COPY( m_bTransforms[i] );
|
||||
DO_COPY( m_bMuteOnError );
|
||||
DO_COPY( m_ScoreDisplay );
|
||||
DO_COPY( m_FailType );
|
||||
DO_COPY( m_sNoteSkin );
|
||||
#undef APPROACH
|
||||
@@ -115,6 +118,11 @@ void PlayerOptions::GetMods( vector<RString> &AddTo, bool bForceNoteSkin ) const
|
||||
|
||||
if( !m_fTimeSpacing )
|
||||
{
|
||||
if( m_fMaxScrollBPM )
|
||||
{
|
||||
RString s = ssprintf( "m%.0f", m_fMaxScrollBPM );
|
||||
AddTo.push_back( s );
|
||||
}
|
||||
if( m_bSetScrollSpeed || m_fScrollSpeed != 1 )
|
||||
{
|
||||
/* -> 1.00 */
|
||||
@@ -179,7 +187,7 @@ void PlayerOptions::GetMods( vector<RString> &AddTo, bool bForceNoteSkin ) const
|
||||
AddPart( AddTo, m_fCover, "Cover" );
|
||||
|
||||
AddPart( AddTo, m_fRandAttack, "RandomAttacks" );
|
||||
AddPart( AddTo, m_fSongAttack, "SongAttacks" );
|
||||
AddPart( AddTo, m_fNoAttack, "NoAttacks" );
|
||||
AddPart( AddTo, m_fPlayerAutoPlay, "PlayerAutoPlay" );
|
||||
|
||||
AddPart( AddTo, m_fPassmark, "Passmark" );
|
||||
@@ -331,6 +339,7 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut
|
||||
SET_FLOAT( fScrollSpeed )
|
||||
SET_FLOAT( fTimeSpacing )
|
||||
m_fTimeSpacing = 0;
|
||||
m_fMaxScrollBPM = 0;
|
||||
}
|
||||
else if( sscanf( sBit, "c%f", &level ) == 1 )
|
||||
{
|
||||
@@ -339,17 +348,18 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut
|
||||
SET_FLOAT( fScrollBPM )
|
||||
SET_FLOAT( fTimeSpacing )
|
||||
m_fTimeSpacing = 1;
|
||||
m_fMaxScrollBPM = 0;
|
||||
}
|
||||
/* Port M-Mods from OpenITG, starting from r537 */
|
||||
// Midiman
|
||||
// oITG's m-mods
|
||||
// XXX: will not properly tween, I don't think.
|
||||
else if( sscanf( sBit, "m%f", &level ) == 1 )
|
||||
{
|
||||
if( !isfinite(level) || level <= 0.0f )
|
||||
level = 200.0f; // Just pick some value.
|
||||
SET_FLOAT( fScrollBPM )
|
||||
SET_FLOAT( fTimeSpacing )
|
||||
m_fTimeSpacing = 1;
|
||||
level = 200.0f;
|
||||
SET_FLOAT( fMaxScrollBPM )
|
||||
m_fTimeSpacing = 0;
|
||||
}
|
||||
|
||||
else if( sBit == "clearall" ) Init();
|
||||
else if( sBit == "boost" ) SET_FLOAT( fAccels[ACCEL_BOOST] )
|
||||
else if( sBit == "brake" || sBit == "land" ) SET_FLOAT( fAccels[ACCEL_BRAKE] )
|
||||
@@ -416,7 +426,7 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut
|
||||
else if( sBit == "blind" ) SET_FLOAT( fBlind )
|
||||
else if( sBit == "cover" ) SET_FLOAT( fCover )
|
||||
else if( sBit == "randomattacks" ) SET_FLOAT( fRandAttack )
|
||||
else if( sBit == "songattacks" ) SET_FLOAT( fSongAttack )
|
||||
else if( sBit == "noattacks" ) SET_FLOAT( fNoAttack )
|
||||
else if( sBit == "playerautoplay" ) SET_FLOAT( fPlayerAutoPlay )
|
||||
else if( sBit == "passmark" ) SET_FLOAT( fPassmark )
|
||||
else if( sBit == "overhead" ) { m_bSetTiltOrSkew = true; m_fSkew = 0; m_fPerspectiveTilt = 0; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; }
|
||||
@@ -439,9 +449,6 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut
|
||||
GAMESTATE->GetDefaultPlayerOptions( po );
|
||||
m_FailType = po.m_FailType;
|
||||
}
|
||||
else if( sBit == "addscore" ) m_ScoreDisplay = SCORING_ADD;
|
||||
else if( sBit == "subtractscore" ) m_ScoreDisplay = SCORING_SUBTRACT;
|
||||
else if( sBit == "averagescore" ) m_ScoreDisplay = SCORING_AVERAGE;
|
||||
else if( sBit == "muteonerror" ) m_bMuteOnError = on;
|
||||
else if( sBit == "random" ) ChooseRandomModifiers();
|
||||
// deprecated mods/left in for compatibility
|
||||
@@ -652,15 +659,15 @@ bool PlayerOptions::operator==( const PlayerOptions &other ) const
|
||||
COMPARE(m_fTimeSpacing);
|
||||
COMPARE(m_fScrollSpeed);
|
||||
COMPARE(m_fScrollBPM);
|
||||
COMPARE(m_fMaxScrollBPM);
|
||||
COMPARE(m_fRandomSpeed);
|
||||
COMPARE(m_ScoreDisplay);
|
||||
COMPARE(m_FailType);
|
||||
COMPARE(m_bMuteOnError);
|
||||
COMPARE(m_fDark);
|
||||
COMPARE(m_fBlind);
|
||||
COMPARE(m_fCover);
|
||||
COMPARE(m_fRandAttack);
|
||||
COMPARE(m_fSongAttack);
|
||||
COMPARE(m_fNoAttack);
|
||||
COMPARE(m_fPlayerAutoPlay);
|
||||
COMPARE(m_fPerspectiveTilt);
|
||||
COMPARE(m_fSkew);
|
||||
@@ -715,7 +722,36 @@ bool PlayerOptions::IsEasierForSongAndSteps( Song* pSong, Steps* pSteps, PlayerN
|
||||
// This makes songs with sparse notes easier.
|
||||
if( m_bTransforms[TRANSFORM_ECHO] ) return true;
|
||||
|
||||
// Removing attacks is easier in general.
|
||||
if (m_fNoAttack || (!m_fRandAttack && pSteps->HasAttacks()))
|
||||
return true;
|
||||
|
||||
if( m_fCover ) return true;
|
||||
|
||||
// M-mods make songs with indefinite BPMs easier because
|
||||
// they ensure that the song has a scrollable speed.
|
||||
if( m_fMaxScrollBPM != 0 )
|
||||
{
|
||||
// BPM display is obfuscated
|
||||
// if( pSong->m_DisplayBPMType == DISPLAY_BPM_RANDOM )
|
||||
// return true;
|
||||
|
||||
DisplayBpms bpms;
|
||||
if( GAMESTATE->IsCourseMode() )
|
||||
{
|
||||
Trail *pTrail = GAMESTATE->m_pCurCourse->GetTrail( GAMESTATE->GetCurrentStyle()->m_StepsType );
|
||||
pTrail->GetDisplayBpms( bpms );
|
||||
}
|
||||
else
|
||||
{
|
||||
GAMESTATE->m_pCurSong->GetDisplayBpms( bpms );
|
||||
}
|
||||
pSong->GetDisplayBpms( bpms );
|
||||
|
||||
// maximum BPM is obfuscated, so M-mods will set a playable speed.
|
||||
if( bpms.GetMax() <= 0 )
|
||||
return true;
|
||||
}
|
||||
if( m_fPlayerAutoPlay ) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -787,6 +823,7 @@ RString PlayerOptions::GetSavedPrefsString() const
|
||||
SAVE( m_fTimeSpacing );
|
||||
SAVE( m_fScrollSpeed );
|
||||
SAVE( m_fScrollBPM );
|
||||
SAVE( m_fMaxScrollBPM );
|
||||
SAVE( m_fScrolls[SCROLL_REVERSE] );
|
||||
SAVE( m_fPerspectiveTilt );
|
||||
SAVE( m_bTransforms[TRANSFORM_NOHOLDS] );
|
||||
@@ -798,7 +835,6 @@ RString PlayerOptions::GetSavedPrefsString() const
|
||||
SAVE( m_bTransforms[TRANSFORM_NOSTRETCH] );
|
||||
SAVE( m_bTransforms[TRANSFORM_NOLIFTS] );
|
||||
SAVE( m_bTransforms[TRANSFORM_NOFAKES] );
|
||||
SAVE( m_ScoreDisplay );
|
||||
SAVE( m_bMuteOnError );
|
||||
SAVE( m_sNoteSkin );
|
||||
#undef SAVE
|
||||
@@ -816,6 +852,7 @@ void PlayerOptions::ResetPrefs( ResetPrefsType type )
|
||||
CPY( m_fTimeSpacing );
|
||||
CPY( m_fScrollSpeed );
|
||||
CPY( m_fScrollBPM );
|
||||
CPY( m_fMaxScrollBPM );
|
||||
break;
|
||||
case saved_prefs_invalid_for_course:
|
||||
break;
|
||||
@@ -961,7 +998,20 @@ public:
|
||||
DEFINE_METHOD( GetBlind, m_fBlind )
|
||||
DEFINE_METHOD( GetCover, m_fCover )
|
||||
DEFINE_METHOD( GetRandomAttacks, m_fRandAttack )
|
||||
DEFINE_METHOD( GetSongAttacks, m_fSongAttack )
|
||||
|
||||
static int GetStepAttacks( T *p, lua_State *L )
|
||||
{
|
||||
lua_pushnumber(L,
|
||||
(p->m_fNoAttack > 0 || p->m_fRandAttack > 0 ? 0 : 1 ));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This one is deprecated.
|
||||
static int GetSongAttacks( T *p, lua_State *L )
|
||||
{
|
||||
return GetStepAttacks(p, L);
|
||||
}
|
||||
DEFINE_METHOD( GetNoAttacks, m_fNoAttack )
|
||||
DEFINE_METHOD( GetSkew, m_fSkew )
|
||||
DEFINE_METHOD( GetPassmark, m_fPassmark )
|
||||
DEFINE_METHOD( GetRandomSpeed, m_fRandomSpeed )
|
||||
@@ -988,6 +1038,8 @@ public:
|
||||
// SetSkew
|
||||
ADD_METHOD( GetSongAttacks );
|
||||
// SetSongAttacks
|
||||
ADD_METHOD( GetStepAttacks );
|
||||
ADD_METHOD( GetNoAttacks );
|
||||
ADD_METHOD( GetCMod );
|
||||
ADD_METHOD( GetXMod );
|
||||
|
||||
|
||||
+4
-11
@@ -27,15 +27,14 @@ public:
|
||||
m_fBlind(0), m_SpeedfBlind(1.0f),
|
||||
m_fCover(0), m_SpeedfCover(1.0f),
|
||||
m_fRandAttack(0), m_SpeedfRandAttack(1.0f),
|
||||
m_fSongAttack(0), m_SpeedfSongAttack(1.0f),
|
||||
m_fNoAttack(0), m_SpeedfNoAttack(1.0f),
|
||||
m_fPlayerAutoPlay(0), m_SpeedfPlayerAutoPlay(1.0f),
|
||||
m_bSetTiltOrSkew(false),
|
||||
m_fPerspectiveTilt(0), m_SpeedfPerspectiveTilt(1.0f),
|
||||
m_fSkew(0), m_SpeedfSkew(1.0f),
|
||||
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_bMuteOnError(false), m_FailType(FAIL_IMMEDIATE)
|
||||
{
|
||||
m_sNoteSkin = "";
|
||||
ZERO( m_fAccels ); ONE( m_SpeedfAccels );
|
||||
@@ -151,12 +150,6 @@ public:
|
||||
SCROLL_CENTERED,
|
||||
NUM_SCROLLS
|
||||
};
|
||||
enum ScoreDisplay {
|
||||
SCORING_ADD=0,
|
||||
SCORING_SUBTRACT,
|
||||
SCORING_AVERAGE,
|
||||
NUM_SCOREDISPLAYS
|
||||
};
|
||||
|
||||
float GetReversePercentForColumn( int iCol ) const; // accounts for all Directions
|
||||
|
||||
@@ -164,6 +157,7 @@ public:
|
||||
* PlayerOptions::Approach approaches. */
|
||||
bool m_bSetScrollSpeed; // true if the scroll speed was set by FromString
|
||||
float m_fTimeSpacing, m_SpeedfTimeSpacing; // instead of Beat spacing (CMods, mMods)
|
||||
float m_fMaxScrollBPM, m_SpeedfMaxScrollBPM;
|
||||
float m_fScrollSpeed, m_SpeedfScrollSpeed; // used if !m_bTimeSpacing (xMods)
|
||||
float m_fScrollBPM, m_SpeedfScrollBPM; // used if m_bTimeSpacing (CMod)
|
||||
float m_fAccels[NUM_ACCELS], m_SpeedfAccels[NUM_ACCELS];
|
||||
@@ -174,7 +168,7 @@ public:
|
||||
float m_fBlind, m_SpeedfBlind;
|
||||
float m_fCover, m_SpeedfCover; // hide the background per-player--can't think of a good name
|
||||
float m_fRandAttack, m_SpeedfRandAttack;
|
||||
float m_fSongAttack, m_SpeedfSongAttack;
|
||||
float m_fNoAttack, m_SpeedfNoAttack;
|
||||
float m_fPlayerAutoPlay, m_SpeedfPlayerAutoPlay;
|
||||
bool m_bSetTiltOrSkew; // true if the tilt or skew was set by FromString
|
||||
float m_fPerspectiveTilt, m_SpeedfPerspectiveTilt; // -1 = near, 0 = overhead, +1 = space
|
||||
@@ -198,7 +192,6 @@ public:
|
||||
};
|
||||
/** @brief The method for which a player can fail a song. */
|
||||
FailType m_FailType;
|
||||
ScoreDisplay m_ScoreDisplay;
|
||||
|
||||
/**
|
||||
* @brief The Noteskin to use.
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ void PlayerState::RebuildPlayerOptionsFromActiveAttacks()
|
||||
so.FromString( m_ActiveAttacks[s].sModifiers );
|
||||
}
|
||||
m_PlayerOptions.Assign( ModsLevel_Song, po );
|
||||
if( m_PlayerNumber == GAMESTATE->m_MasterPlayerNumber )
|
||||
if( m_PlayerNumber == GAMESTATE->GetMasterPlayerNumber() )
|
||||
GAMESTATE->m_SongOptions.Assign( ModsLevel_Song, so );
|
||||
|
||||
int iSumOfAttackLevels = GetSumOfActiveAttackLevels();
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
class IniFile;
|
||||
|
||||
void ValidateDisplayAspectRatio( float &val );
|
||||
void ValidateSongsPerPlay( int &val );
|
||||
|
||||
/** @brief How many songs can be played during a normal game max?
|
||||
*
|
||||
* This assumes no extra stages, no event mode, no course modes. */
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@
|
||||
* </li></ul>
|
||||
*/
|
||||
#ifndef PRODUCT_VER_BARE
|
||||
#define PRODUCT_VER_BARE v5.0 Preview 1a
|
||||
#define PRODUCT_VER_BARE v5.0 Preview 2
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@
|
||||
#define PRODUCT_VER PRODUCT_XSTRINGIFY(PRODUCT_VER_BARE)
|
||||
#define PRODUCT_ID_VER PRODUCT_XSTRINGIFY(PRODUCT_ID_VER_BARE)
|
||||
|
||||
#define VIDEO_TROUBLESHOOTING_URL "http://www.stepmania.com/stepmania/mediawiki.php?title=Video_Driver_Troubleshooting"
|
||||
#define VIDEO_TROUBLESHOOTING_URL "http://www.stepmania.com/stepmaniawiki.php?title=Video_Driver_Troubleshooting"
|
||||
/** @brief The URL to report bugs on the program. */
|
||||
#define REPORT_BUG_URL "http://ssc.ajworld.net/sm-ssc/bugtracker/"
|
||||
|
||||
|
||||
+3
-3
@@ -5,12 +5,12 @@
|
||||
|
||||
; see ProductInfo.h for use descriptions
|
||||
!define PRODUCT_ID "StepMania"
|
||||
!define PRODUCT_VER "v5.0 Preview 1a"
|
||||
!define PRODUCT_VER "v5.0 Preview 2"
|
||||
!define PRODUCT_DISPLAY "${PRODUCT_ID} ${PRODUCT_VER}"
|
||||
!define PRODUCT_BITMAP "ssc"
|
||||
!define PRODUCT_BITMAP "sm5"
|
||||
|
||||
!define PRODUCT_URL "http://www.stepmania.com/"
|
||||
!define UPDATES_URL "http://www.stepmania.com/"
|
||||
!define UPDATES_URL "http://code.google.com/p/sm-ssc/downloads/list"
|
||||
|
||||
;!define INSTALL_EXTERNAL_PCKS
|
||||
;!define INSTALL_INTERNAL_PCKS
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ public:
|
||||
bpp(0), rate(0), vsync(false), interlaced(false),
|
||||
bSmoothLines(false), bTrilinearFiltering(false),
|
||||
bAnisotropicFiltering(false), sWindowTitle(RString()),
|
||||
sIconFile(RString()), PAL(false), fDisplayAspectRatio(0.0) {}
|
||||
sIconFile(RString()), PAL(false), fDisplayAspectRatio(0.0f) {}
|
||||
|
||||
bool windowed;
|
||||
int width;
|
||||
|
||||
@@ -31,6 +31,13 @@ using namespace RageDisplay_Legacy_Helpers;
|
||||
#define glFlush()
|
||||
#endif
|
||||
|
||||
RString GetInfoLog( GLhandleARB h );
|
||||
GLhandleARB CompileShader( GLenum ShaderType, RString sFile, vector<RString> asDefines );
|
||||
GLhandleARB LoadShader( GLenum ShaderType, RString sFile, vector<RString> asDefines );
|
||||
void InitShaders();
|
||||
void SetupExtensions();
|
||||
void SetPixelMapForSurface( int glImageFormat, int glTexFormat, const RageSurfacePalette *palette );
|
||||
|
||||
//
|
||||
// Globals
|
||||
//
|
||||
@@ -333,7 +340,7 @@ GLhandleARB CompileShader( GLenum ShaderType, RString sFile, vector<RString> asD
|
||||
GLhandleARB LoadShader( GLenum ShaderType, RString sFile, vector<RString> asDefines )
|
||||
{
|
||||
// Don't do anything here if not the hardware/driver can't do it!
|
||||
if (!GLEW_ARB_fragment_program && GLEW_ARB_shading_language_100 && ShaderType == GL_FRAGMENT_SHADER_ARB)
|
||||
if (!GLEW_ARB_fragment_shader && ShaderType == GL_FRAGMENT_SHADER_ARB)
|
||||
return 0;
|
||||
if (!GLEW_ARB_vertex_shader && ShaderType == GL_VERTEX_SHADER_ARB)
|
||||
return 0;
|
||||
@@ -1655,7 +1662,7 @@ void RageDisplay_Legacy::SetTextureFiltering( TextureUnit tu, bool b )
|
||||
|
||||
void RageDisplay_Legacy::SetEffectMode( EffectMode effect )
|
||||
{
|
||||
if (!GLEW_ARB_fragment_program && !GLEW_ARB_shading_language_100)
|
||||
if (!GLEW_ARB_fragment_shader)
|
||||
return;
|
||||
|
||||
GLhandleARB hShader = 0;
|
||||
@@ -2561,7 +2568,7 @@ RString RageDisplay_Legacy::GetTextureDiagnostics(unsigned iTexture) const
|
||||
void RageDisplay_Legacy::SetAlphaTest(bool b)
|
||||
{
|
||||
// Previously this was 0.01, rather than 0x01.
|
||||
glAlphaFunc(GL_GREATER, 0.00390625 /* 1/256 */);
|
||||
glAlphaFunc(GL_GREATER, 0.00390625f /* 1/256 */);
|
||||
if (b)
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
else
|
||||
@@ -2641,11 +2648,9 @@ void RageDisplay_Legacy::SetSphereEnvironmentMapping(TextureUnit tu, bool b)
|
||||
}
|
||||
}
|
||||
|
||||
GLint iCelTexture1, iCelTexture2 = 0;
|
||||
|
||||
void RageDisplay_Legacy::SetCelShaded( int stage )
|
||||
{
|
||||
if (!GLEW_ARB_fragment_program && !GL_ARB_shading_language_100)
|
||||
if (!GLEW_ARB_fragment_shader)
|
||||
return; // not supported
|
||||
|
||||
switch (stage)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user