diff --git a/stepmania/src/Bookkeeper.cpp b/stepmania/src/Bookkeeper.cpp index ddb33cd4dc..fe43ecd3b3 100644 --- a/stepmania/src/Bookkeeper.cpp +++ b/stepmania/src/Bookkeeper.cpp @@ -28,35 +28,6 @@ static const CString COINS_DAT = "Data/Coins.dat"; const int COINS_DAT_VERSION = 1; -tm AddDays( tm start, int iDaysToMove ) -{ - start.tm_mday += iDaysToMove; - time_t seconds = mktime( &start ); - ASSERT( seconds != (time_t)-1 ); - tm time; - localtime_r( &seconds, &time ); - return time; -} - -tm GetYesterday( tm start ) -{ - return AddDays( start, -1 ); -} - -int GetDayOfWeek( tm time ) -{ - int iDayOfWeek = time.tm_wday; - ASSERT( iDayOfWeek < DAYS_IN_WEEK ); - return iDayOfWeek; -} - -tm GetNextSunday( tm start ) -{ - return AddDays( start, DAYS_IN_WEEK-GetDayOfWeek(start) ); -} - - - Bookkeeper::Bookkeeper() { int i, j; diff --git a/stepmania/src/Profile.cpp b/stepmania/src/Profile.cpp index 988c58fbf7..5bdbfa8160 100644 --- a/stepmania/src/Profile.cpp +++ b/stepmania/src/Profile.cpp @@ -95,7 +95,6 @@ void Profile::InitGeneralData() m_iNumSongsPlayedByMeter[i] = 0; ZERO( m_iNumSongsPassedByPlayMode ); ZERO( m_iNumSongsPassedByGrade ); - ZERO( m_iCaloriesByDayForLastYear ); } void Profile::InitSongScores() @@ -120,6 +119,11 @@ void Profile::InitScreenshotData() m_vScreenshots.clear(); } +void Profile::InitCalorieData() +{ + m_mapDayToCaloriesBurned.clear(); +} + CString Profile::GetDisplayName() const { if( !m_sDisplayName.empty() ) @@ -135,7 +139,7 @@ CString Profile::GetDisplayTotalCaloriesBurned() const if( m_fWeightPounds == 0 ) // weight not entered return "N/A"; else - return ssprintf("%0.3fCal",m_fTotalCaloriesBurned); + return ssprintf("%0.3f Cal",m_fTotalCaloriesBurned); } int Profile::GetTotalNumSongsPlayed() const @@ -385,6 +389,7 @@ bool Profile::LoadAllFromDir( CString sDir ) LOAD_NODE( CourseScores ); LOAD_NODE( CategoryScores ); LOAD_NODE( ScreenshotData ); + LOAD_NODE( CalorieData ); } return true; // FIXME? Investigate what happens if we always return true. @@ -408,6 +413,7 @@ bool Profile::SaveAllToDir( CString sDir ) const xml.AppendChild( SaveCourseScoresCreateNode() ); xml.AppendChild( SaveCategoryScoresCreateNode() ); xml.AppendChild( SaveScreenshotDataCreateNode() ); + xml.AppendChild( SaveCalorieDataCreateNode() ); bool bSaved = xml.SaveToFile(fn); if( bSaved && PREFSMAN->m_bSignProfileData ) { @@ -585,17 +591,6 @@ XNode* Profile::SaveGeneralDataCreateNode() const } } - { - XNode* pCaloriesByDayForLastYear = pGeneralDataNode->AppendChild("CaloriesByDayForLastYear"); - for( int i=0; iAppendChild( DayInYearToString(i), m_iCaloriesByDayForLastYear[i] ); - } - } - return pGeneralDataNode; } @@ -727,13 +722,6 @@ void Profile::LoadGeneralDataFromNode( const XNode* pNode ) } - { - XNode* pCaloriesByDayForLastYear = pNode->GetChild("CaloriesByDayForLastYear"); - if( pCaloriesByDayForLastYear ) - for( int i=0; iGetChildValue( DayInYearToString(i), m_iCaloriesByDayForLastYear[i] ); - - } } void Profile::AddStepTotals( int iTotalTapsAndHolds, int iTotalJumps, int iTotalHolds, int iTotalMines, int iTotalHands ) @@ -753,6 +741,11 @@ void Profile::AddStepTotals( int iTotalTapsAndHolds, int iTotalJumps, int iTotal SCALE( m_fWeightPounds, 100.f, 200.f, 0.000f, 0.000f ) * iTotalMines + SCALE( m_fWeightPounds, 100.f, 200.f, 0.222f, 0.386f ) * iTotalHands; m_fTotalCaloriesBurned += fCals; + + time_t cur_time = time(NULL); + tm *cur_tm = localtime( &cur_time ); + Day day = { cur_tm->tm_yday, cur_tm->tm_year+1900 }; + m_mapDayToCaloriesBurned[day] += fCals; } } @@ -1451,3 +1444,66 @@ XNode* Profile::SaveScreenshotDataCreateNode() const return pNode; } + +void Profile::LoadCalorieDataFromNode( const XNode* pNode ) +{ + CHECKPOINT; + + ASSERT( pNode->name == "CalorieData" ); + for( XNodes::const_iterator pDay = pNode->childs.begin(); + pDay != pNode->childs.end(); + pDay++ ) + { + if( (*pDay)->name != "Day" ) + WARN_AND_CONTINUE; + + Day day; + + if( !(*pDay)->GetAttrValue("DayInYear",day.iDayInYear) ) + WARN_AND_CONTINUE; + + if( !(*pDay)->GetAttrValue("Year",day.iYear) ) + WARN_AND_CONTINUE; + + float fCaloriesBurned = 0; + + if( !(*pDay)->GetChildValue("CaloriesBurned",fCaloriesBurned) ) + WARN_AND_CONTINUE; + + m_mapDayToCaloriesBurned[day] = fCaloriesBurned; + } +} + +XNode* Profile::SaveCalorieDataCreateNode() const +{ + CHECKPOINT; + + const Profile* pProfile = this; + ASSERT( pProfile ); + + XNode* pNode = new XNode; + pNode->name = "CalorieData"; + + for( map::const_iterator i = m_mapDayToCaloriesBurned.begin(); + i != m_mapDayToCaloriesBurned.end(); + i++ ) + { + XNode* pDay = pNode->AppendChild( "Day" ); + + pDay->AppendAttr( "DayInYear", i->first.iDayInYear ); + pDay->AppendAttr( "Year", i->first.iYear ); + + pDay->AppendChild( "CaloriesBurned", i->second ); + } + + return pNode; +} + +float Profile::GetCaloriesBurnedForDay( Day day ) const +{ + map::const_iterator i = m_mapDayToCaloriesBurned.find( day ); + if( i == m_mapDayToCaloriesBurned.end() ) + return 0; + else + return i->second; +} diff --git a/stepmania/src/Profile.h b/stepmania/src/Profile.h index cfed538f9b..883857cd49 100644 --- a/stepmania/src/Profile.h +++ b/stepmania/src/Profile.h @@ -72,6 +72,8 @@ public: int GetTotalNumSongsPassed() const; static CString GetProfileDisplayNameFromDir( CString sDir ); int GetSongNumTimesPlayed( const Song* pSong ) const; + + void AddStepTotals( int iNumTapsAndHolds, int iNumJumps, int iNumHolds, int iNumMines, int iNumHands ); // // Editable data @@ -107,9 +109,6 @@ public: int m_iNumSongsPlayedByMeter[MAX_METER+1]; int m_iNumSongsPassedByPlayMode[NUM_PLAY_MODES]; int m_iNumSongsPassedByGrade[NUM_GRADES]; - int m_iCaloriesByDayForLastYear[DAYS_IN_YEAR]; - - void AddStepTotals( int iNumTapsAndHolds, int iNumJumps, int iNumHolds, int iNumMines, int iNumHands ); // // Steps high scores @@ -171,6 +170,33 @@ public: void AddScreenshot( Screenshot screenshot ); int GetNextScreenshotIndex() { return m_vScreenshots.size(); } + + // + // Calorie Data + // + // Why track calories in a map, and not in a static sized array like + // Bookkeeping? The machine's clock is not guaranteed to be set correctly. + // If calorie array is in a static sized array, playing on a machine with + // a mis-set clock could wipe out all your past data. With this scheme, + // the worst that could happen is that playing on a mis-set machine will + // insert some garbage entries into the map. + struct Day + { + int iDayInYear; // 0-365 + int iYear; // e.g. 2004 + bool operator==( const Day& other ) const { return iDayInYear==other.iDayInYear && iYear==other.iYear; } + bool operator<( const Day& other ) const + { + if(iYearother.iYear) + return false; + else + return iDayInYear m_mapDayToCaloriesBurned; + float GetCaloriesBurnedForDay( Day day ) const; // // Init'ing @@ -183,6 +209,7 @@ public: InitCourseScores(); InitCategoryScores(); InitScreenshotData(); + InitCalorieData(); } void InitEditableData(); void InitGeneralData(); @@ -190,6 +217,7 @@ public: void InitCourseScores(); void InitCategoryScores(); void InitScreenshotData(); + void InitCalorieData(); // // Loading and saving @@ -208,6 +236,7 @@ public: void LoadCourseScoresFromNode( const XNode* pNode ); void LoadCategoryScoresFromNode( const XNode* pNode ); void LoadScreenshotDataFromNode( const XNode* pNode ); + void LoadCalorieDataFromNode( const XNode* pNode ); void SaveEditableDataToDir( CString sDir ) const; XNode* SaveGeneralDataCreateNode() const; @@ -215,6 +244,7 @@ public: XNode* SaveCourseScoresCreateNode() const; XNode* SaveCategoryScoresCreateNode() const; XNode* SaveScreenshotDataCreateNode() const; + XNode* SaveCalorieDataCreateNode() const; void DeleteProfileDataFromDirSM390a12( CString sDir ) const; void DeleteSongScoresFromDirSM390a12( CString sDir ) const; diff --git a/stepmania/src/ProfileHtml.cpp b/stepmania/src/ProfileHtml.cpp index 56771bf85f..84f6d50819 100644 --- a/stepmania/src/ProfileHtml.cpp +++ b/stepmania/src/ProfileHtml.cpp @@ -363,6 +363,7 @@ void PrintStatistics( RageFile &f, const Profile *pProfile, CString sTitle, vect END_TABLE; } PRINT_CLOSE(f); + } PRINT_CLOSE(f); } @@ -855,13 +856,7 @@ void PrintBookkeeping( RageFile &f, const Profile *pProfile, CString sTitle, vec BEGIN_TABLE(4); for( int i=0; i\n"); + + // header row + f.Write(""); + f.Write(""); + { + for( int i=0; i"+DayOfWeekToString(i)+""); + } + f.Write("Total"); + f.Write(""); + + // row for each week + time_t now = time( NULL ); + tm when = *localtime( &now ); + when = GetNextSunday( when ); + when = AddDays( when, -DAYS_IN_WEEK ); + { + for( int i=0; i"); + f.Write(""+LastWeekToString(i)+""); + for( int i=0; iGetCaloriesBurnedForDay( day ); + fWeekCals += fDayCals; + f.Write(""+ssprintf("%0.3f",fDayCals)+""); + when = AddDays( when, +1 ); + } + f.Write(""+ssprintf("%0.3f",fWeekCals)+""); + f.Write(""); + when = AddDays( when, -DAYS_IN_WEEK*2 ); + } + } + f.Write("\n"); + } + PRINT_CLOSE(f); +} + void SaveStatsWebPage( CString sDir, @@ -1124,6 +1164,7 @@ TITLE.c_str(), STYLE_CSS.c_str() ) ); PrintSongHighScores( f, pProfile, "My Song High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintCourseHighScores( f, pProfile, "My Course High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintScreenshots( f, pProfile, "My Screenshots", sDir ); + PrintCaloriesBurned( f, pProfile, "My Calories Burned", sDir ); PrintGradeTable( f, pProfile, "My Grade Table", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintPopularity( f, pProfile, "Last Machine Popularity", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintSongHighScores( f, pProfile, "Last Machine High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); @@ -1134,6 +1175,7 @@ TITLE.c_str(), STYLE_CSS.c_str() ) ); PrintSongHighScores( f, pProfile, "Song High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintCourseHighScores( f, pProfile, "Course High Scores", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintScreenshots( f, pProfile, "Screenshots", sDir ); + PrintCaloriesBurned( f, pProfile, "Calories Burned", sDir ); PrintGradeTable( f, pProfile, "Grade Table", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintInventoryList( f, pProfile, "Song Information", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); PrintBookkeeping( f, pProfile, "Bookkeeping", vpSongs, vpAllSteps, vStepsTypesToShow, mapStepsToSong, vpCourses ); diff --git a/stepmania/src/TimeConstants.cpp b/stepmania/src/TimeConstants.cpp index bed7a46ce7..23295686d8 100644 --- a/stepmania/src/TimeConstants.cpp +++ b/stepmania/src/TimeConstants.cpp @@ -63,3 +63,74 @@ CString HourInDayToString( int iHourInDayIndex ) return ssprintf("%02d:00", iHourInDayIndex); } +static const CString MONTH_TO_NAME[MONTHS_IN_YEAR] = +{ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +}; + +CString MonthToString( int iMonthIndex ) +{ + return MONTH_TO_NAME[iMonthIndex]; +} + +CString LastWeekToString( int iLastWeekIndex ) +{ + switch( iLastWeekIndex ) + { + case 0: return "This week"; break; + case 1: return "Last week"; break; + default: return ssprintf("%d weeks ago",iLastWeekIndex); break; + } +} + +tm AddDays( tm start, int iDaysToMove ) +{ + start.tm_mday += iDaysToMove; + time_t seconds = mktime( &start ); + ASSERT( seconds != (time_t)-1 ); + tm time; + localtime_r( &seconds, &time ); + return time; +} + +tm GetYesterday( tm start ) +{ + return AddDays( start, -1 ); +} + +int GetDayOfWeek( tm time ) +{ + int iDayOfWeek = time.tm_wday; + ASSERT( iDayOfWeek < DAYS_IN_WEEK ); + return iDayOfWeek; +} + +tm GetNextSunday( tm start ) +{ + return AddDays( start, DAYS_IN_WEEK-GetDayOfWeek(start) ); +} + + +tm GetDayInYearAndYear( int iDayInYearIndex, int iYear ) +{ + time_t now = time( NULL ); + tm when = *localtime( &now ); + + when.tm_mday = iDayInYearIndex; + when.tm_year = iYear - 1900; + time_t then = mktime( &when ); + + when = *localtime( &then ); + return when; +} diff --git a/stepmania/src/TimeConstants.h b/stepmania/src/TimeConstants.h index 928caecfee..940279d4b0 100644 --- a/stepmania/src/TimeConstants.h +++ b/stepmania/src/TimeConstants.h @@ -11,17 +11,29 @@ ----------------------------------------------------------------------------- */ +#include "time.h" + const int NUM_LAST_DAYS = 7; const int NUM_LAST_WEEKS = 52; const int DAYS_IN_YEAR = 365; const int HOURS_IN_DAY = 24; const int DAYS_IN_WEEK = 7; +const int MONTHS_IN_YEAR = 12; CString DayInYearToString( int iDayInYearIndex ); CString LastDayToString( int iLastDayIndex ); CString DayOfWeekToString( int iDayOfWeekIndex ); CString HourInDayToString( int iHourIndex ); +CString MonthToString( int iMonthIndex ); +CString LastWeekToString( int iLastWeekIndex ); + +tm AddDays( tm start, int iDaysToMove ); +tm GetYesterday( tm start ); +int GetDayOfWeek( tm time ); +tm GetNextSunday( tm start ); + +tm GetDayInYearAndYear( int iDayInYearIndex, int iYear ); #endif diff --git a/stepmania/src/XmlFile.cpp b/stepmania/src/XmlFile.cpp index 041a4664ab..d756a56d14 100644 --- a/stepmania/src/XmlFile.cpp +++ b/stepmania/src/XmlFile.cpp @@ -1043,6 +1043,9 @@ LPXAttr _tagXMLNode::AppendAttr( LPCTSTR name /*= NULL*/, LPCTSTR value /*= NULL return AppendAttr( CreateAttr( name, value ) ); } +LPXAttr _tagXMLNode::AppendAttr( LPCTSTR name, float value ){ return AppendAttr(name,ssprintf("%f",value)); } +LPXAttr _tagXMLNode::AppendAttr( LPCTSTR name, int value ) { return AppendAttr(name,ssprintf("%d",value)); } + //======================================================== // Name : DetachChild // Desc : no delete object, just detach in list diff --git a/stepmania/src/XmlFile.h b/stepmania/src/XmlFile.h index abb4d9b48a..54941aa3e3 100644 --- a/stepmania/src/XmlFile.h +++ b/stepmania/src/XmlFile.h @@ -186,6 +186,8 @@ typedef struct _tagXMLNode XAttrs::iterator GetAttrIterator( LPXAttr node ); LPXAttr CreateAttr( LPCTSTR anem = NULL, LPCTSTR value = NULL ); LPXAttr AppendAttr( LPCTSTR name = NULL, LPCTSTR value = NULL ); + LPXAttr AppendAttr( LPCTSTR name, float value ); + LPXAttr AppendAttr( LPCTSTR name, int value ); LPXAttr AppendAttr( LPXAttr attr ); bool RemoveAttr( LPXAttr attr ); LPXAttr DetachAttr( LPXAttr attr );