forked from stepmania/stepmania
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProfile.h
497 lines (439 loc) · 18.5 KB
/
Profile.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
#ifndef Profile_H
#define Profile_H
#include "GameConstantsAndTypes.h"
#include "Grade.h"
#include "HighScore.h"
#include "DateTime.h"
#include "SongUtil.h" // for SongID
#include "StepsUtil.h" // for StepsID
#include "CourseUtil.h" // for CourseID
#include "TrailUtil.h" // for TrailID
#include "StyleUtil.h" // for StyleID
#include "IDUtil.h" // For hash specializations.
#include "LuaReference.h"
#include "PlayerNumber.h"
#include <unordered_map>
#include <map>
#include <set>
#include <deque>
class XNode;
struct lua_State;
class Character;
// Current file versions
extern const std::string STATS_XML;
/**
* @brief The filename where one can edit their personal profile data.
*
* Editable data is an INI because the default INI file association on Windows
* systems will open the ini file in an editor. The default association for
* XML will open in IE. Users have a much better chance of discovering how to
* edit this data if they don't have to fight against the file associations. */
extern const std::string EDITABLE_INI;
/**
* @brief The filename containing the signature for STATS_XML's signature.
*
*
* The "don't share" file is something that the user should always keep private.
* They can safely share STATS_XML with STATS_XML's signature so that others
* can authenticate the STATS_XML data. However, others can't copy that data
* to their own profile for use in the game unless they also have the "don't
* share" file. DontShare contains a piece of information that we can
* construct using STATS_XML but the user can't construct using STATS_XML. */
extern const std::string DONT_SHARE_SIG;
extern const std::string PUBLIC_KEY_FILE;
extern const std::string SCREENSHOTS_SUBDIR;
extern const std::string EDIT_STEPS_SUBDIR;
extern const std::string EDIT_COURSES_SUBDIR;
extern const std::string LASTGOOD_SUBDIR;
// extern const std::string RIVAL_SUBDIR;
/** @brief The max number of characters that can be used in a profile. */
const unsigned int PROFILE_MAX_DISPLAY_NAME_LENGTH = 32;
class Style;
class Song;
class Steps;
class Course;
struct Game;
// Profile types exist for sorting the list of profiles.
// Guest profiles at the top, test at the bottom.
enum ProfileType
{
ProfileType_Guest,
ProfileType_Normal,
ProfileType_Test,
NUM_ProfileType,
ProfileType_Invalid
};
/**
* @brief Player data that persists between sessions.
*
* This can be stored on a local disk or on a memory card. */
class Profile
{
public:
/**
* @brief Set up the Profile with default values.
*
* Note: there are probably a lot of variables. */
// When adding new score related data, add logic for handling it to
// MergeScoresFromOtherProfile. -Kyz
// When adding any new fields, add them to SwapExceptPriority. Anything not
// added to SwapExceptPriority won't be swapped correctly when the user
// changes the list priority of a profile. -Kyz
Profile():
m_Type(ProfileType_Normal), m_ListPriority(0),
m_sDisplayName(""), m_sCharacterID(""),
m_sLastUsedHighScoreName(""), m_iWeightPounds(0),
m_Voomax(0), m_BirthYear(0), m_IgnoreStepCountCalories(false),
m_IsMale(true),
m_sGuid(MakeGuid()), m_sDefaultModifiers(),
m_SortOrder(SortOrder_Invalid),
m_LastDifficulty(Difficulty_Invalid),
m_LastCourseDifficulty(Difficulty_Invalid),
m_LastStepsType(StepsType_Invalid), m_lastSong(),
m_lastCourse(), m_iCurrentCombo(0), m_iTotalSessions(0),
m_iTotalSessionSeconds(0), m_iTotalGameplaySeconds(0),
m_fTotalCaloriesBurned(0), m_GoalType(GoalType_Calories),
m_iGoalCalories(0), m_iGoalSeconds(0), m_iTotalDancePoints(0),
m_iNumExtraStagesPassed(0), m_iNumExtraStagesFailed(0),
m_iNumToasties(0), m_iTotalTapsAndHolds(0), m_iTotalJumps(0),
m_iTotalHolds(0), m_iTotalRolls(0), m_iTotalMines(0),
m_iTotalHands(0), m_iTotalLifts(0), m_bNewProfile(false),
m_UnlockedEntryIDs(), m_sLastPlayedMachineGuid(""),
m_LastPlayedDate(),m_iNumSongsPlayedByStyle(),
m_iNumTotalSongsPlayed(0), m_UserTable(), m_SongHighScores(),
m_CourseHighScores(), m_vScreenshots(),
m_mapDayToCaloriesBurned()
{
m_lastSong.Unset();
m_lastCourse.Unset();
m_LastPlayedDate.Init();
FOREACH_ENUM( PlayMode, i )
m_iNumSongsPlayedByPlayMode[i] = 0;
FOREACH_ENUM( Difficulty, i )
m_iNumSongsPlayedByDifficulty[i] = 0;
for( int i=0; i<MAX_METER+1; i++ )
m_iNumSongsPlayedByMeter[i] = 0;
ZERO( m_iNumStagesPassedByPlayMode );
ZERO( m_iNumStagesPassedByGrade );
m_UserTable.Unset();
FOREACH_ENUM( StepsType,st )
FOREACH_ENUM( RankingCategory,rc )
m_CategoryHighScores[st][rc].Init();
}
~Profile();
void ClearSongs();
// smart accessors
std::string GetDisplayNameOrHighScoreName() const;
Character *GetCharacter() const;
void SetCharacter(const std::string sCharacterID);
std::string GetDisplayTotalCaloriesBurned() const; // remove me and use Lua instead
std::string GetDisplayTotalCaloriesBurnedToday() const; // remove me and use Lua instead
int GetCalculatedWeightPounds() const; // returns a default value if m_iWeightPounds isn't set
int GetAge() const; // returns a default value if m_Age isn't set
float GetCaloriesBurnedToday() const;
int GetTotalNumSongsPassed() const;
int GetTotalStepsWithTopGrade( StepsType st, Difficulty d, Grade g ) const;
int GetTotalTrailsWithTopGrade( StepsType st, CourseDifficulty d, Grade g ) const;
float GetSongsPossible( StepsType st, Difficulty dc ) const;
float GetCoursesPossible( StepsType st, CourseDifficulty cd ) const;
float GetSongsActual( StepsType st, Difficulty dc ) const;
float GetCoursesActual( StepsType st, CourseDifficulty cd ) const;
float GetSongsPercentComplete( StepsType st, Difficulty dc ) const;
float GetCoursesPercentComplete( StepsType st, CourseDifficulty cd ) const;
float GetSongsAndCoursesPercentCompleteAllDifficulties( StepsType st ) const;
bool GetDefaultModifiers( const Game* pGameType, std::string &sModifiersOut ) const;
void SetDefaultModifiers( const Game* pGameType, const std::string &sModifiers );
void get_preferred_noteskin(StepsType stype, std::string& skin) const;
bool set_preferred_noteskin(StepsType stype, std::string const& skin);
typedef std::map<StepsType, std::string> pref_noteskin_container;
pref_noteskin_container const& get_all_preferred_noteskins()
{ return m_preferred_noteskins; }
LuaReference get_noteskin_params(std::string const& skin, StepsType stype) const;
void set_noteskin_params(std::string const& skin, StepsType stype, LuaReference& params);
typedef std::unordered_map<std::string, std::unordered_map<StepsType, LuaReference> > noteskin_param_container;
noteskin_param_container const& get_all_noteskin_params() const
{ return m_noteskin_params; }
bool IsCodeUnlocked( std::string sUnlockEntryID ) const;
Song *GetMostPopularSong() const;
Course *GetMostPopularCourse() const;
void AddStepTotals( int iNumTapsAndHolds, int iNumJumps, int iNumHolds, int iNumRolls, int iNumMines,
int iNumHands, int iNumLifts, float fCaloriesBurned );
void AddCaloriesToDailyTotal(float cals);
float CalculateCaloriesFromHeartRate(float HeartRate, float Duration);
bool IsMachine() const;
ProfileType m_Type;
// Profiles of the same type and priority are sorted by dir name.
int m_ListPriority;
// Editable data
std::string m_sDisplayName;
std::string m_sCharacterID;
/**
* @brief The last used name for high scoring purposes.
*
* This really shouldn't be in "editable", but it's needed in the smaller editable file
* so that it can be ready quickly. */
std::string m_sLastUsedHighScoreName;
int m_iWeightPounds; // 0 == not set
// Voomax and BirthYear are used for calculating calories from heart rate.
float m_Voomax; // 0 == not set
int m_BirthYear; // 0 == not set
// m_IgnoreStepCountCalories is so that the step count based calorie
// counter can be ignored in favor of calculating calories from heart rate
// and voomax.
bool m_IgnoreStepCountCalories;
bool m_IsMale; // Used solely for calculating calories from heart rate.
//std::string m_sProfileImageName; // todo: add a default image -aj
// General data
static std::string MakeGuid();
std::string m_sGuid;
// Probably not a problem if the per-game sections are written to prefs in
// random order. -Kyz
std::unordered_map<std::string,std::string> m_sDefaultModifiers;
pref_noteskin_container m_preferred_noteskins;
noteskin_param_container m_noteskin_params;
std::vector<Song*> m_songs;
SortOrder m_SortOrder;
Difficulty m_LastDifficulty;
CourseDifficulty m_LastCourseDifficulty;
StepsType m_LastStepsType;
SongID m_lastSong;
CourseID m_lastCourse;
int m_iCurrentCombo;
int m_iTotalSessions;
int m_iTotalSessionSeconds;
int m_iTotalGameplaySeconds;
float m_fTotalCaloriesBurned;
GoalType m_GoalType;
int m_iGoalCalories;
int m_iGoalSeconds;
int m_iTotalDancePoints;
int m_iNumExtraStagesPassed;
int m_iNumExtraStagesFailed;
int m_iNumToasties;
int m_iTotalTapsAndHolds;
int m_iTotalJumps;
int m_iTotalHolds;
int m_iTotalRolls;
int m_iTotalMines;
int m_iTotalHands;
int m_iTotalLifts;
/** @brief Is this a brand new profile? */
bool m_bNewProfile;
std::set<std::string> m_UnlockedEntryIDs;
/**
* @brief Which machine did we play on last, based on the Guid?
*
* This is mutable because it's overwritten on save, but is usually
* const everywhere else. It was decided to keep const on the whole
* save chain and keep this mutable. -Chris */
mutable std::string m_sLastPlayedMachineGuid;
mutable DateTime m_LastPlayedDate;
/* These stats count twice in the machine profile if two players are playing;
* that's the only approach that makes sense for ByDifficulty and ByMeter. */
int m_iNumSongsPlayedByPlayMode[NUM_PlayMode];
std::unordered_map<StyleID,int> m_iNumSongsPlayedByStyle;
int m_iNumSongsPlayedByDifficulty[NUM_Difficulty];
int m_iNumSongsPlayedByMeter[MAX_METER+1];
/**
* @brief Count the total number of songs played.
*
* This stat counts once per song, even if two players are active. */
int m_iNumTotalSongsPlayed;
int m_iNumStagesPassedByPlayMode[NUM_PlayMode];
int m_iNumStagesPassedByGrade[NUM_Grade];
/* store arbitrary data for the theme within a profile */
LuaTable m_UserTable;
// Song high scores
struct HighScoresForASteps
{
HighScoreList hsl;
HighScoresForASteps(): hsl() {}
};
struct HighScoresForASong
{
std::unordered_map<StepsID,HighScoresForASteps> m_StepsHighScores;
int GetNumTimesPlayed() const;
HighScoresForASong(): m_StepsHighScores() {}
};
// No, I don't care if having the song scores in random order confuses
// people reading the raw file. I want fast lookup. -Kyz
std::unordered_map<SongID,HighScoresForASong> m_SongHighScores;
void AddStepsHighScore( const Song* pSong, const Steps* pSteps, HighScore hs, int &iIndexOut );
const HighScoreList& GetStepsHighScoreList( const Song* pSong, const Steps* pSteps ) const;
HighScoreList& GetStepsHighScoreList( const Song* pSong, const Steps* pSteps );
int GetStepsNumTimesPlayed( const Song* pSong, const Steps* pSteps ) const;
void IncrementStepsPlayCount( const Song* pSong, const Steps* pSteps );
void GetGrades( const Song* pSong, StepsType st, int iCounts[NUM_Grade] ) const;
int GetSongNumTimesPlayed( const Song* pSong ) const;
int GetSongNumTimesPlayed( const SongID& songID ) const;
DateTime GetSongLastPlayedDateTime( const Song* pSong ) const;
bool HasPassedSteps( const Song* pSong, const Steps* pSteps ) const;
bool HasPassedAnyStepsInSong( const Song* pSong ) const;
// Course high scores
// struct was a typedef'd array of HighScores, but VC6 freaks out
// in processing the templates for map::operator[].
struct HighScoresForATrail
{
HighScoreList hsl;
HighScoresForATrail(): hsl() {}
};
struct HighScoresForACourse
{
std::unordered_map<TrailID,HighScoresForATrail> m_TrailHighScores;
int GetNumTimesPlayed() const;
HighScoresForACourse(): m_TrailHighScores() {}
};
std::unordered_map<CourseID,HighScoresForACourse> m_CourseHighScores;
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;
DateTime GetCourseLastPlayedDateTime( const Course* pCourse ) const;
void IncrementCoursePlayCount( const Course* pCourse, const Trail* pTrail );
void GetAllUsedHighScoreNames(std::set<std::string>& names);
void MergeScoresFromOtherProfile(Profile* other, bool skip_totals,
std::string const& from_dir, std::string const& to_dir);
// Category high scores
HighScoreList m_CategoryHighScores[NUM_StepsType][NUM_RankingCategory];
void AddCategoryHighScore( StepsType st, RankingCategory rc, HighScore hs, int &iIndexOut );
HighScoreList& GetCategoryHighScoreList( StepsType st, RankingCategory rc );
const HighScoreList& GetCategoryHighScoreList( StepsType st, RankingCategory rc ) const;
int GetCategoryNumTimesPlayed( StepsType st ) const;
void IncrementCategoryPlayCount( StepsType st, RankingCategory rc );
// Screenshot Data
std::vector<Screenshot> m_vScreenshots;
void AddScreenshot( const Screenshot &screenshot );
int GetNextScreenshotIndex() { return m_vScreenshots.size(); }
/**
* @brief The basics for 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 Calories
{
Calories(): fCals(0) {}
float fCals;
};
// Probably not used enough to require fast lookup, so leave it sorted for
// reading convenience. -Kyz
std::map<DateTime,Calories> m_mapDayToCaloriesBurned;
float GetCaloriesBurnedForDay( DateTime day ) const;
/*
// RecentSongScores
struct HighScoreForASongAndSteps
{
StepsID stepsID;
SongID songID;
HighScore hs;
HighScoreForASongAndSteps() { Unset(); }
void Unset() { stepsID.Unset(); songID.Unset(); hs.Unset(); }
XNode* CreateNode() const;
};
void SaveStepsRecentScore( const Song* pSong, const Steps* pSteps, HighScore hs );
// RecentCourseScores
struct HighScoreForACourseAndTrail
{
CourseID courseID;
TrailID trailID;
HighScore hs;
HighScoreForACourseAndTrail() { Unset(); }
void Unset() { courseID.Unset(); hs.Unset(); }
XNode* CreateNode() const;
};
void SaveCourseRecentScore( const Course* pCourse, const Trail* pTrail, HighScore hs );
*/
// Init'ing
void InitAll()
{
InitEditableData();
init_noteskin_params();
InitGeneralData();
InitSongScores();
InitCourseScores();
InitCategoryScores();
InitScreenshotData();
InitCalorieData();
ClearSongs();
}
void InitEditableData();
void init_noteskin_params();
void InitGeneralData();
void InitSongScores();
void InitCourseScores();
void InitCategoryScores();
void InitScreenshotData();
void InitCalorieData();
void ClearStats();
void swap(Profile& other);
// Loading and saving
void HandleStatsPrefixChange( std::string dir, bool require_signature );
ProfileLoadResult LoadAllFromDir( std::string sDir, bool bRequireSignature );
ProfileLoadResult LoadStatsFromDir( std::string dir, bool require_signature );
void LoadSongsFromDir(std::string const& dir, ProfileSlot prof_slot);
void LoadTypeFromDir(std::string dir);
void LoadCustomFunction(std::string dir, PlayerNumber pn);
bool SaveAllToDir(std::string sDir, bool bSignData, PlayerNumber pn) const;
ProfileLoadResult LoadEditableDataFromDir( std::string sDir );
void load_noteskin_params_from_dir(std::string const& dir);
ProfileLoadResult LoadStatsXmlFromNode( const XNode* pNode, bool bIgnoreEditable = true );
void LoadGeneralDataFromNode( const XNode* pNode );
void LoadSongScoresFromNode( const XNode* pNode );
void LoadCourseScoresFromNode( const XNode* pNode );
void LoadCategoryScoresFromNode( const XNode* pNode );
void LoadScreenshotDataFromNode( const XNode* pNode );
void LoadCalorieDataFromNode( const XNode* pNode );
void SaveTypeToDir(std::string dir) const;
void SaveEditableDataToDir( std::string sDir ) const;
void save_noteskin_params_to_dir(std::string const& dir) const;
bool SaveStatsXmlToDir( std::string sDir, bool bSignData ) const;
XNode* SaveStatsXmlCreateNode() const;
XNode* SaveGeneralDataCreateNode() const;
XNode* SaveSongScoresCreateNode() const;
XNode* SaveCourseScoresCreateNode() const;
XNode* SaveCategoryScoresCreateNode() const;
XNode* SaveScreenshotDataCreateNode() const;
XNode* SaveCalorieDataCreateNode() const;
XNode* SaveCoinDataCreateNode() const;
void SaveStatsWebPageToDir( std::string sDir ) const;
void SaveMachinePublicKeyToDir( std::string sDir ) const;
static void MoveBackupToDir( std::string sFromDir, std::string sToDir );
static std::string MakeUniqueFileNameNoExtension( std::string sDir, std::string sFileNameBeginning );
static std::string MakeFileNameNoExtension( std::string sFileNameBeginning, int iIndex );
// Lua
void PushSelf( lua_State *L );
private:
const HighScoresForASong *GetHighScoresForASong( const SongID& songID ) const;
const HighScoresForACourse *GetHighScoresForACourse( const CourseID& courseID ) const;
};
#endif
/**
* @file
* @author Chris Danford (c) 2001-2004
* @section LICENSE
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/