Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
48 KB
Referenced Files
None
Subscribers
None
diff --git a/src/AchievementList.h b/src/AchievementList.cpp
similarity index 97%
copy from src/AchievementList.h
copy to src/AchievementList.cpp
index 28a1159..2a07812 100644
--- a/src/AchievementList.h
+++ b/src/AchievementList.cpp
@@ -1,105 +1,109 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
-// NOTE: This is an internal file for all avaliable achievements.
-// Don't include it in other files!
+#include "AchievementList.h"
-// Format: {id, name, file, pos, description, [type]}
+// We redefined it here to prevent including more header files.
+#ifndef __
+#define __(X) (X)
+#endif
+
+// Format: {<id>, <name>, <file>, <pos>, <description>, <type>, nullptr}
// id: Identifier of achievement. Should not change once the new achievement is added,
// otherwise the old statistics will be lost.
// name: Name of achievement. Translatable.
// file: The icon file. NULL for no icon.
// pos: Specifies which part of the icon will be displayed (left, top, width, height).
// description: Description of achievement. Translatable. Can be multi-line text.
// type [optional]: Specifies the display type of achievement.
// ACHIEVEMENT_HIDDEN [default]: Show "Unknown achievement" when unfinished.
// ACHIEVEMENT_TITLE: Only show icon and title when unfinished.
// ACHIEVEMENT_ALL: Always show icon, title and description.
// ACHIEVEMENT_PROGRESS: Show icon, title and description and a progress bar.
// StatisticsManager::getAchievementProgress() function should return the progress (between 0 and 1).
// NOTE: WARNING: All arguments should now be specified, as surface being nullptr is being relied upon.
AchievementInfo achievementList[]={
{"newbie",__("Newbie"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Complete a level."),ACHIEVEMENT_ALL,nullptr},
{"experienced",__("Experienced player"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Complete 50 levels."),ACHIEVEMENT_PROGRESS,nullptr},
{"goodjob",__("Good job!"),"gfx/medals.png",{60,0,30,30},__("Receive a gold medal."),ACHIEVEMENT_ALL,nullptr},
{"expert",__("Expert"),"gfx/medals.png",{60,0,30,30},__("Earn 50 gold medal."),ACHIEVEMENT_PROGRESS,nullptr},
{"tutorial",__("Graduate"),"gfx/medals.png",{60,0,30,30},__("Complete the tutorial level pack."),ACHIEVEMENT_PROGRESS,nullptr},
{"tutorialGold",__("Outstanding graduate"),"gfx/medals.png",{60,0,30,30},__("Complete the tutorial level pack with gold for all levels."),ACHIEVEMENT_PROGRESS,nullptr},
{"addicted",__("Hooked"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Play Me and My Shadow for more than 2 hours."),ACHIEVEMENT_HIDDEN,nullptr},
{"loyalFan",__("Loyal fan of Me and My Shadow"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Play Me and My Shadow for more than 24 hours."),ACHIEVEMENT_HIDDEN,nullptr},
{"constructor",__("Constructor"),"gfx/gui.png",{112,16,16,16},__("Use the level editor for more than 2 hours."),ACHIEVEMENT_HIDDEN,nullptr},
{"constructor2",__("The creator"),"gfx/gui.png",{112,16,16,16},__("Use the level editor for more than 24 hours."),ACHIEVEMENT_HIDDEN,nullptr},
{"create1",__("Look, cute level!"),"gfx/gui.png",{112,16,16,16},__("Create a level for the first time."),ACHIEVEMENT_ALL,nullptr},
{"create50",__("The level museum"),"gfx/gui.png",{112,16,16,16},__("Create 50 levels."),ACHIEVEMENT_PROGRESS,nullptr},
{"frog",__("Frog"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Jump 1000 times."),ACHIEVEMENT_PROGRESS,nullptr},
{"travel100",__("Wanderer"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 100 meters."),ACHIEVEMENT_PROGRESS,nullptr},
{"travel1k",__("Runner"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 1 kilometer."),ACHIEVEMENT_PROGRESS,nullptr},
{"travel10k",__("Long distance runner"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 10 kilometers."),ACHIEVEMENT_PROGRESS,nullptr},
{"travel42k",__("Marathon runner"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 42,195 meters."),ACHIEVEMENT_PROGRESS,nullptr},
{"die1",__("Be careful!"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die for the first time."),ACHIEVEMENT_ALL,nullptr},
{"die50",__("It doesn't matter..."),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 50 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"die1000",__("Expert of trial and error"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"squash1",__("Keep an eye for moving blocks!"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Get squashed for the first time."),ACHIEVEMENT_HIDDEN,nullptr},
{"squash50",__("Potato masher"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Get squashed 50 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"doubleKill",__("Double kill"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Get both the player and the shadow dead."),ACHIEVEMENT_HIDDEN,nullptr},
{"die5in5",__("Bad luck"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 5 times in under 5 seconds."),ACHIEVEMENT_HIDDEN,nullptr},
{"die10in5",__("This level is too dangerous"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 10 times in under 5 seconds."),ACHIEVEMENT_HIDDEN,nullptr},
{"forget",__("You forgot your friend"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Finish the level with the player or the shadow dead."),ACHIEVEMENT_HIDDEN,nullptr},
{"jit",__("Just in time"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Reach the exit with the player and the shadow simultaneously."),ACHIEVEMENT_HIDDEN,nullptr},
{"record100",__("Recorder"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Record 100 times."),ACHIEVEMENT_PROGRESS,nullptr},
{"record1k",__("Shadowmaster"),"themes/Cloudscape/characters/shadow.png",{0,0,23,40},__("Record 1000 times."),ACHIEVEMENT_PROGRESS,nullptr},
{"switch100",__("Switch puller"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Pull the switch 100 times."),ACHIEVEMENT_PROGRESS,nullptr},
{"switch1k",__("The switch is broken!"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Pull the switch 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"swap100",__("Swapper"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Swap 100 times."),ACHIEVEMENT_PROGRESS,nullptr},
{"swap1k",__("Player to shadow to player to shadow..."),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Swap 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"save1k",__("Play it save"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Save 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"load1k",__("This game is too hard"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Load the game 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"panicSave",__("Panic save"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Save twice in 1 second."),ACHIEVEMENT_HIDDEN,nullptr},
{"panicLoad",__("Panic load"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Load twice in 1 second."),ACHIEVEMENT_HIDDEN,nullptr},
{"loadAndDie",__("Bad saving position"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Load the game and die within 1 second."),ACHIEVEMENT_HIDDEN,nullptr},
{"loadAndDie100",__("This level is too hard"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Load the same save and die 100 times."),ACHIEVEMENT_HIDDEN,nullptr},
{"quickswap",__("Quick swap"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Swap twice in under a second."),ACHIEVEMENT_HIDDEN,nullptr},
//ripped from Achievements Unlocked
{"horizontal",__("Horizontal confusion"),"gfx/emotions.png",{0,0,23,40},__("Press left and right simultaneously."),ACHIEVEMENT_HIDDEN,nullptr},
{"programmer",__("Programmer"),"gfx/gui.png",{112,16,16,16},__("Play the development version of Me and My Shadow."),ACHIEVEMENT_TITLE,nullptr},
//end of achievements
{}
};
diff --git a/src/AchievementList.h b/src/AchievementList.h
index 28a1159..e2518b7 100644
--- a/src/AchievementList.h
+++ b/src/AchievementList.h
@@ -1,105 +1,55 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
-// NOTE: This is an internal file for all avaliable achievements.
-// Don't include it in other files!
+#ifndef ACHIEVEMENTLIST_H
+#define ACHIEVEMENTLIST_H
-// Format: {id, name, file, pos, description, [type]}
-// id: Identifier of achievement. Should not change once the new achievement is added,
-// otherwise the old statistics will be lost.
-// name: Name of achievement. Translatable.
-// file: The icon file. NULL for no icon.
-// pos: Specifies which part of the icon will be displayed (left, top, width, height).
-// description: Description of achievement. Translatable. Can be multi-line text.
-// type [optional]: Specifies the display type of achievement.
-// ACHIEVEMENT_HIDDEN [default]: Show "Unknown achievement" when unfinished.
-// ACHIEVEMENT_TITLE: Only show icon and title when unfinished.
-// ACHIEVEMENT_ALL: Always show icon, title and description.
-// ACHIEVEMENT_PROGRESS: Show icon, title and description and a progress bar.
-// StatisticsManager::getAchievementProgress() function should return the progress (between 0 and 1).
-// NOTE: WARNING: All arguments should now be specified, as surface being nullptr is being relied upon.
+#include <SDL_rect.h>
-AchievementInfo achievementList[]={
- {"newbie",__("Newbie"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Complete a level."),ACHIEVEMENT_ALL,nullptr},
- {"experienced",__("Experienced player"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Complete 50 levels."),ACHIEVEMENT_PROGRESS,nullptr},
- {"goodjob",__("Good job!"),"gfx/medals.png",{60,0,30,30},__("Receive a gold medal."),ACHIEVEMENT_ALL,nullptr},
- {"expert",__("Expert"),"gfx/medals.png",{60,0,30,30},__("Earn 50 gold medal."),ACHIEVEMENT_PROGRESS,nullptr},
+struct SDL_Surface;
- {"tutorial",__("Graduate"),"gfx/medals.png",{60,0,30,30},__("Complete the tutorial level pack."),ACHIEVEMENT_PROGRESS,nullptr},
- {"tutorialGold",__("Outstanding graduate"),"gfx/medals.png",{60,0,30,30},__("Complete the tutorial level pack with gold for all levels."),ACHIEVEMENT_PROGRESS,nullptr},
-
- {"addicted",__("Hooked"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Play Me and My Shadow for more than 2 hours."),ACHIEVEMENT_HIDDEN,nullptr},
- {"loyalFan",__("Loyal fan of Me and My Shadow"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Play Me and My Shadow for more than 24 hours."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"constructor",__("Constructor"),"gfx/gui.png",{112,16,16,16},__("Use the level editor for more than 2 hours."),ACHIEVEMENT_HIDDEN,nullptr},
- {"constructor2",__("The creator"),"gfx/gui.png",{112,16,16,16},__("Use the level editor for more than 24 hours."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"create1",__("Look, cute level!"),"gfx/gui.png",{112,16,16,16},__("Create a level for the first time."),ACHIEVEMENT_ALL,nullptr},
- {"create50",__("The level museum"),"gfx/gui.png",{112,16,16,16},__("Create 50 levels."),ACHIEVEMENT_PROGRESS,nullptr},
-
- {"frog",__("Frog"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Jump 1000 times."),ACHIEVEMENT_PROGRESS,nullptr},
-
- {"travel100",__("Wanderer"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 100 meters."),ACHIEVEMENT_PROGRESS,nullptr},
- {"travel1k",__("Runner"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 1 kilometer."),ACHIEVEMENT_PROGRESS,nullptr},
- {"travel10k",__("Long distance runner"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 10 kilometers."),ACHIEVEMENT_PROGRESS,nullptr},
- {"travel42k",__("Marathon runner"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Travel 42,195 meters."),ACHIEVEMENT_PROGRESS,nullptr},
-
- {"die1",__("Be careful!"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die for the first time."),ACHIEVEMENT_ALL,nullptr},
- {"die50",__("It doesn't matter..."),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 50 times."),ACHIEVEMENT_HIDDEN,nullptr},
- {"die1000",__("Expert of trial and error"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"squash1",__("Keep an eye for moving blocks!"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Get squashed for the first time."),ACHIEVEMENT_HIDDEN,nullptr},
- {"squash50",__("Potato masher"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Get squashed 50 times."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"doubleKill",__("Double kill"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Get both the player and the shadow dead."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"die5in5",__("Bad luck"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 5 times in under 5 seconds."),ACHIEVEMENT_HIDDEN,nullptr},
- {"die10in5",__("This level is too dangerous"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Die 10 times in under 5 seconds."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"forget",__("You forgot your friend"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Finish the level with the player or the shadow dead."),ACHIEVEMENT_HIDDEN,nullptr},
- {"jit",__("Just in time"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Reach the exit with the player and the shadow simultaneously."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"record100",__("Recorder"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Record 100 times."),ACHIEVEMENT_PROGRESS,nullptr},
- {"record1k",__("Shadowmaster"),"themes/Cloudscape/characters/shadow.png",{0,0,23,40},__("Record 1000 times."),ACHIEVEMENT_PROGRESS,nullptr},
-
- {"switch100",__("Switch puller"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Pull the switch 100 times."),ACHIEVEMENT_PROGRESS,nullptr},
- {"switch1k",__("The switch is broken!"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Pull the switch 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"swap100",__("Swapper"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Swap 100 times."),ACHIEVEMENT_PROGRESS,nullptr},
- {"swap1k",__("Player to shadow to player to shadow..."),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Swap 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"save1k",__("Play it save"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Save 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
- {"load1k",__("This game is too hard"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Load the game 1000 times."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"panicSave",__("Panic save"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Save twice in 1 second."),ACHIEVEMENT_HIDDEN,nullptr},
- {"panicLoad",__("Panic load"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Load twice in 1 second."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"loadAndDie",__("Bad saving position"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Load the game and die within 1 second."),ACHIEVEMENT_HIDDEN,nullptr},
- {"loadAndDie100",__("This level is too hard"),"themes/Cloudscape/characters/deathright.png",{0,14,23,40},__("Load the same save and die 100 times."),ACHIEVEMENT_HIDDEN,nullptr},
-
- {"quickswap",__("Quick swap"),"themes/Cloudscape/characters/player.png",{0,0,23,40},__("Swap twice in under a second."),ACHIEVEMENT_HIDDEN,nullptr},
+enum AchievementDisplayStyle{
+ ACHIEVEMENT_HIDDEN,
+ ACHIEVEMENT_TITLE,
+ ACHIEVEMENT_ALL,
+ ACHIEVEMENT_PROGRESS,
+};
- //ripped from Achievements Unlocked
- {"horizontal",__("Horizontal confusion"),"gfx/emotions.png",{0,0,23,40},__("Press left and right simultaneously."),ACHIEVEMENT_HIDDEN,nullptr},
+//internal struct for achievement info
+struct AchievementInfo{
+ //achievement id for save to statistics file
+ const char* id;
+ //achievement name for display
+ const char* name;
+ //achievement image. NULL for no image. will be loaded at getDataPath()+imageFile
+ const char* imageFile;
+ //image offset and size.
+ SDL_Rect r;
+ //achievement description. supports multi-line text
+ const char* description;
+ //display style
+ AchievementDisplayStyle displayStyle;
+
+ //SDL_Surface of achievement image. Should always be NULL before loading.
+ SDL_Surface* imageSurface;
+};
- {"programmer",__("Programmer"),"gfx/gui.png",{112,16,16,16},__("Play the development version of Me and My Shadow."),ACHIEVEMENT_TITLE,nullptr},
+extern AchievementInfo achievementList[];
- //end of achievements
- {}
-};
+#endif
diff --git a/src/StatisticsManager.cpp b/src/StatisticsManager.cpp
index 309dede..7285bb6 100644
--- a/src/StatisticsManager.cpp
+++ b/src/StatisticsManager.cpp
@@ -1,795 +1,793 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StatisticsManager.h"
#include "FileManager.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "Functions.h"
#include "LevelPackManager.h"
#include "MusicManager.h"
#include "SoundManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include "libs/tinyformat/tinyformat.h"
#include <SDL_ttf.h>
#if defined(WIN32)
#define PRINTF_LONGLONG "%I64d"
#else
#define PRINTF_LONGLONG "%lld"
#endif
using namespace std;
StatisticsManager statsMgr;
static const int achievementDisplayTime=(FPS*4500)/1000;
static const int achievementIntervalTime=achievementDisplayTime+(FPS*500)/1000;
-#include "AchievementList.h"
-
static map<string,AchievementInfo*> avaliableAchievements;
//================================================================
StatisticsManager::StatisticsManager(){
bmDropShadow=NULL;
bmQuestionMark=NULL;
bmAchievement=NULL;
startTime=time(NULL);
tutorialLevels=0;
clear();
}
void StatisticsManager::clear(){
playerTravelingDistance=shadowTravelingDistance=0.0f;
playerJumps=shadowJumps
=playerDies=shadowDies
=playerSquashed=shadowSquashed
=completedLevels=silverLevels=goldLevels
=recordTimes=switchTimes=swapTimes=saveTimes=loadTimes
=playTime=levelEditTime
=createdLevels=tutorialCompleted=tutorialGold=0;
achievements.clear();
queuedAchievements.clear();
achievementTime=0;
currentAchievement=0;
if(bmAchievement){
bmAchievement.reset();
}
}
#define LOAD_STATS(var,func) { \
vector<string> &v=node.attributes[ #var ]; \
if(!v.empty() && !v[0].empty()) \
var=func(v[0].c_str()); \
}
void StatisticsManager::loadFile(const std::string& fileName){
clear();
ifstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
POASerializer serializer;
if(!serializer.readNode(file,&node,true)) return;
//load statistics
LOAD_STATS(playerTravelingDistance,atof);
LOAD_STATS(shadowTravelingDistance,atof);
LOAD_STATS(playerJumps,atoi);
LOAD_STATS(shadowJumps,atoi);
LOAD_STATS(playerDies,atoi);
LOAD_STATS(shadowDies,atoi);
LOAD_STATS(playerSquashed,atoi);
LOAD_STATS(shadowSquashed,atoi);
LOAD_STATS(recordTimes,atoi);
LOAD_STATS(switchTimes,atoi);
LOAD_STATS(swapTimes,atoi);
LOAD_STATS(saveTimes,atoi);
LOAD_STATS(loadTimes,atoi);
LOAD_STATS(playTime,atoi);
LOAD_STATS(levelEditTime,atoi);
LOAD_STATS(createdLevels,atoi);
//load achievements.
//format is: name;time,name;time,...
{
vector<string> &v=node.attributes["achievements"];
for(unsigned int i=0;i<v.size();i++){
string s=v[i];
time_t t=0;
string::size_type lps=s.find(';');
if(lps!=string::npos){
string s1=s.substr(lps+1);
s=s.substr(0,lps);
long long n;
sscanf(s1.c_str(),PRINTF_LONGLONG,&n);
t=(time_t)n;
}
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(s);
if(it!=avaliableAchievements.end()){
OwnedAchievement ach={t,it->second};
achievements[it->first]=ach;
}
}
}
}
//Call when level edit is start
void StatisticsManager::startLevelEdit(){
levelEditStartTime=time(NULL);
}
//Call when level edit is end
void StatisticsManager::endLevelEdit(){
levelEditTime+=time(NULL)-levelEditStartTime;
}
//update in-game time
void StatisticsManager::updatePlayTime(){
time_t endTime=time(NULL);
playTime+=endTime-startTime;
startTime=endTime;
}
#define SAVE_STATS(var,pattern) { \
sprintf(s,pattern,var); \
node.attributes[ #var ].push_back(s); \
}
void StatisticsManager::saveFile(const std::string& fileName){
char s[64];
//update in-game time
updatePlayTime();
ofstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
//save statistics
SAVE_STATS(playerTravelingDistance,"%.2f");
SAVE_STATS(shadowTravelingDistance,"%.2f");
SAVE_STATS(playerJumps,"%d");
SAVE_STATS(shadowJumps,"%d");
SAVE_STATS(playerDies,"%d");
SAVE_STATS(shadowDies,"%d");
SAVE_STATS(playerSquashed,"%d");
SAVE_STATS(shadowSquashed,"%d");
SAVE_STATS(recordTimes,"%d");
SAVE_STATS(switchTimes,"%d");
SAVE_STATS(swapTimes,"%d");
SAVE_STATS(saveTimes,"%d");
SAVE_STATS(loadTimes,"%d");
SAVE_STATS(playTime,"%d");
SAVE_STATS(levelEditTime,"%d");
SAVE_STATS(createdLevels,"%d");
//save achievements.
//format is: name;time,name;time,...
{
vector<string>& v=node.attributes["achievements"];
for(map<string,OwnedAchievement>::iterator it=achievements.begin();it!=achievements.end();++it){
stringstream strm;
char s[32];
long long n=it->second.achievedTime;
sprintf(s,
#ifdef WIN32
"%I64d",
#else
"%Ld",
#endif
n);
strm<<it->first<<";"<<s;
v.push_back(strm.str());
}
}
POASerializer serializer;
serializer.writeNode(&node,file,true,true);
}
void StatisticsManager::loadPicture(SDL_Renderer& renderer, ImageManager& imageManager){
//Load drop shadow picture
bmDropShadow=imageManager.loadTexture(getDataPath()+"gfx/dropshadow.png", renderer);
bmQuestionMark=imageManager.loadImage(getDataPath()+"gfx/menu/questionmark.png");
}
void StatisticsManager::registerAchievements(ImageManager& imageManager){
if(!avaliableAchievements.empty()) return;
for(int i=0;achievementList[i].id!=NULL;i++){
avaliableAchievements[achievementList[i].id]=&achievementList[i];
if(achievementList[i].imageFile!=NULL){
achievementList[i].imageSurface = imageManager.loadImage(getDataPath()+achievementList[i].imageFile);
}
}
}
void StatisticsManager::render(ImageManager&,SDL_Renderer &renderer){
if(achievementTime==0 && !bmAchievement && currentAchievement<(int)queuedAchievements.size()){
//create surface
bmAchievement=createAchievementSurface(renderer, queuedAchievements[currentAchievement++]);
//FIXME: Draw the box.
//drawGUIBox(0,0,bmAchievement->w,bmAchievement->h,bmAchievement,0xFFFFFF00);
//check if queue is empty
if(currentAchievement>=(int)queuedAchievements.size()){
queuedAchievements.clear();
currentAchievement=0;
}
//play a sound
getSoundManager()->playSound("achievement");
}
//check if we need to display achievements
if(bmAchievement){
achievementTime++;
if(achievementTime<=0){
return;
}else if(achievementTime<=5){
drawAchievement(renderer,achievementTime);
}else if(achievementTime<=achievementDisplayTime-5){
drawAchievement(renderer,5);
}else if(achievementTime<achievementDisplayTime){
drawAchievement(renderer,achievementDisplayTime-achievementTime);
}else if(achievementTime>=achievementIntervalTime){
if(bmAchievement){
bmAchievement.reset();
}
achievementTime=0;
}
}
}
void StatisticsManager::newAchievement(const std::string& id,bool save){
//check avaliable achievements
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(id);
if(it==avaliableAchievements.end()) return;
//check if already have this achievement
if(save){
map<string,OwnedAchievement>::iterator it2=achievements.find(id);
if(it2!=achievements.end()) return;
OwnedAchievement ach={time(NULL),it->second};
achievements[id]=ach;
}
//add it to queue
queuedAchievements.push_back(it->second);
}
float StatisticsManager::getAchievementProgress(AchievementInfo* info){
if(!strcmp(info->id,"experienced")){
return float(completedLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"expert")){
return float(goldLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"tutorial")){
if(tutorialLevels>0)
return float(tutorialCompleted)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"tutorialGold")){
if(tutorialLevels>0)
return float(tutorialGold)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"create50")){
return float(createdLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"frog")){
return float(playerJumps+shadowJumps)/1000.0f*100.0f;
}
if(!strcmp(info->id,"die50")){
return float(playerDies+shadowDies)/50.0f*100.0f;
}
if(!strcmp(info->id,"die1000")){
return float(playerDies+shadowDies)/1000.0f*100.0f;
}
if(!strcmp(info->id,"suqash50")){
return float(playerSquashed+shadowSquashed)/50.0f*100.0f;
}
if(!strcmp(info->id,"travel100")){
return (playerTravelingDistance+shadowTravelingDistance)/100.0f*100.0f;
}
if(!strcmp(info->id,"travel1k")){
return (playerTravelingDistance+shadowTravelingDistance)/1000.0f*100.0f;
}
if(!strcmp(info->id,"travel10k")){
return (playerTravelingDistance+shadowTravelingDistance)/10000.0f*100.0f;
}
if(!strcmp(info->id,"travel42k")){
return (playerTravelingDistance+shadowTravelingDistance)/42195.0f*100.0f;
}
if(!strcmp(info->id,"record100")){
return float(recordTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"record1k")){
return float(recordTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"switch100")){
return float(switchTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"switch1k")){
return float(switchTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"swap100")){
return float(swapTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"swap1k")){
return float(swapTimes)/1000.0f*100.0f;
}
//not found
return 0.0f;
}
SharedTexture StatisticsManager::createAchievementSurface(SDL_Renderer& renderer, AchievementInfo* info,SDL_Rect* rect,bool showTip,const time_t *achievedTime){
if(info==NULL || info->id==NULL) return NULL;
//prepare text
SurfacePtr title0(nullptr);
SurfacePtr title1(nullptr);
vector<SDL_Surface*> descSurfaces;
SDL_Color fg={0,0,0};
int fontHeight=TTF_FontLineSkip(fontText);
bool showDescription=false;
bool showImage=false;
float achievementProgress=0.0f;
if(showTip){
title0.reset(TTF_RenderUTF8_Blended(fontText,_("New achievement:"),fg));
title1.reset(TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg));
showDescription=showImage=true;
}else if(achievedTime){
char s[128];
strftime(s,sizeof(s),"%c",localtime(achievedTime));
stringstream strm;
tinyformat::format(strm,_("Achieved on %s"),s);
title0.reset(TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg));
title1.reset(TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg));
showDescription=showImage=true;
}else if(info->displayStyle==ACHIEVEMENT_HIDDEN){
title0.reset(TTF_RenderUTF8_Blended(fontGUISmall,_("Unknown achievement"),fg));
}else{
if(info->displayStyle==ACHIEVEMENT_PROGRESS){
achievementProgress=getAchievementProgress(info);
stringstream strm;
tinyformat::format(strm,_("Achieved %1.0f%%"),achievementProgress);
title1.reset(TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg));
}else{
title1.reset(TTF_RenderUTF8_Blended(fontText,_("Not achieved"),fg));
}
title0.reset(TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg));
showDescription= info->displayStyle==ACHIEVEMENT_ALL || info->displayStyle==ACHIEVEMENT_PROGRESS;
showImage=true;
}
if(info->description!=NULL && showDescription){
string description=_(info->description);
string::size_type lps=0,lpe;
for(;;){
lpe=description.find('\n',lps);
if(lpe==string::npos){
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps)+' ').c_str(),fg));
break;
}else{
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps,lpe-lps)+' ').c_str(),fg));
lps=lpe+1;
}
}
}
//calculate the size
int w=0,h=0,w1=8,h1=0;
if(title0!=NULL){
if(title0->w>w) w=title0->w;
h1+=title0->h;
}
if(title1!=NULL){
if(title1->w>w) w=title1->w;
h1+=title1->h;
/*//calc progress bar size
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
h1+=4;
}*/
}
if(showImage){
if(info->imageSurface!=NULL){
w1+=info->r.w+8;
w+=info->r.w+8;
if(info->r.h>h1) h1=info->r.h;
}
}else{
w1+=bmQuestionMark->w+8;
w+=bmQuestionMark->w+8;
if(bmQuestionMark->h>h1) h1=bmQuestionMark->h;
}
h=h1+8;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
if(descSurfaces[i]->w>w) w=descSurfaces[i]->w;
}
}
h+=descSurfaces.size()*fontHeight;
w+=16;
h+=16;
//check if size is specified
int left=0,top=0;
if(rect!=NULL){
//NOTE: SDL2 port. This was never used.
/* if(surface!=NULL){
left=rect->x;
top=rect->y;
}*/
if(rect->w>0) w=rect->w;
else rect->w=w;
rect->h=h;
}
//create surface if necessary
SurfacePtr surface = createSurface(w, h);
std::unique_ptr<SDL_Renderer,decltype(&SDL_DestroyRenderer)> surfaceRenderer(
SDL_CreateSoftwareRenderer(surface.get()), &SDL_DestroyRenderer);
//draw background
const SDL_Rect r={left,top,w,h};
if(showTip || achievedTime){
SDL_FillRect(surface.get(),&r,SDL_MapRGB(surface->format,255,255,255));
}else{
SDL_FillRect(surface.get(),&r,SDL_MapRGB(surface->format,192,192,192));
}
//draw horizontal separator
//FIXME: this is moved from StatisticsScreen::createGUI
if (!showTip) {
const SDL_Rect r0 = { left, top, w, 1 };
const SDL_Rect r1 = { left, top + h - 2, w, 1 };
const SDL_Rect r2 = { left, top + h - 1, w, 1 };
Uint32 c0 = achievedTime ? SDL_MapRGB(surface->format, 224, 224, 224) : SDL_MapRGB(surface->format, 168, 168, 168);
Uint32 c2 = achievedTime ? SDL_MapRGB(surface->format, 128, 128, 128) : SDL_MapRGB(surface->format, 96, 96, 96);
SDL_FillRect(surface.get(), &r0, c0);
SDL_FillRect(surface.get(), &r1, c0);
SDL_FillRect(surface.get(), &r2, c2);
}
//draw picture
if(showImage){
if(info->imageSurface){
SDL_Rect r={left+8,top+8+(h1-info->r.h)/2,0,0};
SDL_BlitSurface(info->imageSurface,&info->r,surface.get(),&r);
}
}else{
SDL_Rect r={left+8,top+8+(h1-bmQuestionMark->h)/2,0,0};
SDL_BlitSurface(bmQuestionMark,NULL,surface.get(),&r);
}
//draw text
h=8;
if(title0){
SDL_Rect r={left+w1,top+h,0,0};
SDL_BlitSurface(title0.get(),NULL,surface.get(),&r);
h+=title0->h;
}
if(title1){
SDL_Rect r={left+w1,top+h,0,0};
//Draw progress bar.
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
//Draw borders.
SDL_Rect r1={r.x,r.y,w-8-r.x,title1->h};
drawGUIBox(r1.x,r1.y,r1.w,r1.h,*surfaceRenderer,0x1D);
//Draw progress.
r1.x++;
r1.y++;
r1.w=int(achievementProgress/100.0f*float(r1.w-2)+0.5f);
r1.h-=2;
SDL_SetRenderDrawColor(surfaceRenderer.get(),0,0,0,100);
SDL_RenderFillRect(surfaceRenderer.get(),&r1);
//shift the text a little bit (???)
r.x+=2;
r.y+=2;
}
//Draw text.
SDL_BlitSurface(title1.get(),NULL,surface.get(),&r);
}
h=h1+16;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_Rect r={left+8,top+h+static_cast<int>(i)*fontHeight,0,0};
SDL_BlitSurface(descSurfaces[i],NULL,surface.get(),&r);
}
}
//clean up
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_FreeSurface(descSurfaces[i]);
}
}
//FIXME: Should we clear the vector here?
//over
return textureFromSurface(renderer, std::move(surface));
}
void StatisticsManager::drawAchievement(SDL_Renderer& renderer,int alpha){
if(!bmAchievement || alpha<=0) {
return;
}
if(alpha>5) alpha=5;
SDL_Rect r = rectFromTexture(*bmAchievement);
int w=0,h=0;
SDL_GetRendererOutputSize(&renderer, &w, &h);
r.x = w-32-r.w;
r.y = 32;
SDL_SetTextureAlphaMod(bmAchievement.get(), alpha*40);
applyTexture(r.x, r.y,bmAchievement, renderer);
if(!bmDropShadow) {
return;
}
//draw drop shadow - corner
{
int w1=r.w/2,w2=r.w-w1,h1=r.h/2,h2=r.h-h1;
if(w1>16) w1=16;
if(w2>16) w2=16;
if(h1>16) h1=16;
if(h2>16) h2=16;
const int x=(5-alpha)*64;
//top-left
SDL_Rect r1={x,0,w1+16,h1+16};//),r2={r.x-16,r.y-16,0,0};
SDL_Rect r2 ={r.x-16, r.y-16, r1.w, r1.h};
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//top-right
r1.x=x+48-w2;r2.w=r1.w =w2+16;r2.x=r.x+r.w-w2;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom-right
r1.y=48-h2;r2.h=r1.h=h2+16;r2.y=r.y+r.h-h2;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom-left
r1.x=x;r2.w=r1.w=w1+16;r2.x=r.x-16;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
}
//draw drop shadow - border
int i=r.w-32;
while(i>0){
const int ii=i>128?128:i;
//top
SDL_Rect r1={0,256-alpha*16,ii,16};
SDL_Rect r2={r.x+r.w-16-i,r.y-16,r1.w,r1.h};
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom
r1.x=128;r2.y=r.y+r.h;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
i-=ii;
}
i=r.h-32;
while(i>0){
const int ii=i>128?128:i;
//top
SDL_Rect r1={512-alpha*16,0,16,ii};
SDL_Rect r2={r.x-16,r.y+r.h-16-i, r1.w, r1.h};
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom
r1.y=128;r2.x=r.x+r.w;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
i-=ii;
}
}
void StatisticsManager::reloadCompletedLevelsAndAchievements(){
completedLevels=silverLevels=goldLevels=0;
LevelPackManager *lpm=getLevelPackManager();
vector<pair<string,string> > v=lpm->enumLevelPacks();
bool tutorial=false,tutorialIsGold=false;
for(unsigned int i=0;i<v.size();i++){
string& s=v[i].first;
LevelPack *levels=lpm->getLevelPack(s);
levels->loadProgress();
bool b=false;
if(s==lpm->tutorialLevelPackPath){
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
b=tutorial=tutorialIsGold=true;
}
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
completedLevels++;
if(b) tutorialCompleted++;
if(medal==2) silverLevels++;
if(medal==3){
goldLevels++;
if(b) tutorialGold++;
}
if(medal!=3 && b) tutorialIsGold=false;
}else if(b){
tutorial=tutorialIsGold=false;
}
}
}
//upadte achievements
updateLevelAchievements();
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
void StatisticsManager::reloadOtherAchievements(){
int i;
if(playTime>=7200) newAchievement("addicted");
if(playTime>=86400) newAchievement("loyalFan");
if(levelEditTime>=7200) newAchievement("constructor");
if(levelEditTime>=86400) newAchievement("constructor2");
if(createdLevels>=1) newAchievement("create1");
if(createdLevels>=50) newAchievement("create50");
i=playerJumps+shadowJumps;
if(i>=1000) newAchievement("frog");
i=playerDies+shadowDies;
if(i>=1) newAchievement("die1");
if(i>=50) newAchievement("die50");
if(i>=1000) newAchievement("die1000");
i=playerSquashed+shadowSquashed;
if(i>=1) newAchievement("squash1");
if(i>=50) newAchievement("squash50");
float d=playerTravelingDistance+shadowTravelingDistance;
if(d>=100.0f) newAchievement("travel100");
if(d>=1000.0f) newAchievement("travel1k");
if(d>=10000.0f) newAchievement("travel10k");
if(d>=42195.0f) newAchievement("travel42k");
if(recordTimes>=100) newAchievement("record100");
if(recordTimes>=1000) newAchievement("record1k");
if(switchTimes>=100) newAchievement("switch100");
if(switchTimes>=1000) newAchievement("switch1k");
if(swapTimes>=100) newAchievement("swap100");
if(swapTimes>=1000) newAchievement("swap1k");
if(saveTimes>=1000) newAchievement("save1k");
if(loadTimes>=1000) newAchievement("load1k");
if(version.find("Development")!=string::npos) newAchievement("programmer");
}
//Update level specified achievements.
//Make sure the completed level count is correct.
void StatisticsManager::updateLevelAchievements(){
if(completedLevels>=1) newAchievement("newbie");
if(goldLevels>=1) newAchievement("goodjob");
if(completedLevels>=50) newAchievement("experienced");
if(goldLevels>=50) newAchievement("expert");
}
//Update tutorial specified achievements.
//Make sure the level progress of tutorial is correct.
void StatisticsManager::updateTutorialAchievements(){
//find tutorial level pack
LevelPackManager *lpm=getLevelPackManager();
LevelPack *levels=lpm->getTutorialLevelPack();
if(levels==NULL) return;
bool tutorial=true,tutorialIsGold=true;
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
tutorialCompleted++;
if(medal!=3) tutorialIsGold=false;
else tutorialGold++;
}else{
tutorial=tutorialIsGold=false;
break;
}
}
//upadte achievements
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
//internal function
//flags: a bit-field value indicates which achievements we have.
void StatisticsManager::updateTutorialAchievementsInternal(int flags){
if(flags&1) newAchievement("tutorial");
if(flags&2) newAchievement("tutorialGold");
}
diff --git a/src/StatisticsManager.h b/src/StatisticsManager.h
index f7974e8..9efd7b8 100644
--- a/src/StatisticsManager.h
+++ b/src/StatisticsManager.h
@@ -1,184 +1,158 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STATISTICSMANAGER_H
#define STATISTICSMANAGER_H
#include <SDL.h>
#include <string>
#include <vector>
#include <map>
#include <time.h>
#include "Render.h"
-
-enum AchievementDisplayStyle{
- ACHIEVEMENT_HIDDEN,
- ACHIEVEMENT_TITLE,
- ACHIEVEMENT_ALL,
- ACHIEVEMENT_PROGRESS,
-};
-
-//internal struct for achievement info
-struct AchievementInfo{
- //achievement id for save to statistics file
- const char* id;
- //achievement name for display
- const char* name;
- //achievement image. NULL for no image. will be loaded at getDataPath()+imageFile
- const char* imageFile;
- //image offset and size.
- SDL_Rect r;
- //achievement description. supports multi-line text
- const char* description;
- //display style
- AchievementDisplayStyle displayStyle;
-
- //SDL_Surface of achievement image.
- SDL_Surface* imageSurface;
-};
+#include "AchievementList.h"
struct OwnedAchievement{
time_t achievedTime;
AchievementInfo* info;
};
class StatisticsScreen;
class StatisticsManager{
friend class StatisticsScreen;
public:
//Player and shadow traveling distance (m), 1 block = 1 meter
float playerTravelingDistance,shadowTravelingDistance;
//Player and shadow jumps
int playerJumps,shadowJumps;
//Player and shadow dies
int playerDies,shadowDies;
//Player and shadow squashed
int playerSquashed,shadowSquashed;
//Completed levels. NOTE: this is dynamically calculated, and doesn't save to file.
int completedLevels,silverLevels,goldLevels;
//Record times
int recordTimes;
//number of switched pulled
int switchTimes;
//swap times
int swapTimes;
//save and load times
int saveTimes,loadTimes;
//play time (s)
int playTime;
//level edit time (s)
int levelEditTime;
//created levels
int createdLevels;
private:
//current achievement displayed time
int achievementTime;
//some picture
SharedTexture bmDropShadow;
SDL_Surface* bmQuestionMark;
//SDL_Surface for current achievement (excluding drop shadow)
//SDL_Surface *bmAchievement;
SharedTexture bmAchievement;
//currently owned achievements
std::map<std::string,OwnedAchievement> achievements;
//queued achievements for display
std::vector<AchievementInfo*> queuedAchievements;
//currently displayed achievement
int currentAchievement;
//starting time
time_t startTime;
//level edit starting time
time_t levelEditStartTime;
//statistics for tutorial level pack
int tutorialLevels,tutorialCompleted,tutorialGold;
public:
StatisticsManager();
//clear the statistics and achievements.
void clear();
//load needed picture
void loadPicture(SDL_Renderer &renderer, ImageManager &imageManager);
//register avaliable achievements
static void registerAchievements(ImageManager& imageManager);
//load statistics file.
void loadFile(const std::string& fileName);
//save statistics file.
void saveFile(const std::string& fileName);
//add or display a new achievement.
//name: the achievement id. if can't find it in avaliable achievement, nothing happens.
//save: if true then save to currently owned achievements. if it already exists in
//currently owned achievements, nothing happens.
//if false then just added it to queue, including duplicated achievements.
void newAchievement(const std::string& id,bool save=true);
//if there are new achievements, draw it on the screen,
//otherwise do nothing.
void render(ImageManager&,SDL_Renderer& renderer);
//Call this function to update completed levels.
//NOTE: Level progress files are reloaded, so it's slow.
void reloadCompletedLevelsAndAchievements();
//Call this function to update other achievements at game startup.
void reloadOtherAchievements();
//Update level specified achievements.
//Make sure the completed level count is correct.
void updateLevelAchievements();
//Update tutorial specified achievements.
//Make sure the level progress of tutorial is correct.
void updateTutorialAchievements();
//Call when level edit is start
void startLevelEdit();
//Call when level edit is end
void endLevelEdit();
//update in-game time
void updatePlayTime();
//create a SharedTexture contains specified achievements or draw to existing surface.
//renderer: renderer to create the texture on.
//info: achievement info.
//(surface: specifies SDL_Surface to draw on. if NULL then new surface will be created.)
//NOTE: Removed this arg for sdl2 port as it was not used anyway.
//rect [in, out, optional]: specifies position and optionally width to draw on. height will be returned.
// if NULL then will be drawn on top-left corner. if surface is NULL then rect->x and rect->y are ignored.
//showTip: shows "New achievement" tip
//achievedTime: if we should show achieved time (and progress bar if AchievementInfo specifies) and when is it.
// NOTE: if showTip=true then this argument does nothing.
//return value: A texture that contains the specified achievements or NULL if any error occured.
SharedTexture createAchievementSurface(SDL_Renderer& renderer, AchievementInfo* info,SDL_Rect* rect=NULL,bool showTip=true,const time_t *achievedTime=NULL);
private:
//internal function
//flags: a bit-field value indicates which achievements we have.
void updateTutorialAchievementsInternal(int flags);
//internal function. alpha should be 1-5, 5 means fully opaque (not really)
void drawAchievement(SDL_Renderer& renderer, int alpha);
//internal function for get progress (in percent, 0-100)
float getAchievementProgress(AchievementInfo* info);
};
extern StatisticsManager statsMgr;
-extern AchievementInfo achievementList[];
#endif
diff --git a/tools/messages.pot.sh b/tools/messages.pot.sh
index 0bbffb9..246d189 100644
--- a/tools/messages.pot.sh
+++ b/tools/messages.pot.sh
@@ -1,43 +1,42 @@
#!/bin/bash
# Copyright (C) 2012 Me and My Shadow
#
# This file is part of Me and My Shadow.
#
# Me and My Shadow is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Me and My Shadow is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
#variables
output="../data/locale/messages.pot"
version="0.5svn"
#automatically get all string from source code
-# NOTE: One exception is the achievementlist.
-xgettext -o ${output} -c" /" -k_ -k__ --package-name=meandmyshadow --package-version=${version} ../src/*.cpp ../src/AchievementList.h
+xgettext -o ${output} -c" /" -k_ -k__ --package-name=meandmyshadow --package-version=${version} ../src/*.cpp
#little hack to get tranlator comments work
sed -i 's/#. \/ /# /g' ${output}
echo >> ${output}
#make SDL key names translatable
keys=("backspace" "tab" "clear" "return" "pause" "escape" "space" "delete" "enter" "equals"
"up" "down" "right" "left" "insert" "home" "end" "page up" "page down" "numlock" "caps lock"
"scroll lock" "right shift" "left shift" "right ctrl" "left ctrl" "right alt" "left alt"
"right meta" "left meta" "left super" "right super" "alt gr" "compose" "help" "print screen"
"sys req" "break" "menu" "power" "euro" "undo")
for i in ${!keys[*]}; do
echo "# TRANSLATORS: name of a key" >> ${output}
echo "msgid \"${keys[$i]}\"" >> ${output}
echo "msgstr \"\"" >> ${output}
echo >> ${output}
done

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jun 15, 11:40 PM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70219
Default Alt Text
(48 KB)

Event Timeline