diff --git a/stepmania/src/Course.cpp b/stepmania/src/Course.cpp index e3117bb326..a549838ccb 100644 --- a/stepmania/src/Course.cpp +++ b/stepmania/src/Course.cpp @@ -551,6 +551,8 @@ Trail* Course::GetTrail( StepsType nt, CourseDifficulty cd ) const int CurSong = 0; /* Current offset into AllSongsShuffled */ Trail trail; + trail.m_StepsType = nt; + trail.m_CourseDifficulty = cd; for( unsigned i=0; i &asFeatsOu // Find Machine Records { Profile* pProfile = PROFILEMAN->GetMachineProfile(); - HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, nt, cd ); + Trail *pTrail = pCourse->GetTrail( nt, cd ); + HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, pTrail ); for( unsigned i=0; i &asFeatsOu // Find Personal Records if( PROFILEMAN->IsUsingProfile( pn ) ) { - HighScoreList &hsl = pProf->GetCourseHighScoreList( pCourse, nt, cd ); + Trail *pTrail = pCourse->GetTrail( nt, cd ); + HighScoreList &hsl = pProf->GetCourseHighScoreList( pCourse, pTrail ); for( unsigned i=0; iGetCurrentStyleDef()->m_StepsType; const Course *pCourse = GAMESTATE->m_pCurCourse; const CourseDifficulty cd = GAMESTATE->m_PreferredCourseDifficulty[m_PlayerNumber]; + const Trail *pTrail = pCourse ? pCourse->GetTrail(st,cd) : NULL; float val = 0; CString str; @@ -218,7 +219,7 @@ void PaneDisplay::SetContent( PaneContents c ) case COURSE_MACHINE_HIGH_NAME: /* set val for color */ case COURSE_MACHINE_HIGH_SCORE: - val = 100.0f * PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList(pCourse,st,cd).GetTopScore().fPercentDP; + val = 100.0f * PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList(pCourse,pTrail).GetTopScore().fPercentDP; break; case COURSE_MACHINE_NUM_PLAYS: @@ -234,7 +235,7 @@ void PaneDisplay::SetContent( PaneContents c ) break; case COURSE_PROFILE_HIGH_SCORE: - val = 100.0f * PROFILEMAN->GetProfile(m_PlayerNumber)->GetCourseHighScoreList(pCourse,st,cd).GetTopScore().fPercentDP; + val = 100.0f * PROFILEMAN->GetProfile(m_PlayerNumber)->GetCourseHighScoreList(pCourse,pTrail).GetTopScore().fPercentDP; break; case COURSE_PROFILE_NUM_PLAYS: val = (float) PROFILEMAN->GetProfile(m_PlayerNumber)->GetCourseNumTimesPlayed( pCourse ); @@ -267,7 +268,8 @@ void PaneDisplay::SetContent( PaneContents c ) str = PROFILEMAN->GetMachineProfile()->GetStepsHighScoreList(pSong,pSteps).GetTopScore().sName; break; case COURSE_MACHINE_HIGH_NAME: - str = PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList(pCourse,st,cd).GetTopScore().sName; + + str = PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList(pCourse,pTrail).GetTopScore().sName; break; case SONG_MACHINE_HIGH_SCORE: diff --git a/stepmania/src/Profile.h b/stepmania/src/Profile.h index 23dce760ec..a45a4c2ecd 100644 --- a/stepmania/src/Profile.h +++ b/stepmania/src/Profile.h @@ -22,6 +22,7 @@ #include "SongUtil.h" // for SongID #include "StepsUtil.h" // for StepsID #include "CourseUtil.h" // for CourseID +#include "TrailUtil.h" // for TrailID struct XNode; @@ -150,18 +151,22 @@ public: // // struct was a typedef'd array of HighScores, but VC6 freaks out // in processing the templates for map::operator[]. + struct HighScoresForATrail + { + HighScoreList hs; + }; struct HighScoresForACourse { - HighScoreList hs[NUM_STEPS_TYPES][NUM_COURSE_DIFFICULTIES]; + std::map m_TrailHighScores; }; std::map m_CourseHighScores; - void AddCourseHighScore( const Course* pCourse, StepsType st, CourseDifficulty cd, HighScore hs, int &iIndexOut ); - HighScoreList& GetCourseHighScoreList( const Course* pCourse, StepsType st, CourseDifficulty cd ); - const HighScoreList& GetCourseHighScoreList( const Course* pCourse, StepsType st, CourseDifficulty cd ) const; + void AddCourseHighScore( const Course* pCourse, const Trail* pTrail, HighScore hs, int &iIndexOut ); + HighScoreList& GetCourseHighScoreList( const Course* pCourse, const Trail* pTrail ); + const HighScoreList& GetCourseHighScoreList( const Course* pCourse, const Trail* pTrail ) const; int GetCourseNumTimesPlayed( const Course* pCourse ) const; int GetCourseNumTimesPlayed( const CourseID& courseID ) const; - void IncrementCoursePlayCount( const Course* pCourse, StepsType st, CourseDifficulty cd ); + void IncrementCoursePlayCount( const Course* pCourse, const Trail* pTrail ); // @@ -267,6 +272,7 @@ public: struct HighScoreForACourse { CourseID courseID; + TrailID trailID; HighScore hs; HighScoreForACourse() { Unset(); } @@ -276,7 +282,7 @@ public: void LoadFromNode( const XNode* pNode ); }; vector m_vRecentCourseScores; - void AddCourseRecentScore( const Course* pCourse, StepsType st, CourseDifficulty cd, HighScore hs ); + void AddCourseRecentScore( const Course* pCourse, const Trail* pTrail, HighScore hs ); // // Init'ing @@ -311,11 +317,6 @@ public: bool LoadAllFromDir( CString sDir, bool bRequireSignature ); // return false on error bool SaveAllToDir( CString sDir, bool bSignData ) const; - void LoadProfileDataFromDirSM390a12( CString sDir ); - void LoadSongScoresFromDirSM390a12( CString sDir ); - void LoadCourseScoresFromDirSM390a12( CString sDir ); - void LoadCategoryScoresFromDirSM390a12( CString sDir ); - void LoadEditableDataFromDir( CString sDir ); void LoadGeneralDataFromNode( const XNode* pNode ); void LoadSongScoresFromNode( const XNode* pNode ); @@ -338,16 +339,12 @@ public: XNode* SaveRecentSongScoresCreateNode() const; XNode* SaveRecentCourseScoresCreateNode() const; - void DeleteProfileDataFromDirSM390a12( CString sDir ) const; - void DeleteSongScoresFromDirSM390a12( CString sDir ) const; - void DeleteCourseScoresFromDirSM390a12( CString sDir ) const; - void DeleteCategoryScoresFromDirSM390a12( CString sDir ) const; - void SaveStatsWebPageToDir( CString sDir ) const; void SaveMachinePublicKeyToDir( CString sDir ) const; private: const HighScoresForASong *GetHighScoresForASong( const SongID& songID ) const; + const HighScoresForACourse *GetHighScoresForACourse( const CourseID& courseID ) const; }; diff --git a/stepmania/src/ProfileHtml.cpp b/stepmania/src/ProfileHtml.cpp index 95df40ab7e..ec37644a14 100644 --- a/stepmania/src/ProfileHtml.cpp +++ b/stepmania/src/ProfileHtml.cpp @@ -737,7 +737,11 @@ bool PrintHighScoresForCourse( RageFile &f, const Profile *pProfile, Course* pCo { FOREACH_ShownCourseDifficulty( cd ) { - const HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, st, cd ); + Trail *pTrail = pCourse->GetTrail( st, cd ); + if( pTrail == NULL ) + continue; + + const HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, pTrail ); if( hsl.vHighScores.empty() ) continue; @@ -886,7 +890,8 @@ bool PrintPercentCompleteForStepsType( RageFile &f, const Profile *pProfile, Ste FOREACH_ShownCourseDifficulty( cd ) { - if( pCourse->HasCourseDifficulty(st,cd) ) + Trail *pTrail = pCourse->GetTrail( st, cd ); + if( pTrail ) { TranslatedWrite(f,""); /* HACK: Course::GetMeter() requires that a style be set, since @@ -896,7 +901,7 @@ bool PrintPercentCompleteForStepsType( RageFile &f, const Profile *pProfile, Ste GAMESTATE->m_CurStyle = STYLE_DANCE_SINGLE; float fMeter = pCourse->GetMeter(st,cd); TranslatedWrite(f, ssprintf("(%.2f)",fMeter) ); - HighScore hs = pProfile->GetCourseHighScoreList(pCourse, st, cd).GetTopScore(); + HighScore hs = pProfile->GetCourseHighScoreList(pCourse,pTrail).GetTopScore(); Grade grade = hs.grade; if( grade != GRADE_NO_DATA ) { diff --git a/stepmania/src/ProfileManager.cpp b/stepmania/src/ProfileManager.cpp index 45b2ab2057..88071543a0 100644 --- a/stepmania/src/ProfileManager.cpp +++ b/stepmania/src/ProfileManager.cpp @@ -498,29 +498,29 @@ HighScore ProfileManager::GetHighScoreForDifficulty( const Song *s, const StyleD // // Course stats // -void ProfileManager::AddCourseScore( const Course* pCourse, StepsType st, CourseDifficulty cd, PlayerNumber pn, HighScore hs, int &iPersonalIndexOut, int &iMachineIndexOut ) +void ProfileManager::AddCourseScore( const Course* pCourse, const Trail* pTrail, PlayerNumber pn, HighScore hs, int &iPersonalIndexOut, int &iMachineIndexOut ) { // Don't use a minimum percentage for Course scores // if( hs.fPercentDP >= PREFSMAN->m_fMinPercentageForHighScore ) { hs.sName = RANKING_TO_FILL_IN_MARKER[pn]; if( PROFILEMAN->IsUsingProfile(pn) ) - PROFILEMAN->GetProfile(pn)->AddCourseHighScore( pCourse, st, cd, hs, iPersonalIndexOut ); + PROFILEMAN->GetProfile(pn)->AddCourseHighScore( pCourse, pTrail, hs, iPersonalIndexOut ); else iPersonalIndexOut = -1; - PROFILEMAN->GetMachineProfile()->AddCourseHighScore( pCourse, st, cd, hs, iMachineIndexOut ); + PROFILEMAN->GetMachineProfile()->AddCourseHighScore( pCourse, pTrail, hs, iMachineIndexOut ); } if( PROFILEMAN->IsUsingProfile(pn) ) - PROFILEMAN->GetProfile(pn)->AddCourseRecentScore( pCourse, st, cd, hs ); - PROFILEMAN->GetMachineProfile()->AddCourseRecentScore( pCourse, st, cd, hs ); + PROFILEMAN->GetProfile(pn)->AddCourseRecentScore( pCourse, pTrail, hs ); + PROFILEMAN->GetMachineProfile()->AddCourseRecentScore( pCourse, pTrail, hs ); } -void ProfileManager::IncrementCoursePlayCount( const Course* pCourse, StepsType st, CourseDifficulty cd, PlayerNumber pn ) +void ProfileManager::IncrementCoursePlayCount( const Course* pCourse, const Trail* pTrail, PlayerNumber pn ) { if( PROFILEMAN->IsUsingProfile(pn) ) - PROFILEMAN->GetProfile(pn)->IncrementCoursePlayCount( pCourse, st, cd ); - PROFILEMAN->GetMachineProfile()->IncrementCoursePlayCount( pCourse, st, cd ); + PROFILEMAN->GetProfile(pn)->IncrementCoursePlayCount( pCourse, pTrail ); + PROFILEMAN->GetMachineProfile()->IncrementCoursePlayCount( pCourse, pTrail ); } diff --git a/stepmania/src/ProfileManager.h b/stepmania/src/ProfileManager.h index 44a287c140..8afa589a1c 100644 --- a/stepmania/src/ProfileManager.h +++ b/stepmania/src/ProfileManager.h @@ -77,8 +77,8 @@ public: // // Course stats // - void AddCourseScore( const Course* pCourse, StepsType st, CourseDifficulty cd, PlayerNumber pn, HighScore hs, int &iPersonalIndexOut, int &iMachineIndexOut ); - void IncrementCoursePlayCount( const Course* pCourse, StepsType st, CourseDifficulty cd, PlayerNumber pn ); + void AddCourseScore( const Course* pCourse, const Trail* pTrail, PlayerNumber pn, HighScore hs, int &iPersonalIndexOut, int &iMachineIndexOut ); + void IncrementCoursePlayCount( const Course* pCourse, const Trail* pTrail, PlayerNumber pn ); // // Category stats diff --git a/stepmania/src/ScreenEvaluation.cpp b/stepmania/src/ScreenEvaluation.cpp index 9648c1bec9..b18940af26 100644 --- a/stepmania/src/ScreenEvaluation.cpp +++ b/stepmania/src/ScreenEvaluation.cpp @@ -946,15 +946,17 @@ void ScreenEvaluation::CommitScores( case course: { Course* pCourse = GAMESTATE->m_pCurCourse; - CourseDifficulty cd = GAMESTATE->m_PreferredCourseDifficulty[p]; ASSERT( pCourse ); + CourseDifficulty cd = GAMESTATE->m_PreferredCourseDifficulty[p]; + StepsType st = GAMESTATE->GetCurrentStyleDef()->m_StepsType; + Trail* pTrail = pCourse->GetTrail( st, cd ); // don't save scores for a failed Nonstop // DO save scores for a failed Oni/Endless if( stageStats.bFailed[p] && pCourse->IsNonstop() ) continue; - PROFILEMAN->AddCourseScore( pCourse, nt, cd, p, hs, iPersonalHighScoreIndexOut[p], iMachineHighScoreIndexOut[p] ); + PROFILEMAN->AddCourseScore( pCourse, pTrail, p, hs, iPersonalHighScoreIndexOut[p], iMachineHighScoreIndexOut[p] ); } break; default: @@ -976,7 +978,7 @@ void ScreenEvaluation::CommitScores( HighScore &hs = m_HighScore[p]; Profile* pProfile = PROFILEMAN->GetMachineProfile(); - StepsType nt = GAMESTATE->GetCurrentStyleDef()->m_StepsType; + StepsType st = GAMESTATE->GetCurrentStyleDef()->m_StepsType; const HighScoreList *pHSL = NULL; switch( m_Type ) @@ -990,15 +992,17 @@ void ScreenEvaluation::CommitScores( break; case summary: { - pHSL = &pProfile->GetCategoryHighScoreList( nt, rcOut[p] ); + pHSL = &pProfile->GetCategoryHighScoreList( st, rcOut[p] ); } break; case course: { Course* pCourse = GAMESTATE->m_pCurCourse; CourseDifficulty cd = GAMESTATE->m_PreferredCourseDifficulty[p]; + Trail *pTrail = pCourse->GetTrail( st, cd ); ASSERT( pCourse ); - pHSL = &pProfile->GetCourseHighScoreList( pCourse, nt, cd ); + ASSERT( pTrail ); + pHSL = &pProfile->GetCourseHighScoreList( pCourse, pTrail ); } break; default: diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 51d3f7b415..3377663a79 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -188,8 +188,14 @@ void ScreenGameplay::Init() /* Increment the play count. */ if( !m_bDemonstration ) + { FOREACH_EnabledPlayer(p) - PROFILEMAN->IncrementCoursePlayCount( pCourse, st, GAMESTATE->m_PreferredCourseDifficulty[p], (PlayerNumber)p ); + { + CourseDifficulty cd = GAMESTATE->m_PreferredCourseDifficulty[p]; + Trail* pTrail = pCourse->GetTrail( st, cd ); + PROFILEMAN->IncrementCoursePlayCount( pCourse, pTrail, p ); + } + } m_apSongsQueue.clear(); PlayerNumber pnMaster = GAMESTATE->m_MasterPlayerNumber; diff --git a/stepmania/src/ScreenNameEntryTraditional.cpp b/stepmania/src/ScreenNameEntryTraditional.cpp index 6ec22ca4b5..b63f9302cf 100644 --- a/stepmania/src/ScreenNameEntryTraditional.cpp +++ b/stepmania/src/ScreenNameEntryTraditional.cpp @@ -322,6 +322,10 @@ ScreenNameEntryTraditional::ScreenNameEntryTraditional( CString sClassName ) : S Song* pSong = ss.pSong; Steps* pSteps = ss.pSteps[p]; Course* pCourse = GAMESTATE->m_pCurCourse; + StepsType st = GAMESTATE->GetCurrentStyleDef()->m_StepsType; + CourseDifficulty cd = GAMESTATE->m_PreferredCourseDifficulty[p]; + Trail* pTrail = pCourse->GetTrail( st, cd ); + int iHighScoreIndex = -1; // -1 means "out of ranking" Grade grade = ss.GetGrade( p ); int iScore = ss.iScore[p]; @@ -337,7 +341,7 @@ ScreenNameEntryTraditional::ScreenNameEntryTraditional( CString sClassName ) : S const HighScoreList& hsl = GAMESTATE->IsCourseMode() ? - PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList(pCourse, GAMESTATE->GetCurrentStyleDef()->m_StepsType, GAMESTATE->m_PreferredCourseDifficulty[p]) : + PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList(pCourse, pTrail) : PROFILEMAN->GetMachineProfile()->GetStepsHighScoreList(pSong,pSteps); for( unsigned h=0; hGetMachineProfile()->GetCourseHighScoreList( pts.pCourse, pts.nt, pts.cd ); + Trail *pTrail = pts.pCourse->GetTrail( pts.nt, pts.cd ); + ASSERT( pTrail ); + const HighScoreList &hsl = PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList( pts.pCourse, pTrail ); for( int l=0; lm_textSongTitle.SetText( pCourse->m_sName ); FOREACH_ShownCourseDifficulty( cd ) { - const HighScoreList &hsl = PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList( pCourse, pts.nt, cd ); + Trail *pTrail = pCourse->GetTrail( pts.nt, cd ); + const HighScoreList &hsl = PROFILEMAN->GetMachineProfile()->GetCourseHighScoreList( pCourse, pTrail ); BitmapText* pTextStepsScore = &pCourseScoreRowItem->m_textStepsScore[cd]; HighScore hs; diff --git a/stepmania/src/ScreenSelectCourse.cpp b/stepmania/src/ScreenSelectCourse.cpp index 3bbae58f26..5b205a4163 100644 --- a/stepmania/src/ScreenSelectCourse.cpp +++ b/stepmania/src/ScreenSelectCourse.cpp @@ -416,7 +416,8 @@ void ScreenSelectCourse::AfterCourseChange() * have an opinion on which should be used here for * each of oni, endless, nonstop -- * should this choice be an option or a metric? */ - const HighScoreList& hsl = pProfile->GetCourseHighScoreList( pCourse, st, cd ); + Trail *pTrail = pCourse->GetTrail( st, cd ); + const HighScoreList& hsl = pProfile->GetCourseHighScoreList( pCourse, pTrail ); if ( pCourse->IsOni() || pCourse->IsEndless() ) { /* use survive time */ diff --git a/stepmania/src/StepsUtil.cpp b/stepmania/src/StepsUtil.cpp index c3441cb40d..cdaf15f09f 100644 --- a/stepmania/src/StepsUtil.cpp +++ b/stepmania/src/StepsUtil.cpp @@ -123,6 +123,7 @@ void StepsUtil::SortStepsByTypeAndDifficulty( vector &arraySongPointers sort( arraySongPointers.begin(), arraySongPointers.end(), CompareStepsPointersByTypeAndDifficulty ); } + void StepsID::FromSteps( const Steps *p ) { if( p == NULL ) diff --git a/stepmania/src/Trail.h b/stepmania/src/Trail.h index ab266daa2b..b1a1f61c75 100644 --- a/stepmania/src/Trail.h +++ b/stepmania/src/Trail.h @@ -1,5 +1,15 @@ #ifndef Trail_H #define Trail_H +/* +----------------------------------------------------------------------------- + Class: TrialUtil + + Desc: + + Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. + Chris Danford +----------------------------------------------------------------------------- +*/ #include "Attack.h" @@ -30,14 +40,14 @@ struct TrailEntry class Trail { public: - StepsType m_st; - CourseDifficulty m_cd; + StepsType m_StepsType; + CourseDifficulty m_CourseDifficulty; vector m_vEntries; Trail() { - m_st = STEPS_TYPE_INVALID; - m_cd = COURSE_DIFFICULTY_INVALID; + m_StepsType = STEPS_TYPE_INVALID; + m_CourseDifficulty = COURSE_DIFFICULTY_INVALID; } RadarValues GetRadarValues() const; diff --git a/stepmania/src/TrailUtil.cpp b/stepmania/src/TrailUtil.cpp new file mode 100644 index 0000000000..e1cbfe3bfe --- /dev/null +++ b/stepmania/src/TrailUtil.cpp @@ -0,0 +1,85 @@ +#include "global.h" +/* +----------------------------------------------------------------------------- + Class: TrailUtil + + Desc: See header. + + Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. + Chris Danford +----------------------------------------------------------------------------- +*/ + +#include "TrailUtil.h" +#include "Trail.h" +#include "Course.h" +#include "XmlFile.h" +#include "GameManager.h" + + +void TrailID::FromTrail( const Trail *p ) +{ + if( p == NULL ) + { + st = STEPS_TYPE_INVALID; + cd = COURSE_DIFFICULTY_INVALID; + } + else + { + st = p->m_StepsType; + cd = p->m_CourseDifficulty; + } +} + +Trail *TrailID::ToTrail( const Course *p, bool bAllowNull ) const +{ + if( st == STEPS_TYPE_INVALID || cd == COURSE_DIFFICULTY_INVALID ) + return NULL; + + return p->GetTrail( st, cd ); +} + +XNode* TrailID::CreateNode() const +{ + XNode* pNode = new XNode; + pNode->name = "Trail"; + + pNode->AppendAttr( "StepsType", GameManager::NotesTypeToString(st) ); + pNode->AppendAttr( "CourseDifficulty", CourseDifficultyToString(cd) ); + + return pNode; +} + +void TrailID::LoadFromNode( const XNode* pNode ) +{ + ASSERT( pNode->name == "Trail" ); + + CString sTemp; + + pNode->GetAttrValue("StepsType", sTemp); + st = GameManager::StringToNotesType( sTemp ); + + pNode->GetAttrValue("CourseDifficulty", sTemp); + cd = StringToCourseDifficulty( sTemp ); +} + +CString TrailID::ToString() const +{ + CString s = GameManager::NotesTypeToString(st); + s += " " + CourseDifficultyToString(cd); + return s; +} + +bool TrailID::IsValid() const +{ + return st != STEPS_TYPE_INVALID && cd != COURSE_DIFFICULTY_INVALID; +} + +bool TrailID::operator<( const TrailID &rhs ) const +{ +#define COMP(a) if(arhs.a) return false; + COMP(st); + COMP(cd); +#undef COMP + return false; +} diff --git a/stepmania/src/TrailUtil.h b/stepmania/src/TrailUtil.h new file mode 100644 index 0000000000..535850ecb9 --- /dev/null +++ b/stepmania/src/TrailUtil.h @@ -0,0 +1,40 @@ +#ifndef TrialUtil_H +#define TrialUtil_H +/* +----------------------------------------------------------------------------- + Class: TrialUtil + + Desc: An queue of Songs and Steps that are generated from a Course. + + Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. + Chris Danford +----------------------------------------------------------------------------- +*/ + +#include "GameConstantsAndTypes.h" + +class Trail; +class Course; +struct XNode; + +class TrailID +{ + StepsType st; + CourseDifficulty cd; + +public: + TrailID() { Unset(); } + void Unset() { FromTrail(NULL); } + void FromTrail( const Trail *p ); + Trail *ToTrail( const Course *p, bool bAllowNull ) const; + bool operator<( const TrailID &rhs ) const; + bool MatchesStepsType( StepsType s ) const { return st == s; } + + XNode* CreateNode() const; + void LoadFromNode( const XNode* pNode ); + CString ToString() const; + bool IsValid() const; + static void FlushCache(); +}; + +#endif