From b43af6dce9ec4d4cf34aab280c0d6bda3ceb74eb Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Fri, 28 Nov 2014 21:56:25 -0700 Subject: [PATCH] Added Guest and Test profile types, and profile priorities so the position of a profile in the list can be controlled. Fixes crash bug that occurs when a profile dir is renamed to a non-number and a new profile is made. --- Docs/Changelog_sm5.txt | 12 ++ Docs/Luadoc/Lua.xml | 7 + Docs/Luadoc/LuaDocumentation.xml | 6 + Themes/_fallback/Languages/en.ini | 5 + src/Profile.cpp | 111 +++++++++++++ src/Profile.h | 27 +++- src/ProfileManager.cpp | 235 +++++++++++++++++++++++++--- src/ProfileManager.h | 4 +- src/ScreenOptionsManageProfiles.cpp | 38 +++++ 9 files changed, 418 insertions(+), 27 deletions(-) diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index a1c06e7e9f..820943677b 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,6 +4,18 @@ The StepMania 5 Changelog covers all post-sm-ssc changes. For a list of changes from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. ________________________________________________________________________________ + +2014/11/28 +---------- +* [Profile] Guest and Test profile types added. All existing and new + profiles are Normal. If a profile is changed to Guest through the profile + management screen, it will always show at the top of the list. A profile + set to Test will always be at the bottom of the list. Profiles can be + moved around in the list. + GetType and GetPriority lua functions added to Profile. + Bug that caused a crash when a profile dir was renamed to a non-number and + a new profile was created fixed. [kyzentun] + 2014/11/15 ---------- * [Preferences] Default Fail Type preference mechanism changed internally again. diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index ab33c6e73d..b111f3ddd1 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1200,6 +1200,7 @@ + @@ -1219,6 +1220,7 @@ + @@ -2270,6 +2272,11 @@ + + + + + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index c6209eb1d4..e10cfd60ef 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -3462,6 +3462,9 @@ save yourself some time, copy this for undocumented things: Returns the total number of songs played with the profile. + + Returns the position the profile should have in its category in the list. + Returns the number of times song s has been played with the profile. @@ -3510,6 +3513,9 @@ save yourself some time, copy this for undocumented things: Returns the number of trails with the specified StepsType and Difficulty that you've scored a certain Grade g on. + + Returns the type of the profile. The type of the profile is only used to determine where the profile shows up in the list of profiles, and that problem is already handled by ProfileManager, so if you're reading this, this function only exists so you can make your theme color this profile's list entry based on the type or something like that. + Returns the user table for this Profile. diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 505e1c7929..c0a00b4c3f 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -1220,6 +1220,11 @@ MergeToMachine=Merge into Machine Profile MergeToMachineSkipTotal=Merge into Machine Profile Without Totals MergeToP1=Merge into P1 Profile MergeToP2=Merge into P2 Profile +ChangeToGuest=Change To Guest +ChangeToNormal=Change To Normal +ChangeToTest=Change To Test +MoveUp=Move Up +MoveDown=Move Down [RadarCategory] Air=Air diff --git a/src/Profile.cpp b/src/Profile.cpp index 83cb481c47..20a8ea37f9 100644 --- a/src/Profile.cpp +++ b/src/Profile.cpp @@ -32,6 +32,8 @@ const RString STATS_XML = "Stats.xml"; const RString STATS_XML_GZ = "Stats.xml.gz"; /** @brief The filename for where one can edit their personal profile information. */ const RString EDITABLE_INI = "Editable.ini"; +/** @brief A tiny file containing the type and list priority. */ +const RString TYPE_INI = "Type.ini"; /** @brief The filename containing the signature for STATS_XML's signature. */ const RString DONT_SHARE_SIG = "DontShare.sig"; const RString PUBLIC_KEY_FILE = "public.key"; @@ -60,6 +62,15 @@ const float DEFAULT_BIRTH_YEAR= 1995; #pragma warning (disable : 4706) // assignment within conditional expression #endif +static const char* ProfileTypeNames[] = { + "Guest", + "Normal", + "Test", +}; +XToString(ProfileType); +StringToX(ProfileType); +LuaXType(ProfileType); + int Profile::HighScoresForASong::GetNumTimesPlayed() const { @@ -921,6 +932,64 @@ void Profile::MergeScoresFromOtherProfile(Profile* other, bool skip_totals, } } +void Profile::swap(Profile& other) +{ + // Type is skipped because this is meant to be used only on matching types, + // to move profiles after the priorities have been assigned. -Kyz + // A bit of a misnomer, since it actually works on any type that has its + // own swap function, which includes the standard containers. +#define SWAP_STR_MEMBER(member_name) member_name.swap(other.member_name) +#define SWAP_GENERAL(member_name) std::swap(member_name, other.member_name) + SWAP_GENERAL(m_ListPriority); + SWAP_STR_MEMBER(m_sDisplayName); + SWAP_STR_MEMBER(m_sCharacterID); + SWAP_STR_MEMBER(m_sLastUsedHighScoreName); + SWAP_GENERAL(m_iWeightPounds); + SWAP_GENERAL(m_Voomax); + SWAP_GENERAL(m_BirthYear); + SWAP_GENERAL(m_IgnoreStepCountCalories); + SWAP_GENERAL(m_IsMale); + SWAP_STR_MEMBER(m_sGuid); + SWAP_GENERAL(m_iCurrentCombo); + SWAP_GENERAL(m_iTotalSessions); + SWAP_GENERAL(m_iTotalSessionSeconds); + SWAP_GENERAL(m_iTotalGameplaySeconds); + SWAP_GENERAL(m_fTotalCaloriesBurned); + SWAP_GENERAL(m_GoalType); + SWAP_GENERAL(m_iGoalCalories); + SWAP_GENERAL(m_iGoalSeconds); + SWAP_GENERAL(m_iTotalDancePoints); + SWAP_GENERAL(m_iNumExtraStagesPassed); + SWAP_GENERAL(m_iNumExtraStagesFailed); + SWAP_GENERAL(m_iNumToasties); + SWAP_GENERAL(m_iTotalTapsAndHolds); + SWAP_GENERAL(m_iTotalJumps); + SWAP_GENERAL(m_iTotalHolds); + SWAP_GENERAL(m_iTotalRolls); + SWAP_GENERAL(m_iTotalMines); + SWAP_GENERAL(m_iTotalHands); + SWAP_GENERAL(m_iTotalLifts); + SWAP_GENERAL(m_bNewProfile); + SWAP_STR_MEMBER(m_UnlockedEntryIDs); + SWAP_STR_MEMBER(m_sLastPlayedMachineGuid); + SWAP_GENERAL(m_LastPlayedDate); + SWAP_GENERAL(m_iNumSongsPlayedByPlayMode); + SWAP_STR_MEMBER(m_iNumSongsPlayedByStyle); + SWAP_GENERAL(m_iNumSongsPlayedByDifficulty); + SWAP_GENERAL(m_iNumSongsPlayedByMeter); + SWAP_GENERAL(m_iNumTotalSongsPlayed); + SWAP_GENERAL(m_iNumStagesPassedByPlayMode); + SWAP_GENERAL(m_iNumStagesPassedByGrade); + SWAP_GENERAL(m_UserTable); + SWAP_STR_MEMBER(m_SongHighScores); + SWAP_STR_MEMBER(m_CourseHighScores); + SWAP_GENERAL(m_CategoryHighScores); + SWAP_STR_MEMBER(m_vScreenshots); + SWAP_STR_MEMBER(m_mapDayToCaloriesBurned); +#undef SWAP_STR_MEMBER +#undef SWAP_GENERAL +} + // Category high scores void Profile::AddCategoryHighScore( StepsType st, RankingCategory rc, HighScore hs, int &iIndexOut ) { @@ -998,6 +1067,7 @@ ProfileLoadResult Profile::LoadAllFromDir( RString sDir, bool bRequireSignature InitAll(); + LoadTypeFromDir(sDir); // Not critical if this fails LoadEditableDataFromDir( sDir ); @@ -1085,6 +1155,34 @@ ProfileLoadResult Profile::LoadAllFromDir( RString sDir, bool bRequireSignature return ProfileLoadResult_Success; } +void Profile::LoadTypeFromDir(RString dir) +{ + m_Type= ProfileType_Normal; + m_ListPriority= 0; + RString fn= dir + TYPE_INI; + if(FILEMAN->DoesFileExist(fn)) + { + IniFile ini; + if(ini.ReadFile(fn)) + { + XNode const* data= ini.GetChild("ListPosition"); + if(data != NULL) + { + RString type_str; + if(data->GetAttrValue("Type", type_str)) + { + m_Type= StringToProfileType(type_str); + if(m_Type >= NUM_ProfileType) + { + m_Type= ProfileType_Normal; + } + } + data->GetAttrValue("Priority", m_ListPriority); + } + } + } +} + ProfileLoadResult Profile::LoadStatsXmlFromNode( const XNode *xml, bool bIgnoreEditable ) { /* The placeholder stats.xml file has an tag. Don't load it, @@ -1135,6 +1233,7 @@ bool Profile::SaveAllToDir( RString sDir, bool bSignData ) const m_sLastPlayedMachineGuid = PROFILEMAN->GetMachineProfile()->m_sGuid; m_LastPlayedDate = DateTime::GetNowDate(); + SaveTypeToDir(sDir); // Save editable.ini SaveEditableDataToDir( sDir ); @@ -1243,6 +1342,14 @@ bool Profile::SaveStatsXmlToDir( RString sDir, bool bSignData ) const return true; } +void Profile::SaveTypeToDir(RString dir) const +{ + IniFile ini; + ini.SetValue("ListPosition", "Type", ProfileTypeToString(m_Type)); + ini.SetValue("ListPosition", "Priority", m_ListPriority); + ini.WriteFile(dir + TYPE_INI); +} + void Profile::SaveEditableDataToDir( RString sDir ) const { IniFile ini; @@ -2289,6 +2396,8 @@ public: p->AddScreenshot(screenshot); return 0; } + DEFINE_METHOD(GetType, m_Type); + DEFINE_METHOD(GetPriority, m_ListPriority); static int GetDisplayName( T* p, lua_State *L ) { lua_pushstring(L, p->m_sDisplayName ); return 1; } static int SetDisplayName( T* p, lua_State *L ) @@ -2511,6 +2620,8 @@ public: LunaProfile() { ADD_METHOD( AddScreenshot ); + ADD_METHOD( GetType ); + ADD_METHOD( GetPriority ); ADD_METHOD( GetDisplayName ); ADD_METHOD( SetDisplayName ); ADD_METHOD( GetLastUsedHighScoreName ); diff --git a/src/Profile.h b/src/Profile.h index a7932bdedc..d89975d175 100644 --- a/src/Profile.h +++ b/src/Profile.h @@ -60,6 +60,18 @@ 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. * @@ -73,7 +85,12 @@ public: * Note: there are probably a lot of variables. */ // When adding new score related data, add logic for handling it to // MergeScoresFromOtherProfile. -Kyz - Profile(): m_sDisplayName(""), m_sCharacterID(""), + // 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), @@ -149,6 +166,10 @@ public: bool IsMachine() const; + ProfileType m_Type; + // Profiles of the same type and priority are sorted by dir name. + int m_ListPriority; + // Editable data RString m_sDisplayName; RString m_sCharacterID; @@ -366,8 +387,11 @@ public: void InitCalorieData(); void ClearStats(); + void swap(Profile& other); + // Loading and saving ProfileLoadResult LoadAllFromDir( RString sDir, bool bRequireSignature ); + void LoadTypeFromDir(RString dir); void LoadCustomFunction( RString sDir ); bool SaveAllToDir( RString sDir, bool bSignData ) const; @@ -380,6 +404,7 @@ public: void LoadScreenshotDataFromNode( const XNode* pNode ); void LoadCalorieDataFromNode( const XNode* pNode ); + void SaveTypeToDir(RString dir) const; void SaveEditableDataToDir( RString sDir ) const; bool SaveStatsXmlToDir( RString sDir, bool bSignData ) const; XNode* SaveStatsXmlCreateNode() const; diff --git a/src/ProfileManager.cpp b/src/ProfileManager.cpp index 85591b9751..cbdc5dbcd2 100644 --- a/src/ProfileManager.cpp +++ b/src/ProfileManager.cpp @@ -27,6 +27,10 @@ ProfileManager* PROFILEMAN = NULL; // global and accessible from anywhere in our program +#define ID_DIGITS 8 +#define ID_DIGITS_STR "8" +#define MAX_ID 99999999 + static void DefaultLocalProfileIDInit( size_t /*PlayerNumber*/ i, RString &sNameOut, RString &defaultValueOut ) { sNameOut = ssprintf( "DefaultLocalProfileIDP%d", int(i+1) ); @@ -54,6 +58,11 @@ struct DirAndProfile { RString sDir; Profile profile; + void swap(DirAndProfile& other) + { + sDir.swap(other.sDir); + profile.swap(other.profile); + } }; static vector g_vLocalProfile; @@ -399,20 +408,67 @@ void ProfileManager::UnloadAllLocalProfiles() g_vLocalProfile.clear(); } +static void add_category_to_global_list(vector& cat) +{ + g_vLocalProfile.insert(g_vLocalProfile.end(), cat.begin(), cat.end()); +} + void ProfileManager::RefreshLocalProfilesFromDisk() { UnloadAllLocalProfiles(); - vector vsProfileID; - GetDirListing( USER_PROFILES_DIR + "*", vsProfileID, true, true ); - FOREACH_CONST( RString, vsProfileID, p ) + vector profile_ids; + GetDirListing(USER_PROFILES_DIR + "*", profile_ids, true, true); + // Profiles have 3 types: + // 1. Guest profiles: + // Meant for use by guests, always at the top of the list. + // 2. Normal profiles: + // Meant for normal use, listed after guests. + // e. Test profiles: + // Meant for use when testing things, listed last. + // If the user renames a profile directory manually, that should not be a + // problem. -Kyz + map > categorized_profiles; + // The type data for a profile is in its own file so that loading isn't + // slowed down by copying temporary profiles around to make sure the list + // is sorted. The profiles are loaded at the end. -Kyz + FOREACH_CONST(RString, profile_ids, id) { - g_vLocalProfile.push_back( DirAndProfile() ); - DirAndProfile &dap = g_vLocalProfile.back(); - dap.sDir = *p + "/"; - dap.profile.LoadAllFromDir( dap.sDir, PREFSMAN->m_bSignProfileData ); + DirAndProfile derp; + derp.sDir= *id + "/"; + derp.profile.LoadTypeFromDir(derp.sDir); + map >::iterator category= + categorized_profiles.find(derp.profile.m_Type); + if(category == categorized_profiles.end()) + { + categorized_profiles[derp.profile.m_Type].push_back(derp); + } + else + { + bool inserted= false; + FOREACH(DirAndProfile, category->second, curr) + { + if(curr->profile.m_ListPriority > derp.profile.m_ListPriority) + { + category->second.insert(curr, derp); + inserted= true; + break; + } + } + if(!inserted) + { + category->second.push_back(derp); + } + } } -} + add_category_to_global_list(categorized_profiles[ProfileType_Guest]); + add_category_to_global_list(categorized_profiles[ProfileType_Normal]); + add_category_to_global_list(categorized_profiles[ProfileType_Test]); + FOREACH(DirAndProfile, g_vLocalProfile, curr) + { + curr->profile.LoadAllFromDir(curr->sDir, PREFSMAN->m_bSignProfileData); + } + } const Profile *ProfileManager::GetLocalProfile( const RString &sProfileID ) const { @@ -433,14 +489,44 @@ bool ProfileManager::CreateLocalProfile( RString sName, RString &sProfileIDOut ) // Find a directory directory name that's a number greater than all // existing numbers. This preserves the "order by create date". - int iMaxProfileNumber = -1; - vector vs; - GetLocalProfileIDs( vs ); - FOREACH_CONST( RString, vs, s ) - iMaxProfileNumber = StringToInt( *s ); + // Profile IDs are actually the directory names, so they can be any string, + // and we have to handle the case where the user renames one. + // Since the user can rename them, they might have any number, wrapping our + // counter or setting it to a ridiculous value. That case must also be + // handled. -Kyz + int max_profile_number= -1; + int first_free_number= 0; + vector profile_ids; + GetLocalProfileIDs(profile_ids); + FOREACH_CONST(RString, profile_ids, id) + { + int tmp= 0; + if((*id) >> tmp) + { + // The profile ids are already in order, so we don't have to handle the + // case where 5 is encountered before 3. + if(tmp == first_free_number) + { + ++first_free_number; + } + max_profile_number= max(tmp, max_profile_number); + } + } - int iProfileNumber = iMaxProfileNumber + 1; - RString sProfileID = ssprintf( "%08d", iProfileNumber ); + int profile_number = max_profile_number + 1; + // Prevent profiles from going over the 8 digit limit. + if(profile_number > MAX_ID || profile_number < 0) + { + profile_number= first_free_number; + } + ASSERT_M(profile_number >= 0 && profile_number <= MAX_ID, + "Too many profiles, cannot assign ID to new profile."); + RString profile_id = ssprintf( "%0" ID_DIGITS_STR "d", profile_number ); + + // make sure this id doesn't already exist + ASSERT_M(GetLocalProfile(profile_id) == NULL, + ssprintf("creating profile with ID \"%s\" that already exists", + profile_id.c_str())); // Create the new profile. Profile *pProfile = new Profile; @@ -448,7 +534,7 @@ bool ProfileManager::CreateLocalProfile( RString sName, RString &sProfileIDOut ) pProfile->m_sCharacterID = CHARMAN->GetRandomCharacter()->m_sCharacterID; // Save it to disk. - RString sProfileDir = LocalProfileIDToDir( sProfileID ); + RString sProfileDir = LocalProfileIDToDir(profile_id); if( !pProfile->SaveAllToDir(sProfileDir, PREFSMAN->m_bSignProfileData) ) { delete pProfile; @@ -456,12 +542,37 @@ bool ProfileManager::CreateLocalProfile( RString sName, RString &sProfileIDOut ) return false; } - AddLocalProfileByID( pProfile, sProfileID ); + AddLocalProfileByID(pProfile, profile_id); - sProfileIDOut = sProfileID; + sProfileIDOut = profile_id; return true; } +static void InsertProfileIntoList(DirAndProfile& derp) +{ + bool inserted= false; + derp.profile.m_ListPriority= 0; + FOREACH(DirAndProfile, g_vLocalProfile, curr) + { + if(curr->profile.m_Type > derp.profile.m_Type) + { + derp.profile.SaveTypeToDir(derp.sDir); + g_vLocalProfile.insert(curr, derp); + inserted= true; + break; + } + else if(curr->profile.m_Type == derp.profile.m_Type) + { + ++derp.profile.m_ListPriority; + } + } + if(!inserted) + { + derp.profile.SaveTypeToDir(derp.sDir); + g_vLocalProfile.push_back(derp); + } +} + void ProfileManager::AddLocalProfileByID( Profile *pProfile, RString sProfileID ) { // make sure this id doesn't already exist @@ -469,11 +580,10 @@ void ProfileManager::AddLocalProfileByID( Profile *pProfile, RString sProfileID ssprintf("creating \"%s\" \"%s\" that already exists", pProfile->m_sDisplayName.c_str(), sProfileID.c_str()) ); - // insert - g_vLocalProfile.push_back( DirAndProfile() ); - DirAndProfile &dap = g_vLocalProfile.back(); - dap.sDir = LocalProfileIDToDir( sProfileID ); - dap.profile = *pProfile; + DirAndProfile derp; + derp.sDir= LocalProfileIDToDir(sProfileID); + derp.profile= *pProfile; + InsertProfileIntoList(derp); } bool ProfileManager::RenameLocalProfile( RString sProfileID, RString sNewName ) @@ -643,6 +753,58 @@ void ProfileManager::MergeLocalProfileIntoMachine(RString const& from_id, bool s LocalProfileIDToDir(from_id), MACHINE_PROFILE_DIR); } +void ProfileManager::ChangeProfileType(int index, ProfileType new_type) +{ + if(index < 0 || static_cast(index) >= g_vLocalProfile.size()) + { return; } + if(new_type == g_vLocalProfile[index].profile.m_Type) + { return; } + DirAndProfile derp= g_vLocalProfile[index]; + g_vLocalProfile.erase(g_vLocalProfile.begin() + index); + derp.profile.m_Type= new_type; + InsertProfileIntoList(derp); +} + +void ProfileManager::MoveProfilePriority(int index, bool up) +{ + if(index < 0 || static_cast(index) >= g_vLocalProfile.size()) + { return; } + // Changing the priority is complicated a bit because the profiles might + // all have the same priority. So this function has to assign priorities + // to all the profiles of the same type. + // bools are numbers, true evaluatues to 1. + int swindex= index + ((up * -2) + 1); + ProfileType type= g_vLocalProfile[index].profile.m_Type; + int priority= 0; + for(size_t i= 0; i < g_vLocalProfile.size(); ++i) + { + DirAndProfile* curr= &g_vLocalProfile[i]; + if(curr->profile.m_Type == type) + { + if(curr->profile.m_ListPriority != priority) + { + curr->profile.m_ListPriority= priority; + if(i != index && i != swindex) + { + curr->profile.SaveTypeToDir(curr->sDir); + } + } + ++priority; + } + else if(curr->profile.m_Type > type) + { + break; + } + } + // Only swap if both indices are valid and the types match. + if(swindex >= 0 && static_cast(swindex) < g_vLocalProfile.size() && + g_vLocalProfile[swindex].profile.m_Type == + g_vLocalProfile[index].profile.m_Type) + { + g_vLocalProfile[index].swap(g_vLocalProfile[swindex]); + } +} + // // General // @@ -862,8 +1024,31 @@ public: lua_pushnil(L); return 1; } - static int GetLocalProfileFromIndex( T* p, lua_State *L ) { Profile *pProfile = p->GetLocalProfileFromIndex(IArg(1)); ASSERT(pProfile != NULL); pProfile->PushSelf(L); return 1; } - static int GetLocalProfileIDFromIndex( T* p, lua_State *L ) { lua_pushstring(L, p->GetLocalProfileIDFromIndex(IArg(1)) ); return 1; } + static int GetLocalProfileFromIndex( T* p, lua_State *L ) + { + int index= IArg(1); + if(index >= p->GetNumLocalProfiles()) + { + luaL_error(L, "Profile index %d out of range.", index); + } + Profile *pProfile = p->GetLocalProfileFromIndex(index); + if(pProfile == NULL) + { + luaL_error(L, "No profile at index %d.", index); + } + pProfile->PushSelf(L); + return 1; + } + static int GetLocalProfileIDFromIndex( T* p, lua_State *L ) + { + int index= IArg(1); + if(index >= p->GetNumLocalProfiles()) + { + luaL_error(L, "Profile index %d out of range.", index); + } + lua_pushstring(L, p->GetLocalProfileIDFromIndex(index) ); + return 1; + } static int GetLocalProfileIndexFromID( T* p, lua_State *L ) { lua_pushnumber(L, p->GetLocalProfileIndexFromID(SArg(1)) ); return 1; } static int GetNumLocalProfiles( T* p, lua_State *L ) { lua_pushnumber(L, p->GetNumLocalProfiles() ); return 1; } static int GetProfileDir( T* p, lua_State *L ) { lua_pushstring(L, p->GetProfileDir(Enum::Check(L, 1)) ); return 1; } diff --git a/src/ProfileManager.h b/src/ProfileManager.h index eb41ff4bb0..dd457292f6 100644 --- a/src/ProfileManager.h +++ b/src/ProfileManager.h @@ -6,8 +6,8 @@ #include "Difficulty.h" #include "Preference.h" #include "Grade.h" +#include "Profile.h" -class Profile; class Song; class Steps; class Style; @@ -54,6 +54,8 @@ public: void MergeLocalProfiles(RString const& from_id, RString const& to_id); void MergeLocalProfileIntoMachine(RString const& from_id, bool skip_totals); + void ChangeProfileType(int index, ProfileType new_type); + void MoveProfilePriority(int index, bool up); // General data void IncrementToastiesCount( PlayerNumber pn ); diff --git a/src/ScreenOptionsManageProfiles.cpp b/src/ScreenOptionsManageProfiles.cpp index 0b1d450dde..f80665135b 100644 --- a/src/ScreenOptionsManageProfiles.cpp +++ b/src/ScreenOptionsManageProfiles.cpp @@ -34,6 +34,11 @@ enum ProfileAction ProfileAction_MergeToMachineSkipTotal, ProfileAction_MergeToP1, ProfileAction_MergeToP2, + ProfileAction_ChangeToGuest, + ProfileAction_ChangeToNormal, + ProfileAction_ChangeToTest, + ProfileAction_MoveUp, + ProfileAction_MoveDown, NUM_ProfileAction }; static const char *ProfileActionNames[] = { @@ -47,6 +52,11 @@ static const char *ProfileActionNames[] = { "MergeToMachineSkipTotal", "MergeToP1", "MergeToP2", + "ChangeToGuest", + "ChangeToNormal", + "ChangeToTest", + "MoveUp", + "MoveDown", }; XToString( ProfileAction ); XToLocalizedString( ProfileAction ); @@ -333,6 +343,29 @@ void ScreenOptionsManageProfiles::HandleScreenMessage( const ScreenMessage SM ) PROFILEMAN->MergeLocalProfiles(GetLocalProfileIDWithFocus(), ProfileManager::m_sDefaultLocalProfileID[PLAYER_2].Get()); break; + case ProfileAction_ChangeToGuest: + PROFILEMAN->ChangeProfileType(GetLocalProfileIndexWithFocus(), + ProfileType_Guest); + SCREENMAN->SetNewScreen(this->m_sName); // reload + break; + case ProfileAction_ChangeToNormal: + PROFILEMAN->ChangeProfileType(GetLocalProfileIndexWithFocus(), + ProfileType_Normal); + SCREENMAN->SetNewScreen(this->m_sName); // reload + break; + case ProfileAction_ChangeToTest: + PROFILEMAN->ChangeProfileType(GetLocalProfileIndexWithFocus(), + ProfileType_Test); + SCREENMAN->SetNewScreen(this->m_sName); // reload + break; + case ProfileAction_MoveUp: + PROFILEMAN->MoveProfilePriority(GetLocalProfileIndexWithFocus(), true); + SCREENMAN->SetNewScreen(this->m_sName); // reload + break; + case ProfileAction_MoveDown: + PROFILEMAN->MoveProfilePriority(GetLocalProfileIndexWithFocus(), false); + SCREENMAN->SetNewScreen(this->m_sName); // reload + break; } } } @@ -411,6 +444,11 @@ void ScreenOptionsManageProfiles::ProcessMenuStart( const InputEventPlus & ) ADD_ACTION( ProfileAction_MergeToMachineSkipTotal ); ADD_ACTION( ProfileAction_MergeToP1 ); ADD_ACTION( ProfileAction_MergeToP2 ); + ADD_ACTION( ProfileAction_ChangeToGuest ); + ADD_ACTION( ProfileAction_ChangeToNormal ); + ADD_ACTION( ProfileAction_ChangeToTest ); + ADD_ACTION( ProfileAction_MoveUp ); + ADD_ACTION( ProfileAction_MoveDown ); } int iWidth, iX, iY;