Files
itgmania212121/stepmania/src/Steps.cpp
T

385 lines
9.1 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-05-20 08:59:37 +00:00
/*
-----------------------------------------------------------------------------
2003-08-03 00:57:20 +00:00
Class: Steps
2002-05-20 08:59:37 +00:00
2002-07-27 19:29:51 +00:00
Desc: See header.
2002-05-20 08:59:37 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-07-27 19:29:51 +00:00
Chris Danford
2003-01-02 22:10:51 +00:00
Glenn Maynard
2002-05-20 08:59:37 +00:00
-----------------------------------------------------------------------------
*/
2003-08-03 00:57:20 +00:00
#include "Steps.h"
2003-02-16 04:28:17 +00:00
#include "song.h"
2003-08-03 00:57:20 +00:00
#include "Steps.h"
2002-05-20 08:59:37 +00:00
#include "IniFile.h"
#include "math.h" // for fabs()
#include "RageUtil.h"
#include "RageLog.h"
2002-07-03 03:13:13 +00:00
#include "NoteData.h"
2002-05-20 08:59:37 +00:00
#include "GameInput.h"
2002-07-27 19:29:51 +00:00
#include "RageException.h"
#include "MsdFile.h"
2002-08-23 00:22:27 +00:00
#include "GameManager.h"
2003-08-07 06:36:34 +00:00
#include "NoteDataUtil.h"
2002-05-20 08:59:37 +00:00
2003-08-03 00:57:20 +00:00
Steps::Steps()
2002-05-20 08:59:37 +00:00
{
2003-08-07 06:16:17 +00:00
/* FIXME: should we init this to STEPS_TYPE_INVALID?
2002-08-23 00:22:27 +00:00
* I have a feeling that it's the right thing to do but that
* it'd trip obscure asserts all over the place, so I'll wait
* until after b6 to do this. -glenn */
2003-08-07 06:36:34 +00:00
m_StepsType = STEPS_TYPE_DANCE_SINGLE;
m_Difficulty = DIFFICULTY_INVALID;
2002-05-20 08:59:37 +00:00
m_iMeter = 0;
2003-01-30 07:18:33 +00:00
for(int i = 0; i < NUM_RADAR_CATEGORIES; ++i)
m_fRadarValues[i] = -1; /* unknown */
2002-06-30 23:19:33 +00:00
2002-12-14 21:43:00 +00:00
notes = NULL;
notes_comp = NULL;
2003-01-02 22:10:51 +00:00
parent = NULL;
2002-05-20 08:59:37 +00:00
}
2003-08-03 00:57:20 +00:00
Steps::~Steps()
2002-05-20 08:59:37 +00:00
{
2002-12-14 21:43:00 +00:00
delete notes;
delete notes_comp;
2002-05-20 08:59:37 +00:00
}
2003-10-04 04:55:59 +00:00
void Steps::SetNoteData( const NoteData* pNewNoteData )
2002-07-03 21:27:26 +00:00
{
2003-08-07 06:36:34 +00:00
ASSERT( pNewNoteData->GetNumTracks() == GameManager::NotesTypeToNumTracks(m_StepsType) );
2002-12-14 21:43:00 +00:00
2003-01-02 22:10:51 +00:00
DeAutogen();
2002-12-14 21:43:00 +00:00
delete notes_comp;
notes_comp = NULL;
delete notes;
notes = new NoteData(*pNewNoteData);
2002-07-03 21:27:26 +00:00
}
2002-05-20 08:59:37 +00:00
2003-08-03 00:57:20 +00:00
void Steps::GetNoteData( NoteData* pNoteDataOut ) const
2002-07-03 03:13:13 +00:00
{
2003-01-19 21:14:27 +00:00
ASSERT(this);
2003-01-19 21:13:00 +00:00
ASSERT(pNoteDataOut);
2003-07-15 19:40:41 +00:00
2002-12-14 21:43:00 +00:00
Decompress();
2003-07-15 19:40:41 +00:00
if( notes != NULL )
*pNoteDataOut = *notes;
else
{
pNoteDataOut->ClearAll();
2003-08-07 06:36:34 +00:00
pNoteDataOut->SetNumTracks( GameManager::NotesTypeToNumTracks(m_StepsType) );
2003-07-15 19:40:41 +00:00
}
2002-07-03 03:13:13 +00:00
}
2002-05-20 08:59:37 +00:00
2003-08-03 00:57:20 +00:00
void Steps::SetSMNoteData( const CString &out )
2002-12-14 21:33:29 +00:00
{
2002-12-14 21:43:00 +00:00
delete notes;
notes = NULL;
if(!notes_comp)
notes_comp = new CString;
*notes_comp = out;
2002-12-14 21:33:29 +00:00
}
2003-08-03 00:57:20 +00:00
CString Steps::GetSMNoteData() const
2002-12-14 21:33:29 +00:00
{
2002-12-14 21:43:00 +00:00
if(!notes_comp)
{
if(!notes) return ""; /* no data is no data */
notes_comp = new CString;
NoteDataUtil::GetSMNoteDataString( *notes, *notes_comp );
2002-12-14 21:43:00 +00:00
}
return *notes_comp;
2002-12-14 21:33:29 +00:00
}
2003-08-03 00:57:20 +00:00
void Steps::TidyUpData()
2002-10-06 16:56:58 +00:00
{
if( GetDifficulty() == DIFFICULTY_INVALID )
SetDifficulty(StringToDifficulty(GetDescription()));
if( GetDifficulty() == DIFFICULTY_INVALID )
{
if( GetMeter() == 1 ) SetDifficulty(DIFFICULTY_BEGINNER);
else if( GetMeter() <= 3 ) SetDifficulty(DIFFICULTY_EASY);
else if( GetMeter() <= 6 ) SetDifficulty(DIFFICULTY_MEDIUM);
else SetDifficulty(DIFFICULTY_HARD);
2002-10-06 16:56:58 +00:00
}
// Meter is overflowing (invalid), but some files (especially maniac/smaniac steps) are purposefully set higher than 10.
// See: BMR's Gravity; we probably should keep those as difficult as we can represent.
2003-02-09 01:30:06 +00:00
/* Why? If the data file says a meter of 72, we should keep it as 72; if
* individual bits of code (eg. scoring, feet) have maximums, they should
* enforce it internally. Doing it here will make us lose the difficulty
* completely if the song is edited and written. -glenn */
/* if( GetMeter() >10 ) {
if( GetDifficulty() == DIFFICULTY_HARD || GetDifficulty() == DIFFICULTY_CHALLENGE)
SetMeter(10);
else
SetMeter(0);
2003-02-09 01:30:06 +00:00
} */
if( GetMeter() < 1) // meter is invalid
2002-07-03 03:13:13 +00:00
{
// guess meter from difficulty class
2003-01-02 22:10:51 +00:00
switch( GetDifficulty() )
2002-06-30 23:19:33 +00:00
{
case DIFFICULTY_BEGINNER: SetMeter(1); break;
case DIFFICULTY_EASY: SetMeter(3); break;
case DIFFICULTY_MEDIUM: SetMeter(5); break;
case DIFFICULTY_HARD: SetMeter(8); break;
case DIFFICULTY_CHALLENGE: SetMeter(8); break;
case DIFFICULTY_INVALID: SetMeter(5); break;
2002-07-03 03:13:13 +00:00
default: ASSERT(0);
2002-06-30 23:19:33 +00:00
}
}
if( m_sDescription.empty() )
{
m_sDescription = Capitalize( DifficultyToString(m_Difficulty) );
}
2002-05-20 08:59:37 +00:00
}
2003-08-03 00:57:20 +00:00
void Steps::Decompress() const
2002-12-14 21:43:00 +00:00
{
2003-03-13 01:26:50 +00:00
if(notes)
{
return; // already decompressed
}
else if(parent)
2003-01-02 22:10:51 +00:00
{
2003-03-13 01:26:50 +00:00
// get autogen notes
2003-01-02 22:10:51 +00:00
NoteData pdata;
parent->GetNoteData(&pdata);
notes = new NoteData;
int iNewTracks = GameManager::NotesTypeToNumTracks(m_StepsType);
if( pdata.GetNumTracks() > iNewTracks)
2003-08-07 06:36:34 +00:00
{
notes->SetNumTracks( pdata.GetNumTracks() );
notes->CopyRange( &pdata, 0, pdata.GetLastRow(), 0 );
const int iOriginalTracks = pdata.GetNumTracks();
const bool iUnevenTracks = (iOriginalTracks % iNewTracks) != 0;
const int iTracksToOverlap = iOriginalTracks / iNewTracks;
if( iTracksToOverlap )
{
// if we have at least as many tracks in the old mode
// as we do in the mode we're going to
for (int ix = 0; ix < iNewTracks; ix++)
{
for (int iy = 0; iy < iTracksToOverlap; iy++)
{
notes->CombineTracks(ix, (ix + iy * iNewTracks));
}
}
if( iUnevenTracks )
{
for (int ix = iOriginalTracks - iUnevenTracks; ix < iOriginalTracks; ix++)
{
// spread out the remaining tracks evenly
notes->CombineTracks((ix * iOriginalTracks) % iNewTracks, ix);
}
}
}
notes->SetNumTracks( iNewTracks );
} else
notes->LoadTransformedSlidingWindow( &pdata, iNewTracks );
NoteDataUtil::FixImpossibleRows( *notes, m_StepsType );
2003-01-02 22:10:51 +00:00
}
2003-03-13 01:26:50 +00:00
else if(!notes_comp)
{
2003-08-07 06:16:17 +00:00
/* there is no data, do nothing */
2003-03-13 01:26:50 +00:00
}
else
{
// load from compressed
notes = new NoteData;
2003-08-07 06:36:34 +00:00
notes->SetNumTracks( GameManager::NotesTypeToNumTracks(m_StepsType) );
2003-01-02 22:10:51 +00:00
2003-03-13 01:26:50 +00:00
NoteDataUtil::LoadFromSMNoteDataString(*notes, *notes_comp);
}
2002-12-14 21:43:00 +00:00
}
2003-08-03 00:57:20 +00:00
void Steps::Compress() const
2002-12-14 21:43:00 +00:00
{
if(!notes_comp)
{
if(!notes) return; /* no data is no data */
notes_comp = new CString;
NoteDataUtil::GetSMNoteDataString( *notes, *notes_comp );
2002-12-14 21:43:00 +00:00
}
delete notes;
notes = NULL;
}
2003-01-02 22:10:51 +00:00
/* Copy our parent's data. This is done when we're being changed from autogen
* to normal. (needed?) */
2003-08-03 00:57:20 +00:00
void Steps::DeAutogen()
2003-01-02 22:10:51 +00:00
{
if(!parent)
return; /* OK */
2003-03-13 01:26:50 +00:00
Decompress(); // fills in notes with sliding window transform
2003-01-02 22:10:51 +00:00
m_iMeter = Real()->m_iMeter;
m_sDescription = Real()->m_sDescription;
m_Difficulty = Real()->m_Difficulty;
2003-01-30 07:18:33 +00:00
for(int i = 0; i < NUM_RADAR_CATEGORIES; ++i)
2003-01-02 22:10:51 +00:00
m_fRadarValues[i] = Real()->m_fRadarValues[i];
parent = NULL;
2003-03-13 01:26:50 +00:00
Compress();
2003-01-02 22:10:51 +00:00
}
2003-08-07 06:16:17 +00:00
void Steps::AutogenFrom( Steps *parent_, StepsType ntTo )
2003-01-02 22:10:51 +00:00
{
parent = parent_;
2003-08-07 06:36:34 +00:00
m_StepsType = ntTo;
2003-01-02 22:10:51 +00:00
}
2003-08-07 06:16:17 +00:00
void Steps::CopyFrom( Steps* pSource, StepsType ntTo ) // pSource does not have to be of the same StepsType!
2003-01-30 07:18:33 +00:00
{
2003-08-07 06:36:34 +00:00
m_StepsType = ntTo;
2003-01-30 07:18:33 +00:00
NoteData noteData;
pSource->GetNoteData( &noteData );
2003-02-01 05:16:38 +00:00
noteData.SetNumTracks( GameManager::NotesTypeToNumTracks(ntTo) );
2003-01-30 07:18:33 +00:00
this->SetNoteData( &noteData );
this->SetDescription( "Copied from "+pSource->GetDescription() );
this->SetDifficulty( pSource->GetDifficulty() );
this->SetMeter( pSource->GetMeter() );
const float* radarValues = pSource->GetRadarValues();
for( int r=0; r<NUM_RADAR_CATEGORIES; r++ )
this->SetRadarValue( (RadarCategory)r, radarValues[r] );
}
2003-08-07 06:16:17 +00:00
void Steps::CreateBlank( StepsType ntTo )
2003-01-30 07:18:33 +00:00
{
2003-08-07 06:36:34 +00:00
m_StepsType = ntTo;
2003-01-30 07:18:33 +00:00
NoteData noteData;
2003-02-01 05:16:38 +00:00
noteData.SetNumTracks( GameManager::NotesTypeToNumTracks(ntTo) );
2003-01-30 07:18:33 +00:00
this->SetNoteData( &noteData );
}
2003-08-03 00:57:20 +00:00
const Steps *Steps::Real() const
2003-01-02 22:10:51 +00:00
{
if(parent) return parent;
return this;
}
2003-08-03 00:57:20 +00:00
bool Steps::IsAutogen() const
2003-01-02 22:10:51 +00:00
{
return parent != NULL;
}
2003-08-03 00:57:20 +00:00
void Steps::SetDescription(CString desc)
2003-01-02 22:10:51 +00:00
{
DeAutogen();
m_sDescription = desc;
}
2003-08-03 00:57:20 +00:00
void Steps::SetDifficulty(Difficulty d)
2003-01-02 22:10:51 +00:00
{
DeAutogen();
m_Difficulty = d;
}
2003-08-03 00:57:20 +00:00
void Steps::SetMeter(int meter)
2003-01-02 22:10:51 +00:00
{
DeAutogen();
m_iMeter = meter;
}
2003-08-03 00:57:20 +00:00
void Steps::SetRadarValue(int r, float val)
2003-01-02 22:10:51 +00:00
{
DeAutogen();
2003-01-30 07:18:33 +00:00
ASSERT(r < NUM_RADAR_CATEGORIES);
2003-01-02 22:10:51 +00:00
m_fRadarValues[r] = val;
}
2003-08-07 06:36:34 +00:00
//
// Sorting stuff
//
2003-08-03 00:57:20 +00:00
bool CompareNotesPointersByRadarValues(const Steps* pNotes1, const Steps* pNotes2)
{
float fScore1 = 0;
float fScore2 = 0;
for( int r=0; r<NUM_RADAR_CATEGORIES; r++ )
{
fScore1 += pNotes1->GetRadarValues()[r];
fScore2 += pNotes2->GetRadarValues()[r];
}
return fScore1 < fScore2;
}
2003-08-03 00:57:20 +00:00
bool CompareNotesPointersByMeter(const Steps *pNotes1, const Steps* pNotes2)
{
return pNotes1->GetMeter() < pNotes2->GetMeter();
}
2003-08-03 00:57:20 +00:00
bool CompareNotesPointersByDifficulty(const Steps *pNotes1, const Steps *pNotes2)
{
return pNotes1->GetDifficulty() < pNotes2->GetDifficulty();
}
2003-08-03 00:57:20 +00:00
void SortNotesArrayByDifficulty( vector<Steps*> &arraySteps )
{
/* Sort in reverse order of priority. */
stable_sort( arraySteps.begin(), arraySteps.end(), CompareNotesPointersByRadarValues );
stable_sort( arraySteps.begin(), arraySteps.end(), CompareNotesPointersByMeter );
stable_sort( arraySteps.begin(), arraySteps.end(), CompareNotesPointersByDifficulty );
2003-04-24 00:42:21 +00:00
}
2003-10-14 22:42:23 +00:00
2003-10-19 21:38:11 +00:00
bool Steps::MemCardData::HighScore::operator>=( const Steps::MemCardData::HighScore& other ) const
2003-10-14 22:42:23 +00:00
{
2003-10-20 01:06:26 +00:00
return iScore >= other.iScore;
2003-10-14 22:42:23 +00:00
/* Make sure we treat AAAA as higher than AAA, even though the score
* is the same.
*
* XXX: Isn't it possible to beat the grade but not beat the score, since
* grading and scores are on completely different systems? Should we be
* checking for these completely separately? */
// if( vsScore > this->fScore )
// return true;
// if( vsScore < this->fScore )
// return false;
// return vsGrade > this->grade;
}
void Steps::MemCardData::AddHighScore( Steps::MemCardData::HighScore hs, int &iIndexOut )
{
int i;
for( i=0; i<(int)vHighScores.size(); i++ )
{
2003-10-19 21:38:11 +00:00
if( hs >= vHighScores[i] ) // tie goes to new score
2003-10-14 22:42:23 +00:00
break;
}
if( i < NUM_RANKING_LINES )
{
vHighScores.insert( vHighScores.begin()+i, hs );
iIndexOut = i;
if( vHighScores.size() > NUM_RANKING_LINES )
vHighScores.erase( vHighScores.begin()+NUM_RANKING_LINES, vHighScores.end() );
}
}