mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-23 01:59:43 +01:00
Refactor how custom level stats are stored, read, and written
There were a few problems with the old way of doing things: (1) Level stats were an ad-hoc object. Basically, it's an object whose attributes are stored in separate arrays, instead of being an actual object with its attributes stored in one array. (2) Level filenames with pipes in them could cause trouble. This is because the filename attribute array was stored in the XML by being separated by pipes. (3) There was an arbitrary limit of only having 200 level stats, for whatever reason. To remedy this issue, I've made a new struct named CustomLevelStat that is a proper object. The separate attribute arrays have been replaced with a proper vector, which also doesn't have a size limit. For compatibility with versions 2.2 and below, I've kept being able to read the old format. This only happens if the new format doesn't exist. However, I also WRITE the old format as well, in case you want to go back to version 2.2 or below for whatever reason. It's slightly wasteful to have both, but that way there's no risk of breaking compatibility.
This commit is contained in:
parent
f64e9237c4
commit
9a008dc77c
2 changed files with 89 additions and 38 deletions
|
@ -541,13 +541,8 @@ void Game::lifesequence()
|
|||
|
||||
void Game::clearcustomlevelstats()
|
||||
{
|
||||
//just clearing the arrays
|
||||
for(int i=0; i<200; i++)
|
||||
{
|
||||
customlevelstats[i]="";
|
||||
customlevelscore[i]=0;
|
||||
}
|
||||
numcustomlevelstats=0;
|
||||
//just clearing the array
|
||||
customlevelstats.clear();
|
||||
|
||||
customlevelstatsloaded=false; //To ensure we don't load it where it isn't needed
|
||||
}
|
||||
|
@ -560,28 +555,24 @@ void Game::updatecustomlevelstats(std::string clevel, int cscore)
|
|||
clevel = clevel.substr(7);
|
||||
}
|
||||
int tvar=-1;
|
||||
for(int j=0; j<numcustomlevelstats; j++)
|
||||
for(size_t j=0; j<customlevelstats.size(); j++)
|
||||
{
|
||||
if(clevel==customlevelstats[j])
|
||||
if(clevel==customlevelstats[j].name)
|
||||
{
|
||||
tvar=j;
|
||||
j=numcustomlevelstats+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(tvar>=0 && cscore > customlevelscore[tvar])
|
||||
if(tvar>=0 && cscore > customlevelstats[tvar].score)
|
||||
{
|
||||
//update existing entry
|
||||
customlevelscore[tvar]=cscore;
|
||||
customlevelstats[tvar].score=cscore;
|
||||
}
|
||||
else
|
||||
{
|
||||
//add a new entry
|
||||
if(numcustomlevelstats<200)
|
||||
{
|
||||
customlevelstats[numcustomlevelstats]=clevel;
|
||||
customlevelscore[numcustomlevelstats]=cscore;
|
||||
numcustomlevelstats++;
|
||||
}
|
||||
CustomLevelStat levelstat = {clevel, cscore};
|
||||
customlevelstats.push_back(levelstat);
|
||||
}
|
||||
savecustomlevelstats();
|
||||
}
|
||||
|
@ -595,11 +586,15 @@ void Game::loadcustomlevelstats()
|
|||
if (!FILESYSTEM_loadTiXml2Document("saves/levelstats.vvv", doc))
|
||||
{
|
||||
//No levelstats file exists; start new
|
||||
numcustomlevelstats=0;
|
||||
customlevelstats.clear();
|
||||
savecustomlevelstats();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Old system
|
||||
std::vector<std::string> customlevelnames;
|
||||
std::vector<int> customlevelscores;
|
||||
|
||||
tinyxml2::XMLHandle hDoc(&doc);
|
||||
tinyxml2::XMLElement* pElem;
|
||||
tinyxml2::XMLHandle hRoot(NULL);
|
||||
|
@ -616,7 +611,8 @@ void Game::loadcustomlevelstats()
|
|||
hRoot=tinyxml2::XMLHandle(pElem);
|
||||
}
|
||||
|
||||
|
||||
// First pass, look for the new system of storing stats
|
||||
// If they don't exist, then fall back to the old system
|
||||
for (pElem = hRoot.FirstChildElement("Data").FirstChild().ToElement(); pElem; pElem = pElem->NextSiblingElement())
|
||||
{
|
||||
std::string pKey(pElem->Value());
|
||||
|
@ -626,10 +622,38 @@ void Game::loadcustomlevelstats()
|
|||
pText = "";
|
||||
}
|
||||
|
||||
if (pKey == "numcustomlevelstats")
|
||||
if (pKey == "stats")
|
||||
{
|
||||
numcustomlevelstats = atoi(pText);
|
||||
if(numcustomlevelstats>=200) numcustomlevelstats=199;
|
||||
for (tinyxml2::XMLElement* stat_el = pElem->FirstChildElement(); stat_el; stat_el = stat_el->NextSiblingElement())
|
||||
{
|
||||
CustomLevelStat stat = {};
|
||||
|
||||
if (stat_el->GetText() != NULL)
|
||||
{
|
||||
stat.score = atoi(stat_el->GetText());
|
||||
}
|
||||
|
||||
if (stat_el->Attribute("name"))
|
||||
{
|
||||
stat.name = stat_el->Attribute("name");
|
||||
}
|
||||
|
||||
customlevelstats.push_back(stat);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Since we're still here, we must be on the old system
|
||||
for( pElem = hRoot.FirstChildElement( "Data" ).FirstChild().ToElement(); pElem; pElem=pElem->NextSiblingElement())
|
||||
{
|
||||
std::string pKey(pElem->Value());
|
||||
const char* pText = pElem->GetText() ;
|
||||
if(pText == NULL)
|
||||
{
|
||||
pText = "";
|
||||
}
|
||||
|
||||
if (pKey == "customlevelscore")
|
||||
|
@ -640,7 +664,7 @@ void Game::loadcustomlevelstats()
|
|||
std::vector<std::string> values = split(TextString,',');
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
if(i<200) customlevelscore[i]=(atoi(values[i].c_str()));
|
||||
customlevelscores.push_back(atoi(values[i].c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -653,11 +677,18 @@ void Game::loadcustomlevelstats()
|
|||
std::vector<std::string> values = split(TextString,'|');
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
if(i<200) customlevelstats[i]=values[i];
|
||||
customlevelnames.push_back(values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the two arrays happen to differ in length, just go with the smallest one
|
||||
for (size_t i = 0; i < std::min(customlevelnames.size(), customlevelscores.size()); i++)
|
||||
{
|
||||
CustomLevelStat stat = {customlevelnames[i], customlevelscores[i]};
|
||||
customlevelstats.push_back(stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -678,6 +709,7 @@ void Game::savecustomlevelstats()
|
|||
tinyxml2::XMLElement * msgs = doc.NewElement( "Data" );
|
||||
root->LinkEndChild( msgs );
|
||||
|
||||
int numcustomlevelstats = customlevelstats.size();
|
||||
if(numcustomlevelstats>=200)numcustomlevelstats=199;
|
||||
msg = doc.NewElement( "numcustomlevelstats" );
|
||||
msg->LinkEndChild( doc.NewText( help.String(numcustomlevelstats).c_str() ));
|
||||
|
@ -686,7 +718,7 @@ void Game::savecustomlevelstats()
|
|||
std::string customlevelscorestr;
|
||||
for(int i = 0; i < numcustomlevelstats; i++ )
|
||||
{
|
||||
customlevelscorestr += help.String(customlevelscore[i]) + ",";
|
||||
customlevelscorestr += help.String(customlevelstats[i].score) + ",";
|
||||
}
|
||||
msg = doc.NewElement( "customlevelscore" );
|
||||
msg->LinkEndChild( doc.NewText( customlevelscorestr.c_str() ));
|
||||
|
@ -695,12 +727,27 @@ void Game::savecustomlevelstats()
|
|||
std::string customlevelstatsstr;
|
||||
for(int i = 0; i < numcustomlevelstats; i++ )
|
||||
{
|
||||
customlevelstatsstr += customlevelstats[i] + "|";
|
||||
customlevelstatsstr += customlevelstats[i].name + "|";
|
||||
}
|
||||
msg = doc.NewElement( "customlevelstats" );
|
||||
msg->LinkEndChild( doc.NewText( customlevelstatsstr.c_str() ));
|
||||
msgs->LinkEndChild( msg );
|
||||
|
||||
// New system
|
||||
msg = doc.NewElement("stats");
|
||||
tinyxml2::XMLElement* stat_el;
|
||||
for (size_t i = 0; i < customlevelstats.size(); i++)
|
||||
{
|
||||
stat_el = doc.NewElement("stat");
|
||||
CustomLevelStat& stat = customlevelstats[i];
|
||||
|
||||
stat_el->SetAttribute("name", stat.name.c_str());
|
||||
stat_el->LinkEndChild(doc.NewText(help.String(stat.score).c_str()));
|
||||
|
||||
msg->LinkEndChild(stat_el);
|
||||
}
|
||||
msgs->LinkEndChild(msg);
|
||||
|
||||
if(FILESYSTEM_saveTiXml2Document("saves/levelstats.vvv", doc))
|
||||
{
|
||||
printf("Level stats saved\n");
|
||||
|
@ -7029,26 +7076,26 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ )
|
|||
{
|
||||
//This is, er, suboptimal. Whatever, life optimisation and all that
|
||||
int tvar=-1;
|
||||
for(int j=0; j<numcustomlevelstats; j++)
|
||||
for(size_t j=0; j<customlevelstats.size(); j++)
|
||||
{
|
||||
if(ed.ListOfMetaData[i].filename.substr(7) == customlevelstats[j])
|
||||
if(ed.ListOfMetaData[i].filename.substr(7) == customlevelstats[j].name)
|
||||
{
|
||||
tvar=j;
|
||||
j=numcustomlevelstats+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::string text;
|
||||
if(tvar>=0)
|
||||
{
|
||||
if(customlevelscore[tvar]==0)
|
||||
if(customlevelstats[tvar].score==0)
|
||||
{
|
||||
text = " " + ed.ListOfMetaData[i].title;
|
||||
}
|
||||
else if(customlevelscore[tvar]==1)
|
||||
else if(customlevelstats[tvar].score==1)
|
||||
{
|
||||
text = " * " + ed.ListOfMetaData[i].title;
|
||||
}
|
||||
else if(customlevelscore[tvar]==3)
|
||||
else if(customlevelstats[tvar].score==3)
|
||||
{
|
||||
text = "** " + ed.ListOfMetaData[i].title;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,12 @@ struct MenuStackFrame
|
|||
enum Menu::MenuName name;
|
||||
};
|
||||
|
||||
struct CustomLevelStat
|
||||
{
|
||||
std::string name;
|
||||
int score; //0 - not played, 1 - finished, 2 - all trinkets, 3 - finished, all trinkets
|
||||
};
|
||||
|
||||
|
||||
class Game
|
||||
{
|
||||
|
@ -363,9 +369,7 @@ public:
|
|||
void savecustomlevelstats();
|
||||
void updatecustomlevelstats(std::string clevel, int cscore);
|
||||
|
||||
std::string customlevelstats[200]; //string array containing level filenames
|
||||
int customlevelscore[200];//0 - not played, 1 - finished, 2 - all trinkets, 3 - finished, all trinkets
|
||||
int numcustomlevelstats;
|
||||
std::vector<CustomLevelStat> customlevelstats;
|
||||
bool customlevelstatsloaded;
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue