Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
132 KB
Referenced Files
None
Subscribers
None
diff --git a/src/Addons.cpp b/src/Addons.cpp
index f1c7004..d449be6 100644
--- a/src/Addons.cpp
+++ b/src/Addons.cpp
@@ -1,570 +1,570 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "Addons.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "GUIObject.h"
#include "GUIScrollBar.h"
#include "GUIListBox.h"
#include "POASerializer.h"
#include "InputManager.h"
#include <string>
#include <sstream>
#include <iostream>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL.h>
using namespace std;
Addons::Addons(){
//Load the backgroundimage and the addons file.
background=loadImage(getDataPath()+"gfx/menu/background.png");
//Render the title.
SDL_Color black={0,0,0};
title=TTF_RenderText_Blended(fontTitle,"Addons",black);
- FILE* addon=fopen((getUserPath()+"addons").c_str(),"wb");
+ FILE* addon=fopen((getUserPath(USER_CACHE)+"addons").c_str(),"wb");
action=NONE;
addons=NULL;
//Create the gui.
GUIObject* obj;
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Try to get(download) the addonsList.
if(getAddonsList(addon)==false) {
//It failed so we show the error message.
GUIObjectRoot=new GUIObject(0,0,800,600);
obj=new GUIObject(90,96,200,32,GUIObjectLabel,"Unable to initialze addon menu:");
obj->name="lbl";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(120,130,200,32,GUIObjectLabel,error.c_str());
obj->name="lbl";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(90,550,200,32,GUIObjectButton,"Back");
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
return;
}
//Downloaded the addons file now we can create the GUI.
GUIObjectRoot=new GUIObject(0,0,800,600);
obj=new GUIObject(90,96,200,32,GUIObjectButton,"Levels");
obj->name="cmdLvls";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(300,96,200,32,GUIObjectButton,"Level Packs");
obj->name="cmdLvlpacks";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(510,96,200,32,GUIObjectButton,"Themes");
obj->name="cmdThemes";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Create the list for the addons.
//By default levels will be selected.
list=new GUIListBox(90,140,620,400);
list->item=addonsToList("levels");
list->name="lstAddons";
list->eventCallback=this;
GUIObjectRoot->childControls.push_back(list);
type="levels";
//And the buttons at the bottom of the screen.
obj=new GUIObject(90,550,200,32,GUIObjectButton,"Back");
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
actionButton=new GUIObject(510,550,200,32,GUIObjectButton,"Install");
actionButton->name="cmdInstall";
actionButton->eventCallback=this;
GUIObjectRoot->childControls.push_back(actionButton);
updateButton=new GUIObject(300,550,200,32,GUIObjectButton,"Update");
updateButton->name="cmdUpdate";
updateButton->enabled=false;
updateButton->visible=false;
updateButton->eventCallback=this;
GUIObjectRoot->childControls.push_back(updateButton);
}
Addons::~Addons(){
delete addons;
//Free the title surface.
SDL_FreeSurface(title);
//If the GUIObjectRoot exist delete it.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
bool Addons::getAddonsList(FILE* file){
//First we download the file.
if(downloadFile("http://meandmyshadow.sourceforge.net/game/addons02",file)==false){
error="ERROR: unable to download addons file!";
cerr<<error<<endl;
return false;
}
fclose(file);
//Load the downloaded file.
ifstream addonFile;
- addonFile.open((getUserPath()+"addons").c_str());
+ addonFile.open((getUserPath(USER_CACHE)+"addons").c_str());
if(addonFile==false) {
error="ERROR: unable to load addon_list file!";
cerr<<error<<endl;
return false;
}
//Parse the addonsfile.
TreeStorageNode obj;
{
POASerializer objSerializer;
if(!objSerializer.readNode(addonFile,&obj,true)){
error="ERROR: Invalid file format of addons file!";
cerr<<error<<endl;
return false;
}
}
//Also load the installed_addons file.
ifstream iaddonFile;
- iaddonFile.open((getUserPath()+"installed_addons").c_str());
+ iaddonFile.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddonFile) {
//The installed_addons file doesn't exist, so we create it.
ofstream iaddons;
- iaddons.open((getUserPath()+"installed_addons").c_str());
+ iaddons.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
iaddons<<" "<<endl;
iaddons.close();
//Also load the installed_addons file.
- iaddonFile.open((getUserPath()+"installed_addons").c_str());
+ iaddonFile.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddonFile) {
error="ERROR: Unable to create the installed_addons file.";
cerr<<error<<endl;
return false;
}
}
//And parse the installed_addons file.
TreeStorageNode obj1;
{
POASerializer objSerializer;
if(!objSerializer.readNode(iaddonFile,&obj1,true)){
error="ERROR: Invalid file format of the installed_addons!";
cerr<<error<<endl;
return false;
}
}
//Fill the vector.
addons = new std::vector<Addon>;
fillAddonList(*addons,obj,obj1);
//Close the files.
iaddonFile.close();
addonFile.close();
return true;
}
void Addons::fillAddonList(std::vector<Addons::Addon> &list, TreeStorageNode &addons, TreeStorageNode &installed_addons){
//Loop through the blocks of the addons file.
//These should contain the types levels, levelpacks, themes.
for(unsigned int i=0;i<addons.subNodes.size();i++){
TreeStorageNode* block=addons.subNodes[i];
if(block==NULL) continue;
string type;
type=block->name;
//Now loop the entries(subNodes) of the block.
for(unsigned int i=0;i<block->subNodes.size();i++){
TreeStorageNode* entry=block->subNodes[i];
if(entry==NULL) continue;
if(entry->name=="entry" && entry->value.size()==1){
//The entry is valid so create a new Addon.
Addon addon = *(new Addon);
addon.type=type;
addon.name=entry->value[0];
addon.file=entry->attributes["file"][0];
if(!entry->attributes["folder"].empty()){
addon.folder=entry->attributes["folder"][0];
}
addon.author=entry->attributes["author"][0];
addon.version=atoi(entry->attributes["version"][0].c_str());
addon.upToDate=false;
addon.installed=false;
//Check if the addon is already installed.
for(unsigned int i=0;i<installed_addons.subNodes.size();i++){
TreeStorageNode* installed=installed_addons.subNodes[i];
if(installed==NULL) continue;
if(installed->name=="entry" && installed->value.size()==3){
if(addon.type.compare(installed->value[0])==0 && addon.name.compare(installed->value[1])==0) {
addon.installed=true;
addon.installedVersion=atoi(installed->value[2].c_str());
if(addon.installedVersion>=addon.version) {
addon.upToDate=true;
}
}
}
}
//Finally put him in the list.
list.push_back(addon);
}
}
}
}
std::vector<std::string> Addons::addonsToList(const std::string &type){
std::vector<std::string> result;
for(unsigned int i=0;i<addons->size();i++) {
//Check if the addon is from the right type.
if((*addons)[i].type==type) {
string entry = (*addons)[i].name + " by " + (*addons)[i].author;
if((*addons)[i].installed) {
if((*addons)[i].upToDate) {
entry += " *";
} else {
entry += " +";
}
}
result.push_back(entry);
}
}
return result;
}
bool Addons::saveInstalledAddons(){
if(!addons) return false;
//Open the file.
ofstream iaddons;
- iaddons.open((getUserPath()+"installed_addons").c_str());
+ iaddons.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddons) return false;
//Loop all the levels.
TreeStorageNode installed;
for(unsigned int i=0;i<addons->size();i++) {
//Check if the level is installed or not.
if((*addons)[i].installed) {
TreeStorageNode *entry=new TreeStorageNode;
entry->name="entry";
entry->value.push_back((*addons)[i].type);
entry->value.push_back((*addons)[i].name);
char version[64];
sprintf(version,"%d",(*addons)[i].installedVersion);
entry->value.push_back(version);
installed.subNodes.push_back(entry);
}
}
//And write away the file.
POASerializer objSerializer;
objSerializer.writeNode(&installed,iaddons,true,true);
return true;
}
void Addons::handleEvents(){
//Check if we should quit.
if(event.type==SDL_QUIT){
//Save the installed addons before exiting.
saveInstalledAddons();
setNextState(STATE_EXIT);
}
//Check if escape is pressed, if so return to the levelselect screen.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_LEVEL_SELECT);
}
}
void Addons::logic(){}
void Addons::render(){
//We only need to draw the background.
applySurface(0,0,background,screen,NULL);
//Draw the title.
applySurface((800-title->w)/2,16,title,screen,NULL);
}
void Addons::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
if(name=="cmdLvlpacks"){
list->item=addonsToList("levelpacks");
list->value=0;
type="levelpacks";
GUIEventCallback_OnEvent("lstAddons",list,GUIEventChange);
}else if(name=="cmdLvls"){
list->item=addonsToList("levels");
list->value=0;
type="levels";
GUIEventCallback_OnEvent("lstAddons",list,GUIEventChange);
}else if(name=="cmdThemes"){
list->item=addonsToList("themes");
list->value=0;
type="themes";
GUIEventCallback_OnEvent("lstAddons",list,GUIEventChange);
}else if(name=="lstAddons"){
//Get the addon struct that belongs to it.
Addon *addon=NULL;
if(list->item.size()>0) {
string entry = list->item[list->value];
if(type.compare("levels")==0) {
for(unsigned int i=0;i<addons->size();i++) {
std::string prefix=(*addons)[i].name;
if(!entry.compare(0, prefix.size(), prefix)) {
addon=&(*addons)[i];
}
}
} else if(type.compare("levelpacks")==0) {
for(unsigned int i=0;i<addons->size();i++) {
std::string prefix=(*addons)[i].name;
if(!entry.compare(0, prefix.size(), prefix)) {
addon=&(*addons)[i];
}
}
} else if(type.compare("themes")==0) {
for(unsigned int i=0;i<addons->size();i++) {
std::string prefix=(*addons)[i].name;
if(!entry.compare(0, prefix.size(), prefix)) {
addon=&(*addons)[i];
}
}
}
}
selected=addon;
updateActionButton();
updateUpdateButton();
}else if(name=="cmdBack"){
saveInstalledAddons();
setNextState(STATE_MENU);
}else if(name=="cmdUpdate"){
//First remove the addon and then install it again.
if(type.compare("levels")==0) {
- if(downloadFile(selected->file,(getUserPath()+"/levels/"))!=false){
+ if(downloadFile(selected->file,(getUserPath(USER_DATA)+"/levels/"))!=false){
selected->upToDate=true;
selected->installedVersion=selected->version;
list->item=addonsToList("levels");
updateActionButton();
updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox("ERROR: Unable to download addon!",MsgBoxOKOnly,"ERROR:");
return;
}
}else if(type.compare("levelpacks")==0) {
- if(!removeDirectory((getUserPath()+"levelpacks/"+selected->folder+"/").c_str())){
- cerr<<"ERROR: Unable to remove the directory "<<(getUserPath()+"levelpacks/"+selected->folder+"/")<<"."<<endl;
+ if(!removeDirectory((getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/").c_str())){
+ cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/")<<"."<<endl;
return;
}
- if(downloadFile(selected->file,(getUserPath()+"/tmp/"))!=false){
- extractFile(getUserPath()+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath()+"/levelpacks/"+selected->folder+"/");
+ if(downloadFile(selected->file,(getUserPath(USER_CACHE)+"/tmp/"))!=false){
+ extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/levelpacks/"+selected->folder+"/");
selected->upToDate=true;
selected->installedVersion=selected->version;
list->item=addonsToList("levelpacks");
updateActionButton();
updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox("ERROR: Unable to download addon!",MsgBoxOKOnly,"ERROR:");
return;
}
}else if(type.compare("themes")==0) {
- if(!removeDirectory((getUserPath()+"themes/"+selected->folder+"/").c_str())){
- cerr<<"ERROR: Unable to remove the directory "<<(getUserPath()+"themes/"+selected->folder+"/")<<"."<<endl;
+ if(!removeDirectory((getUserPath(USER_DATA)+"themes/"+selected->folder+"/").c_str())){
+ cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"themes/"+selected->folder+"/")<<"."<<endl;
return;
}
- if(downloadFile(selected->file,(getUserPath()+"/tmp/"))!=false){
- extractFile((getUserPath()+"/tmp/"+fileNameFromPath(selected->file,true)),(getUserPath()+"/themes/"+selected->folder+"/"));
+ if(downloadFile(selected->file,(getUserPath(USER_CACHE)+"/tmp/"))!=false){
+ extractFile((getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true)),(getUserPath(USER_DATA)+"/themes/"+selected->folder+"/"));
selected->upToDate=true;
selected->installedVersion=selected->version;
list->item=addonsToList("themes");
updateActionButton();
updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox("ERROR: Unable to download addon!",MsgBoxOKOnly,"ERROR:");
return;
}
}
}else if(name=="cmdInstall"){
switch(action) {
case NONE:
break;
case INSTALL:
//Download the addon.
if(type.compare("levels")==0) {
- if(downloadFile(selected->file,getUserPath()+"/levels/")!=false){
+ if(downloadFile(selected->file,getUserPath(USER_DATA)+"/levels/")!=false){
selected->upToDate=true;
selected->installed=true;
selected->installedVersion=selected->version;
list->item=addonsToList("levels");
updateActionButton();
updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox("ERROR: Unable to download addon!",MsgBoxOKOnly,"ERROR:");
return;
}
}else if(type.compare("levelpacks")==0) {
- if(downloadFile(selected->file,getUserPath()+"/tmp/")!=false){
- extractFile(getUserPath()+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath()+"/levelpacks/"+selected->folder+"/");
+ if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
+ extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/levelpacks/"+selected->folder+"/");
selected->upToDate=true;
selected->installed=true;
selected->installedVersion=selected->version;
list->item=addonsToList("levelpacks");
updateActionButton();
updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox("ERROR: Unable to download addon!",MsgBoxOKOnly,"ERROR:");
return;
}
}else if(type.compare("themes")==0) {
- if(downloadFile(selected->file,getUserPath()+"/tmp/")!=false){
- extractFile(getUserPath()+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath()+"/themes/"+selected->folder+"/");
+ if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
+ extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/themes/"+selected->folder+"/");
selected->upToDate=true;
selected->installed=true;
selected->installedVersion=selected->version;
list->item=addonsToList("themes");
updateActionButton();
updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox("ERROR: Unable to download addon!",MsgBoxOKOnly,"ERROR:");
return;
}
}
break;
case UNINSTALL:
//Uninstall the addon.
if(type.compare("levels")==0) {
- if(remove((getUserPath()+"levels/"+fileNameFromPath(selected->file)).c_str())){
- cerr<<"ERROR: Unable to remove the file "<<(getUserPath() + "levels/" + fileNameFromPath(selected->file))<<"."<<endl;
+ if(remove((getUserPath(USER_DATA)+"levels/"+fileNameFromPath(selected->file)).c_str())){
+ cerr<<"ERROR: Unable to remove the file "<<(getUserPath(USER_DATA) + "levels/" + fileNameFromPath(selected->file))<<"."<<endl;
return;
}
selected->upToDate=false;
selected->installed=false;
list->item=addonsToList("levels");
updateActionButton();
updateUpdateButton();
}else if(type.compare("levelpacks")==0) {
- if(!removeDirectory((getUserPath()+"levelpacks/"+selected->folder+"/").c_str())){
- cerr<<"ERROR: Unable to remove the directory "<<(getUserPath()+"levelpacks/"+selected->folder+"/")<<"."<<endl;
+ if(!removeDirectory((getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/").c_str())){
+ cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/")<<"."<<endl;
return;
}
selected->upToDate=false;
selected->installed=false;
list->item=addonsToList("levelpacks");
updateActionButton();
updateUpdateButton();
}else if(type.compare("themes")==0) {
- if(!removeDirectory((getUserPath()+"themes/"+selected->folder+"/").c_str())){
- cerr<<"ERROR: Unable to remove the directory "<<(getUserPath()+"themes/"+selected->folder+"/")<<"."<<endl;
+ if(!removeDirectory((getUserPath(USER_DATA)+"themes/"+selected->folder+"/").c_str())){
+ cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"themes/"+selected->folder+"/")<<"."<<endl;
return;
}
selected->upToDate=false;
selected->installed=false;
list->item=addonsToList("themes");
updateActionButton();
updateUpdateButton();
}
break;
}
}
}
void Addons::updateUpdateButton(){
//some sanity check
if(selected==NULL){
updateButton->enabled=false;
return;
}
//Check if the selected addon is installed.
if(selected->installed){
//It is installed, but is it uptodate?
if(selected->upToDate){
//The addon is installed and there is no need to show the button.
updateButton->enabled=false;
updateButton->visible=false;
}else{
//Otherwise show the button
updateButton->enabled=true;
updateButton->visible=true;
}
}else{
//The addon isn't installed so we can only install it.
updateButton->enabled=false;
}
}
void Addons::updateActionButton(){
//some sanity check
if(selected==NULL){
actionButton->enabled=false;
action = NONE;
return;
}
//Check if the selected addon is installed.
if(selected->installed){
//It is installed, but is it uptodate?
actionButton->enabled=true;
actionButton->caption="Uninstall";
action = UNINSTALL;
}else{
//The addon isn't installed so we can only install it.
actionButton->enabled=true;
actionButton->caption="Install";
action = INSTALL;
}
}
diff --git a/src/FileManager.cpp b/src/FileManager.cpp
index 18947de..1df8e91 100644
--- a/src/FileManager.cpp
+++ b/src/FileManager.cpp
@@ -1,669 +1,719 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
#include "Globals.h"
#include "FileManager.h"
#include "Functions.h"
#include <archive.h>
#include <archive_entry.h>
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <direct.h>
#pragma comment(lib,"shlwapi.lib")
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
-
+//Under Windows there's just one userpath.
+#ifdef WIN32
string userPath,dataPath,appPath,exeName;
+#else
+//But on other platforms we make a difference between the userPath (config files) and the userDataPath (data files).
+//Finally there's the path for cache data userCachePath.
+string userPath,userDataPath,userCachePath,dataPath,appPath,exeName;
+#endif
bool configurePaths() {
//Get the appPath and the exeName.
{
char s[4096];
int i,m;
#ifdef WIN32
m=GetModuleFileNameA(NULL,s,sizeof(s));
#else
m=readlink("/proc/self/exe",s,sizeof(s));
#endif
s[m]=0;
for(i=m-1;i>=0;i--){
if(s[i]=='/'||s[i]=='\\'){
s[i]=0;
break;
}
}
appPath=s;
exeName=s+i+1;
}
//TODO: Check if the userpath is empty before setting userPath???
//Check if the userPath is empty.
if(getUserPath().empty()){
#ifdef WIN32
//Get the userPath.
char s[1024];
SHGetSpecialFolderPathA(NULL,s,CSIDL_PERSONAL,1);
userPath=s;
userPath+="\\My Games\\meandmyshadow\\";
#else
- //Get the userPath.
- userPath=getenv("HOME");
- userPath+="/.meandmyshadow/";
+ //Temp variable that is used to prevent NULL assignement.
+ char* env;
+
+ //First get the $XDG_CONFIG_HOME env var.
+ env=getenv("XDG_CONFIG_HOME");
+ //If it's null set userPath to $HOME/.config/.
+ if(env!=NULL){
+ userPath=env;
+ }else{
+ userPath=getenv("HOME");
+ userPath+="/.config";
+ }
+ //And add meandmyshadow to it.
+ userPath+="/meandmyshadow/";
+
+ //Now get the $XDG_DATA_HOME env var.
+ env=getenv("XDG_DATA_HOME");
+ //If it's null set userDataPath to $HOME/.local/share.
+ if(env!=NULL){
+ userDataPath=env;
+ }else{
+ userDataPath=getenv("HOME");
+ userDataPath+="/.local/share";
+ }
+ //And add meandmyshadow to it.
+ userDataPath+="/meandmyshadow/";
+
+ //Now get the $XDG_CACHE_HOME env var.
+ env=getenv("XDG_CACHE_HOME");
+ //If it's null set userCachePath to $HOME/.cache.
+ if(env!=NULL){
+ userCachePath=env;
+ }else{
+ userCachePath=getenv("HOME");
+ userCachePath+="/.cache";
+ }
+ //And add meandmyshadow to it.
+ userCachePath+="/meandmyshadow/";
+
+ //Set env null.
+ env=NULL;
#endif
//Print the userPath.
cout<<"User preferences will be fetched from: "<<userPath<<endl;
+#ifndef WIN32
+ //In case of a non-Windows computer show the user data path.
+ cout<<"User data will be fetched from: "<<userDataPath<<endl;
+#endif
}
#ifdef WIN32
//Create the userPath folder and other subfolders.
createDirectory(userPath.c_str());
createDirectory((userPath+"levels").c_str());
createDirectory((userPath+"levelpacks").c_str());
createDirectory((userPath+"themes").c_str());
createDirectory((userPath+"progress").c_str());
createDirectory((userPath+"tmp").c_str());
//The records folder for recordings.
createDirectory((userPath+"records").c_str());
createDirectory((userPath+"records\\autosave").c_str());
//And the custom folder inside the userpath.
createDirectory((userPath+"custom").c_str());
createDirectory((userPath+"custom\\levels").c_str());
createDirectory((userPath+"custom\\levelpacks").c_str());
#else
//Create the userPath.
createDirectory(userPath.c_str());
+ createDirectory(userDataPath.c_str());
+ createDirectory(userCachePath.c_str());
//Also create other folders in the userpath.
- createDirectory((userPath+"/levels").c_str());
- createDirectory((userPath+"/levelpacks").c_str());
- createDirectory((userPath+"/themes").c_str());
- createDirectory((userPath+"/progress").c_str());
- createDirectory((userPath+"/tmp").c_str());
+ createDirectory((userDataPath+"/levels").c_str());
+ createDirectory((userDataPath+"/levelpacks").c_str());
+ createDirectory((userDataPath+"/themes").c_str());
+ createDirectory((userDataPath+"/progress").c_str());
+ createDirectory((userCachePath+"/tmp").c_str());
//The records folder for recordings.
- createDirectory((userPath+"/records").c_str());
- createDirectory((userPath+"/records/autosave").c_str());
+ createDirectory((userDataPath+"/records").c_str());
+ createDirectory((userDataPath+"/records/autosave").c_str());
//And the custom folder inside the userpath.
- createDirectory((userPath+"/custom").c_str());
- createDirectory((userPath+"/custom/levels").c_str());
- createDirectory((userPath+"/custom/levelpacks").c_str());
+ createDirectory((userDataPath+"/custom").c_str());
+ createDirectory((userDataPath+"/custom/levels").c_str());
+ createDirectory((userDataPath+"/custom/levelpacks").c_str());
#endif
//Get the dataPath by trying multiple relative locations.
{
FILE *f;
string s;
while(true){
//try existing one
if(!dataPath.empty()){
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
}
//try "./"
dataPath="./data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try "../"
dataPath="../data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try App.Path
dataPath=getAppPath()+"/data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try App.Path+"/../"
dataPath=getAppPath()+"/../data/";
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
//try DATA_PATH
#ifdef DATA_PATH
dataPath=DATA_PATH;
s=dataPath+"font/knewave.ttf";
if((f=fopen(s.c_str(),"rb"))!=NULL){
fclose(f);
break;
}
#endif
//error: can't find file
return false;
}
//Print the dataPath.
cout<<"Data files will be fetched from: "<<dataPath<<endl;
}
return true;
}
std::vector<std::string> enumAllFiles(std::string path,const char* extension){
vector<string> v;
#ifdef WIN32
string s1;
WIN32_FIND_DATAA f;
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="\\";
}
s1=path;
if(extension!=NULL && *extension){
s1+="*.";
s1+=extension;
}else{
s1+="*";
}
HANDLE h=FindFirstFileA(s1.c_str(),&f);
if(h==NULL||h==INVALID_HANDLE_VALUE) return v;
do{
if(!(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
v.push_back(/*path+*/f.cFileName);
}
}while(FindNextFileA(h,&f));
FindClose(h);
return v;
#else
int len=0;
if(extension!=NULL && *extension) len=strlen(extension);
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="/";
}
DIR *pDir;
struct dirent *pDirent;
pDir=opendir(path.c_str());
if(pDir==NULL) return v;
while((pDirent=readdir(pDir))!=NULL){
if(pDirent->d_name[0]=='.'){
if(pDirent->d_name[1]==0||
(pDirent->d_name[1]=='.'&&pDirent->d_name[2]==0)) continue;
}
string s1=path+pDirent->d_name;
struct stat S_stat;
lstat(s1.c_str(),&S_stat);
if(!S_ISDIR(S_stat.st_mode)){
if(len>0){
if((int)s1.size()<len+1) continue;
if(s1[s1.size()-len-1]!='.') continue;
if(strcasecmp(&s1[s1.size()-len],extension)) continue;
}
v.push_back(/*s1*/string(pDirent->d_name));
}
}
closedir(pDir);
return v;
#endif
}
std::vector<std::string> enumAllDirs(std::string path){
vector<string> v;
#ifdef WIN32
string s1;
WIN32_FIND_DATAA f;
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="\\";
}
s1=path+"*";
HANDLE h=FindFirstFileA(s1.c_str(),&f);
if(h==NULL||h==INVALID_HANDLE_VALUE) return v;
do{
// skip '.' and '..' and hidden folders
if(f.cFileName[0]=='.'){
/*if(f.cFileName[1]==0||
(f.cFileName[1]=='.'&&f.cFileName[2]==0))*/ continue;
}
if(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
v.push_back(/*path+*/f.cFileName);
}
}while(FindNextFileA(h,&f));
FindClose(h);
return v;
#else
if(!path.empty()){
char c=path[path.size()-1];
if(c!='/'&&c!='\\') path+="/";
}
DIR *pDir;
struct dirent *pDirent;
pDir=opendir(path.c_str());
if(pDir==NULL) return v;
while((pDirent=readdir(pDir))!=NULL){
if(pDirent->d_name[0]=='.'){
if(pDirent->d_name[1]==0||
(pDirent->d_name[1]=='.'&&pDirent->d_name[2]==0)) continue;
}
string s1=path+pDirent->d_name;
struct stat S_stat;
lstat(s1.c_str(),&S_stat);
if(S_ISDIR(S_stat.st_mode)){
//Skip hidden folders.
s1=string(pDirent->d_name);
if(s1.find('.')==0) continue;
//Add result to vector.
v.push_back(s1);
}
}
closedir(pDir);
return v;
#endif
}
std::string processFileName(const std::string& s){
string prefix=dataPath;
//FIXME: Do we still need those last three?
//REMARK: maybe 'return prefix+s;' is not needed (?)
// it causes some bugs such as can't save level progress
if(s.compare(0,6,"%DATA%")==0){
if(s.size()>6 && (s[6]=='/' || s[6]=='\\')){
return dataPath+s.substr(7);
}else{
return dataPath+s.substr(6);
}
}else if(s.compare(0,6,"%USER%")==0){
if(s.size()>6 && (s[6]=='/' || s[6]=='\\')){
- return userPath+s.substr(7);
+ return getUserPath(USER_DATA)+s.substr(7);
}else{
- return userPath+s.substr(6);
+ return getUserPath(USER_DATA)+s.substr(6);
}
}else if(s.compare(0,9,"%LVLPACK%")==0){
if(s.size()>9 && (s[9]=='/' || s[9]=='\\')){
return prefix+"levelpacks/"+s.substr(10);
}else{
return prefix+"levelpacks/"+s.substr(9);
}
}else if(s.compare(0,5,"%LVL%")==0){
if(s.size()>5 && (s[5]=='/' || s[5]=='\\')){
return prefix+"levels/"+s.substr(6);
}else{
return prefix+"levels/"+s.substr(5);
}
}else if(s.compare(0,8,"%THEMES%")==0){
if(s.size()>8 && (s[8]=='/' || s[8]=='\\')){
return prefix+"themes/"+s.substr(9);
}else{
return prefix+"themes/"+s.substr(8);
}
}else if(s.size()>0 && (s[0]=='/' || s[0]=='\\')){
return s;
#ifdef WIN32
// Another fix for Windows :(
}else if(s.size()>1 && (s[1]==':')){
return s;
#endif
}else{
return prefix+s;
}
}
std::string fileNameFromPath(const std::string &path, const bool webURL){
std::string filename;
size_t pos;
#ifdef WIN32
// FIXME: '/' in string should be '/' not '\/',
// we don't need to escape it
if(webURL){
pos = path.find_last_of("/");
}else{
// NOTE: sometimes path separator in Windows can be '/',
// so we must check botn '\' and '/'
pos = path.find_last_of("\\/");
}
#else
// FIXME: '/' in string should be '/' not '\/',
// we don't need to escape it
pos = path.find_last_of("/");
#endif
if(pos != std::string::npos)
filename.assign(path.begin() + pos + 1, path.end());
else
filename=path;
return filename;
}
std::string pathFromFileName(const std::string &filename){
std::string path;
// FIXME: '/' in string should be '/' not '\/',
// we don't need to escape it
#ifdef WIN32
// NOTE: sometimes path separator in Windows can be '/',
// so we must check botn '\' and '/'
size_t pos = filename.find_last_of("\\/");
#else
size_t pos = filename.find_last_of("/");
#endif
if(pos != std::string::npos)
path.assign(filename.begin(), filename.begin() + pos +1);
else
path=filename;
return path;
}
bool downloadFile(const string &path, const string &destination) {
string filename=fileNameFromPath(path,true);
FILE* file = fopen((destination+filename).c_str(), "wb");
bool status=downloadFile(path,file);
fclose(file);
//And return the status.
return status;
}
bool downloadFile(const string &path, FILE* destination) {
CURL* curl=curl_easy_init();
// proxy test (test only)
string internetProxy = getSettings()->getValue("internet-proxy");
size_t pos = internetProxy.find_first_of(":");
if(pos!=string::npos){
curl_easy_setopt(curl,CURLOPT_PROXYPORT,atoi(internetProxy.substr(pos+1).c_str()));
internetProxy = internetProxy.substr(0,pos);
curl_easy_setopt(curl,CURLOPT_PROXY,internetProxy.c_str());
}
curl_easy_setopt(curl,CURLOPT_URL,path.c_str());
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,writeData);
curl_easy_setopt(curl,CURLOPT_WRITEDATA,destination);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return (res==0);
}
size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream){
return fwrite(ptr, size, nmemb, (FILE *)stream);
}
bool extractFile(const string &fileName, const string &destination) {
//Create the archive we're going to extract.
archive* file=NULL;
//Create the destination we're going to extract to.
archive* dest=NULL;
file=archive_read_new();
dest=archive_write_disk_new();
archive_write_disk_set_options(dest, ARCHIVE_EXTRACT_TIME);
archive_read_support_format_zip(file);
//Now read the archive.
if(archive_read_open_file(file,fileName.c_str(),10240)) {
cerr<<"Error while reading archive "+fileName<<endl;
return false;
}
//Now write every entry to disk.
int status;
archive_entry* entry=NULL;
while(true) {
status=archive_read_next_header(file,&entry);
if(status==ARCHIVE_EOF){
break;
}
if(status!=ARCHIVE_OK){
cerr<<"Error while reading archive "+fileName<<endl;
return false;
}
archive_entry_copy_pathname(entry,(destination+archive_entry_pathname(entry)).c_str());
status=archive_write_header(dest,entry);
if(status!=ARCHIVE_OK){
cerr<<"Error while extracting archive "+fileName<<endl;
return false;
}else{
copyData(file, dest);
status=archive_write_finish_entry(dest);
if(status!=ARCHIVE_OK){
cerr<<"Error while extracting archive "+fileName<<endl;
return false;
}
}
}
//Finally close the archive.
archive_read_close(file);
archive_read_finish(file);
return true;
}
bool createDirectory(const char* path){
#ifdef WIN32
char s0[1024],s[1024];
GetCurrentDirectoryA(sizeof(s0),s0);
PathCombineA(s,s0,path);
for(unsigned int i=0;i<sizeof(s);i++){
if(s[i]=='\0') break;
else if(s[i]=='/') s[i]='\\';
}
//printf("createDirectory:%s\n",s);
return SHCreateDirectoryExA(NULL,s,NULL)!=0;
#else
return mkdir(path,0777)==0;
#endif
}
bool removeDirectory(const char *path){
#ifdef WIN32
WIN32_FIND_DATAA f;
HANDLE h = FindFirstFileA((string(path)+"\\*").c_str(),&f);
#else
//Open the directory that needs to be removed.
DIR* d=opendir(path);
#endif
//Get the path length
size_t path_len = strlen(path);
//Boolean if the directory is emptied.
//True: succes False: failure
//Default is true because if the directory is empty it will never enter the while loop, but we still have success.
bool r = true;
#ifdef WIN32
if(h!=NULL && h!=INVALID_HANDLE_VALUE) {
#else
//Check if the directory exists.
if(d) {
//Pointer to an entry of the directory.
struct dirent* p;
#endif
#ifdef WIN32
do{
#else
//Loop the entries of the directory that needs to be removed as long as there's no error.
while(r && (p=readdir(d))) {
#endif
//Booleand if the entry is deleted.
//True: succes False: failure
//Default is false.
bool r2 = false;
char* buf;
size_t len;
/* Skip the names "." and ".." as we don't want to recurse on them. */
#ifdef WIN32
if (!strcmp(f.cFileName, ".") || !strcmp(f.cFileName, "..")) {
#else
if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
#endif
//The filename is . or .. so we continue to the next entry.
continue;
} else {
#ifdef WIN32
//Get the length of the path + the directory entry name.
len = path_len + strlen(f.cFileName) + 2;
#else
//Get the length of the path + the directory entry name.
len = path_len + strlen(p->d_name) + 2;
#endif
buf = (char*) malloc(len);
if(buf) {
#ifdef WIN32
_snprintf(buf, len, "%s\\%s", path, f.cFileName);
if(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
r2 = removeDirectory(buf);
}else{
r2 = unlink(buf)==0;
}
#else
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);
if(!stat(buf, &statbuf)){
//Check if the entry is a directory or a file.
if (S_ISDIR(statbuf.st_mode)){
//We call ourself(removeDirectory) recursively.
//We return true on success.
r2 = removeDirectory(buf);
}else{
//unlink() returns zero on succes so we set r2 to the unlink(buf)==0.
r2 = unlink(buf)==0;
}
}
#endif
//Free the buf.
free(buf);
}
//We set r to r2 since r2 contains the status of the latest deletion.
r = r2;
}
#ifdef WIN32
}while(r && FindNextFileA(h,&f));
FindClose(h);
#else
}
//Close the directory.
closedir(d);
#endif
}
//The while loop has ended, meaning we (tried) cleared the directory.
//If r is true, meaning no errors we can delete the directory.
if(r){
//The return value of rmdir is 0 when it succeeded.
r = rmdir(path)==0;
}
//Return the status.
return r;
}
bool renameDirectory(const char* oldPath,const char* newPath){
return rename(oldPath,newPath)==0;
}
void copyData(archive* file, archive* dest) {
int status;
const void* buff;
size_t size;
#if ARCHIVE_VERSION_NUMBER < 3000000
off_t offset;
#else
int64_t offset;
#endif
while(true) {
status=archive_read_data_block(file, &buff, &size, &offset);
if(status==ARCHIVE_EOF){
return;
}
if(status!=ARCHIVE_OK){
cerr<<"Error while writing data to disk."<<endl;
return;
}
status=archive_write_data_block(dest, buff, size, offset);
if(status!=ARCHIVE_OK) {
cerr<<"Error while writing data to disk."<<endl;
return;
}
}
}
bool copyFile(const char* source,const char* dest){
//Open the source file.
ifstream fin(source,fstream::binary);
if(!fin)
return false;
//Open the dest file.
ofstream fout(dest,fstream::trunc|fstream::binary);
if(!fout)
return false;
//Copy.
fout<<fin.rdbuf();
return true;
}
bool removeFile(const char* file){
return remove(file)==0;
}
bool createFile(const char* file){
//Open the file with write permission.
FILE* f=fopen(file,"wb");
//Check if there are no problems.
if(f){
//Close the file.
fclose(f);
return true;
}else{
return false;
}
}
diff --git a/src/FileManager.h b/src/FileManager.h
index f914af4..9104508 100644
--- a/src/FileManager.h
+++ b/src/FileManager.h
@@ -1,140 +1,180 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#ifndef FILE_MANAGER_H
#define FILE_MANAGER_H
//Included for the extractFile method.
#include <archive.h>
//Included for the downloadFile method.
#include <curl/curl.h>
//NOTE: All the methods work with processed pathnames.
//So %DATA%, %USER%, etc. can't be used.
//With exception of processFileName().
//A few strings that all have to do with file locations.
//userPath = The path the user files will be stored (addons, settings).
//exeName = The name of the executable.
//dataPath = The path the data files are located.
//appPath = The path where the executable is located.
extern std::string userPath,exeName,dataPath,appPath;
+//The following two paths are for non-Windows systems only.
+//userDataPath = The path for user created content and user downloaded content (addons).
+//userCachePath = The path where temporary stuff will be stored.
+#ifndef WIN32
+extern std::string userDataPath,userCachePath;
+#endif
+
+//Enum containing the different userPath types.
+//NOTE: They are only needed for the non-Windows platform..
+enum UserPaths{
+ //The userpath containing the config files.
+ //Default $HOME/.config/meandmyshadow/
+ USER_CONFIG,
+ //The userpath containing the user data.
+ //Default $HOME/.local/share/meandmyshadow/
+ USER_DATA,
+ //The userpath containing the temporary files.
+ //Default $HOME/.cache/meandmyshadow/
+ USER_CACHE
+};
+
//Method for retrieving the userPath.
+//type: The type of userpath to return, only used on non-Windows platforms.
//Returns: The userPath.
-inline const std::string& getUserPath(){
+inline const std::string& getUserPath(int type=0){
+#ifdef WIN32
return userPath;
+#else
+ switch(type){
+ case USER_CONFIG:
+ return userPath;
+ break;
+ case USER_DATA:
+ return userDataPath;
+ break;
+ case USER_CACHE:
+ return userCachePath;
+ break;
+ default:
+ std::cerr<<"WARNING: Illegal userpath type, returning user config path."<<std::endl;
+ return userPath;
+ break;
+ }
+#endif
}
//Method for retrieving the exeName.
//Returns: The exeName.
inline const std::string& getEXEName(){
return exeName;
}
//Method for retrieving the dataPath.
//Returns: The dataPath.
inline const std::string& getDataPath(){
return dataPath;
}
//Method for retrieving the appPath.
//Returns: The appPath.
inline const std::string& getAppPath(){
return appPath;
}
//This method will try to find paths for the userPath, dataPath, appPath and exeName.
//Returns: True if nothing went wrong.
bool configurePaths();
//Method that returns a list of all the files in a given directory.
//path: The path to list the files of.
//extension: The extension the files must have.
//Returns: A vector containing the names of the files.
std::vector<std::string> enumAllFiles(std::string path,const char* extension=NULL);
//Method that returns a list of all the directories in a given directory.
//path: The path to list the directory of.
//Returns: A vector containing the names of the directories.
std::vector<std::string> enumAllDirs(std::string path);
//Method that will parse the string.
//It will convert %USER%, %DATA%, etc. to their according path.
//s: The string that needs to be processed.
//Returns: The processed string.
std::string processFileName(const std::string& s);
//Method used to retrieve the fileName from a full path.
//path: The path with the filename.
//webURL: Boolean if the path is a weburl.
//Returns: String containing the fileName.
std::string fileNameFromPath(const std::string &path, const bool webURL=false);
//Method used to retrieve the path without the fileName from a full path.
//filename: The path with the filename.
//Returns: String containing the path.
std::string pathFromFileName(const std::string &filename);
//Method that will download a file.
//path: The file to download.
//destination: The destination path where the file will be downloaded to.
//Returns: True if it succeeds without errors.
bool downloadFile(const std::string &path, const std::string &destination);
//Method that will download a file.
//path: The file to download.
//destination: A destination file where the downloaded file will be written to.
//Returns: True if it succeeds without errors.
bool downloadFile(const std::string &path, FILE* destination);
//Method used by curl to copy blocks of data.
size_t writeData(void* ptr,size_t size,size_t nmemb,void* stream);
//Method that will extract an archive and places it in the destination folder.
//fileName: The name of the archive.
//destination: The destination location where the extracted files will come.
//Returns: True if it succeeds without errors.
bool extractFile(const std::string &fileName, const std::string &destination);
//Method used to read a data blcok from an archive and write it to an archive.
//file: The archive to read from.
//dest: The archive to write to.
void copyData(archive* file, archive* dest);
//Method that will create a directory.
//path: The directory to create.
//Returns: True if it succeeds.
bool createDirectory(const char* path);
//Method that will remove a directory.
//path: The directory to remove.
//Returns: True if it succeeds.
bool removeDirectory(const char* path);
//Method that will rename a directory.
//oldPath: The folder path.
//newPath: The destination folder name.
//Returns: True if it succeeds.
bool renameDirectory(const char* oldPath,const char* newPath);
//Method that will create a file.
//file: The filename of the file to create.
//Returns: True if it succeeds.
bool createFile(const char* file);
//Method that will copy a file.
//source: The input file.
//dest: The output file.
//Returns: True if it succeeds.
bool copyFile(const char* source,const char* dest);
//Method that will remove a file.
//file: The file to remove.
//Returns: True if it succeeds.
bool removeFile(const char* file);
#endif
diff --git a/src/Functions.cpp b/src/Functions.cpp
index 786b273..a88f28c 100644
--- a/src/Functions.cpp
+++ b/src/Functions.cpp
@@ -1,951 +1,951 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <string>
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "Objects.h"
#include "Player.h"
#include "GameObjects.h"
#include "Levels.h"
#include "TitleMenu.h"
#include "LevelEditSelect.h"
#include "LevelEditor.h"
#include "Game.h"
#include "LevelPlaySelect.h"
#include "Addons.h"
#include "ImageManager.h"
#include "MusicManager.h"
#include "ThemeManager.h"
#include "GUIListBox.h"
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
//Initialise the imagemanager.
//The ImageManager is used to prevent loading images multiple times.
ImageManager imageManager;
//Initialise the musicManager.
//The MusicManager is used to prevent loading music files multiple times and for playing/fading music.
MusicManager musicManager;
//Pointer to the settings object.
//It is used to load and save the settings file and change the settings.
Settings* settings=0;
SDL_Surface* loadImage(string file){
//We use the imageManager to load the file.
return imageManager.loadImage(file);
}
void applySurface(int x,int y,SDL_Surface* source,SDL_Surface* dest,SDL_Rect* clip){
//The offset is needed to draw at the right location.
SDL_Rect offset;
offset.x=x;
offset.y=y;
//Let SDL do the drawing of the surface.
SDL_BlitSurface(source,clip,dest,&offset);
}
void drawRect(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
rectangleRGBA(dest,x,y,x+w,y+h,color >> 24,color >> 16,color >> 8,255);
}
//Draw a box with anti-aliased borders using SDL_gfx.
void drawGUIBox(int x,int y,int w,int h,SDL_Surface* dest,Uint32 color){
//Fill content's background color from function parameter
boxRGBA(screen,x+1,y+1,x+w-2,y+h-2,color >> 24,color >> 16,color >> 8,color >> 0);
//Draw first black borders around content and leave 1 pixel in every corner
lineRGBA(screen,x+1,y,x+w-2,y,0,0,0,255);
lineRGBA(screen,x+1,y+h-1,x+w-2,y+h-1,0,0,0,255);
lineRGBA(screen,x,y+1,x,y+h-2,0,0,0,255);
lineRGBA(screen,x+w-1,y+1,x+w-1,y+h-2,0,0,0,255);
//Fill the corners with transperent color to create anti-aliased borders
pixelRGBA(screen,x,y,0,0,0,160);
pixelRGBA(screen,x,y+h-1,0,0,0,160);
pixelRGBA(screen,x+w-1,y,0,0,0,160);
pixelRGBA(screen,x+w-1,y+h-1,0,0,0,160);
//Draw second lighter border around content
rectangleRGBA(screen,x+1,y+1,x+w-2,y+h-2,0,0,0,64);
//Create anti-aliasing in corners of second border
pixelRGBA(screen,x+1,y+1,0,0,0,50);
pixelRGBA(screen,x+1,y+h-2,0,0,0,50);
pixelRGBA(screen,x+w-2,y+1,0,0,0,50);
pixelRGBA(screen,x+w-2,y+h-2,0,0,0,50);
}
void drawLine(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color){
//NOTE: We let SDL_gfx render it.
lineRGBA(dest,x1,y1,x2,y2,color >> 24,color >> 16,color >> 8,255);
}
void drawLineWithArrow(int x1,int y1,int x2,int y2,SDL_Surface* dest,Uint32 color,int spacing,int offset,int xsize,int ysize){
//Draw line first
drawLine(x1,y1,x2,y2,dest,color);
//calc delta and length
double dx=x2-x1;
double dy=y2-y1;
double length=sqrt(dx*dx+dy*dy);
if(length<0.001) return;
//calc the unit vector
dx/=length; dy/=length;
//Now draw arrows on it
for(double p=offset;p<length;p+=spacing){
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx-ysize*dy+0.5),int(y1+(p-xsize)*dy+ysize*dx+0.5),dest,color);
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx+ysize*dy+0.5),int(y1+(p-xsize)*dy-ysize*dx+0.5),dest,color);
}
}
bool init(){
//Initialze SDL.
if(SDL_Init(SDL_INIT_EVERYTHING)==-1) {
fprintf(stderr,"FATAL ERROR: SDL_Init failed\n");
return false;
}
//Initialze SDL_mixer (audio).
if(Mix_OpenAudio(22050,MIX_DEFAULT_FORMAT,2,512)==-1){
fprintf(stderr,"FATAL ERROR: Mix_OpenAudio failed\n");
return false;
}
//Initialze SDL_ttf (fonts).
if(TTF_Init()==-1){
fprintf(stderr,"FATAL ERROR: TTF_Init failed\n");
return false;
}
//Initialise the screen.
screen=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,SDL_HWSURFACE | SDL_DOUBLEBUF /*|SDL_FULLSCREEN*/ );
if(screen==NULL){
fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
return false;
}
//Set the the window caption.
SDL_WM_SetCaption(("Me and my shadow "+version).c_str(),NULL);
SDL_EnableUNICODE(1);
//Create the types of blocks.
for(int i=0;i<TYPE_MAX;i++){
Game::blockNameMap[Game::blockName[i]]=i;
}
//Nothing went wrong so we return true.
return true;
}
bool loadFiles(){
//Load the music and play it.
if(musicManager.loadMusic((getDataPath()+"music/menu.music")).empty()){
printf("WARNING: Unable to load background music! \n");
}
musicManager.playMusic("menu",false);
//Always load the default music list for fallback.
musicManager.loadMusicList((getDataPath()+"music/default.list"));
//Load the fonts.
fontTitle=TTF_OpenFont((getDataPath()+"font/knewave.ttf").c_str(),55);
fontGUI=TTF_OpenFont((getDataPath()+"font/knewave.ttf").c_str(),32);
fontText=TTF_OpenFont((getDataPath()+"font/Blokletters-Viltstift.ttf").c_str(),16);
if(fontTitle==NULL || fontGUI==NULL || fontText==NULL){
printf("ERROR: Unable to load fonts! \n");
return false;
}
//Load the default theme.
if(objThemes.appendThemeFromFile(getDataPath()+"themes/Cloudscape/theme.mnmstheme")==NULL){
printf("ERROR: Can't load default theme file\n");
return false;
}
//Nothing failed so return true.
return true;
}
bool loadSettings(){
- settings=new Settings(getUserPath()+"meandmyshadow.cfg");
+ settings=new Settings(getUserPath(USER_CONFIG)+"meandmyshadow.cfg");
settings->parseFile();
//Always return true?
return true;
}
bool saveSettings(){
settings->save();
//Always return true?
return true;
}
Settings* getSettings(){
return settings;
}
MusicManager* getMusicManager(){
return &musicManager;
}
void clean(){
//We delete the settings.
if(settings){
delete settings;
settings=NULL;
}
//Get rid of the currentstate/
//NOTE: The state is probably already deleted by the changeState function.
if(currentState)
delete currentState;
//Destroy the GUI if present.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Destroy the imageManager.
imageManager.destroy();
//Destroy the musicManager.
musicManager.destroy();
//Close the fonts and quit SDL_ttf.
TTF_CloseFont(fontTitle);
TTF_CloseFont(fontGUI);
TTF_CloseFont(fontText);
TTF_Quit();
//Quit SDL.
SDL_Quit();
//And finally stop audio.
Mix_CloseAudio();
}
void setNextState(int newstate){
//Only change the state when we aren't already exiting.
if(nextState!=STATE_EXIT){
nextState=newstate;
}
}
void changeState(){
//Check if there's a nextState.
if(nextState!=STATE_NULL){
//Delete the currentState.
delete currentState;
currentState=NULL;
//Set the currentState to the nextState.
stateID=nextState;
nextState=STATE_NULL;
//Init the state.
switch(stateID){
case STATE_GAME:
currentState=new Game();
break;
case STATE_MENU:
levels.clear();
currentState=new Menu();
break;
case STATE_LEVEL_SELECT:
currentState=new LevelPlaySelect();
break;
case STATE_LEVEL_EDIT_SELECT:
currentState=new LevelEditSelect();
break;
case STATE_LEVEL_EDITOR:
currentState=new LevelEditor();
break;
case STATE_OPTIONS:
currentState=new Options();
break;
case STATE_ADDONS:
currentState=new Addons();
break;
}
//NOTE: STATE_EXIT isn't mentioned, meaning that currentState is null.
//This way the game loop will break and the program will exit.
//Fade out.
int fade=255;
SDL_BlitSurface(screen,NULL,tempSurface,NULL);
while(fade>0){
fade-=17;
if(fade<0)
fade=0;
SDL_FillRect(screen,NULL,0);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA, fade);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
SDL_Flip(screen);
SDL_Delay(25);
}
}
}
void musicStoppedHook(){
//We just call the musicStopped method of the MusicManager.
musicManager.musicStopped();
}
bool checkCollision(const SDL_Rect& a,const SDL_Rect& b){
//Check if the left side of box a isn't past the right side of b.
if(a.x>=b.x+b.w){
return false;
}
//Check if the right side of box a isn't left of the left side of b.
if(a.x+a.w<=b.x){
return false;
}
//Check if the top side of box a isn't under the bottom side of b.
if(a.y>=b.y+b.h){
return false;
}
//Check if the bottom side of box a isn't above the top side of b.
if(a.y+a.h<=b.y){
return false;
}
//We have collision.
return true;
}
void setCamera(){
//SetCamera only works in the Level editor.
if(stateID==STATE_LEVEL_EDITOR){
//Get the mouse coordinates.
int x,y;
SDL_GetMouseState(&x,&y);
//Make sure we avoid the toolbar.
SDL_Rect mouse={x,y,0,0};
SDL_Rect toolbar={155,550,510,50};
if(checkCollision(mouse,toolbar))
return;
//Check if the mouse is near the left edge of the screen.
//Else check if the mouse is near the right edge.
if(x<50){
//We're near the left edge so move the camera.
camera.x-=5;
}else if(x>SCREEN_WIDTH-50){
//We're near the right edge so move the camera.
camera.x+=5;
}
//Check if the mouse is near the top edge of the screen.
//Else check if the mouse is near the bottom edge.
if(y<50){
//We're near the top edge so move the camera.
camera.y-=5;
}else if(y>SCREEN_HEIGHT-50){
//We're near the bottom edge so move the camera.
camera.y+=5;
}
}
}
bool parseArguments(int argc, char** argv){
//Loop through all arguments.
//We start at one since 0 is the command itself.
for(int i=1;i<argc;i++){
string argument=argv[i];
//Check if the argument is the data-dir.
if(argument=="--data-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return false;
}
//Configure the dataPath with the given path.
dataPath=argv[i];
if(!getDataPath().empty()){
char c=dataPath[dataPath.size()-1];
if(c!='/'&&c!='\\') dataPath+="/";
}
}else if(argument=="--user-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return false;
}
//Configure the userPath with the given path.
userPath=argv[i];
if(!userPath.empty()){
char c=userPath[userPath.size()-1];
if(c!='/'&&c!='\\') userPath+="/";
}
}else if(argument=="-v" || argument=="-version" || argument=="--version"){
//Print the version.
printf("Version: '%s'\n\n",version.c_str());
}else if(argument=="-h" || argument=="-help" || argument=="--help"){
//If the help is requested we'll return false without printing an error.
//This way the usage/help text will be printed.
return false;
}else{
//Any other argument is unknow so we return false.
printf("ERROR: Unknown argument %s\n\n",argument.c_str());
return false;
}
}
//If everything went well we can return true.
return true;
}
//Special structure that will recieve the GUIEventCallbacks of the messagebox.
struct msgBoxHandler:public GUIEventCallback{
public:
//Integer containing the ret(urn) value of the messageBox.
int ret;
public:
//Constructor.
msgBoxHandler():ret(0){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Make sure it's a click event.
if(eventType==GUIEventClick){
//Set the return value.
ret=obj->value;
//After a click event we can delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
};
msgBoxResult msgBox(string prompt,msgBoxButtons buttons,const string& title){
//Create the event handler.
msgBoxHandler objHandler;
//The GUI objects.
GUIObject* obj;
//We keep a pointer to the original GUIObjectRoot for later.
GUIObject* tmp=GUIObjectRoot;
//Create the GUIObjectRoot, the height and y location is temp.
//It depends on the content what it will be.
GUIObjectRoot=new GUIObject(100,200,600,200,GUIObjectFrame,title.c_str());
//Integer containing the current y location used to grow dynamic depending on the content.
int y=50;
//Now process the prompt.
{
//Pointer to the string.
char* lps=(char*)prompt.c_str();
//Pointer to a character.
char* lp=NULL;
//We keep looping forever.
//The only way out is with the break statement.
for(;;){
//As long as it's still the same sentence we continue.
//It will stop when there's a newline or end of line.
for(lp=lps;*lp!='\n'&&*lp!='\r'&&*lp!=0;lp++);
//Store the character we stopped on. (End or newline)
char c=*lp;
//Set the character in the string to 0, making lps a string containing one sentence.
*lp=0;
//Integer used to center the sentence horizontally.
int x;
TTF_SizeText(fontText,lps,&x,NULL);
x=(600-x)/2;
//Add a GUIObjectLabel with the sentence.
GUIObjectRoot->childControls.push_back(new GUIObject(x,y,584,25,GUIObjectLabel,lps));
//Increase y with 25, about the height of the text.
y+=25;
//Check the stored character if it was a stop.
if(c==0){
//It was so break out of the for loop.
lps=lp;
break;
}
//It wasn't meaning more will follow.
//We set lps to point after the "newline" forming a new string.
lps=lp+1;
}
}
//Add 70 to y to leave some space between the content and the buttons.
y+=70;
//Recalc the size of the message box.
GUIObjectRoot->top=(SCREEN_HEIGHT-y)/2;
GUIObjectRoot->height=y;
//Now we need to add the buttons.
//Integer containing the number of buttons to add.
int count=0;
//Array with the return codes for the buttons.
int value[3]={0};
//Array containing the captation for the buttons.
string button[3]={"","",""};
switch(buttons){
case MsgBoxOKCancel:
count=2;
button[0]="OK";value[0]=MsgBoxOK;
button[1]="Cancel";value[1]=MsgBoxCancel;
break;
case MsgBoxAbortRetryIgnore:
count=3;
button[0]="Abort";value[0]=MsgBoxAbort;
button[1]="Retry";value[1]=MsgBoxRetry;
button[2]="Ignore";value[2]=MsgBoxIgnore;
break;
case MsgBoxYesNoCancel:
count=3;
button[0]="Yes";value[0]=MsgBoxYes;
button[1]="No";value[1]=MsgBoxNo;
button[2]="Cancel";value[2]=MsgBoxCancel;
break;
case MsgBoxYesNo:
count=2;
button[0]="Yes";value[0]=MsgBoxYes;
button[1]="No";value[1]=MsgBoxNo;
break;
case MsgBoxRetryCancel:
count=2;
button[0]="Retry";value[0]=MsgBoxRetry;
button[1]="Cancel";value[1]=MsgBoxCancel;
break;
default:
count=1;
button[0]="OK";value[0]=MsgBoxOK;
break;
}
//Now we start making the buttons.
{
//Calculate the x location (centered).
int x=302-count*50;
//Reduce y so that the buttons fit inside the frame.
y-=40;
//Loop to add the buttons.
for(int i=0;i<count;i++,x+=100){
obj=new GUIObject(x,y,96,36,GUIObjectButton,button[i].c_str(),value[i]);
obj->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj);
}
}
//Now we dim the screen and keep the GUI rendering/updating.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)){
GUIObjectHandleEvents(true);
//Also check for the return, escape or backspace button.
//escape = KEYUP.
//backspace and return = KEYDOWN.
if(count==1 && ((event.type==SDL_KEYUP && event.key.keysym.sym==SDLK_ESCAPE) ||
(event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_RETURN || event.key.keysym.sym==SDLK_BACKSPACE)))){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Render the gui.
if(GUIObjectRoot)
GUIObjectRoot->render();
SDL_Flip(screen);
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=tmp;
//And return the result.
return (msgBoxResult)objHandler.ret;
}
struct fileDialogHandler:public GUIEventCallback{
public:
//The ret(urn) value, true=ok and false=cancel
bool ret;
//Boolean if it's a save dialog.
bool isSave;
//Boolean if the file should be verified.
bool verifyFile;
//Boolean if files should be listed instead of directories.
bool files;
//Pointer to the textfield containing the filename.
GUIObject* txtName;
//Pointer to the listbox containing the different files.
GUIListBox* lstFile;
//The extension the files listed should have.
const char* extension;
//The current filename.
string fileName;
//The current search path.
string path;
//Vector containing the search paths.
vector<string> searchPath;
public:
//Constructor.
fileDialogHandler(bool isSave=false,bool verifyFile=false, bool files=true):ret(false),
isSave(isSave),verifyFile(verifyFile),
files(files),txtName(NULL){}
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check for the ok event.
if(name=="cmdOK"){
//Get the entered fileName from the text field.
std::string s=txtName->caption;
//If it doesn't contain a slash we need to add the path to the fileName.
if(s.find_first_of("/")==string::npos)
s=path+s;
//If the string empty we return.
if(s.empty() || s.find_first_of("*?")!=string::npos)
return;
//We only need to check for extensions if it isn't a folder dialog.
if(files){
//If there isn't right extension add it.
size_t found=s.find_first_of(".");
if(found!=string::npos)
s.replace(s.begin()+found+1,s.end(),extension);
else if (s.substr(found+1)!=extension)
s.append(string(".")+extension);
}
//Check if we should save or load the file.
//
if(isSave){
//Open the file with readpremission to check if it already exists.
FILE* f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Prompt the user with a Yes or No question.
if(msgBox(s+" already exists.\nDo you want to overwrite it?",MsgBoxYesNo,"Overwrite Prompt")!=MsgBoxYes){
//He answered no, so we return.
return;
}
}
//Check if we should verify the file.
//Verifying only applies to files not to directories.
if(verifyFile && files){
//Open the file with write permission.
f=fopen(processFileName(s).c_str(),"wb");
//Check if their aren't problems.
if(f){
//Close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//The file can't be opened so tell the user.
msgBox("Can't open file "+s+".",MsgBoxOKOnly,"Error");
return;
}
}
}else if(verifyFile && files){
//We need to verify a file for opening.
FILE *f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it didn't fail.
if(f){
//Succes, so close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render();
//Unable to open file so tell the user.
msgBox("Can't open file "+s+".",MsgBoxOKOnly,"Error");
return;
}
}
//If we haven't returned then it's fine.
//Set the fileName to the chosen file.
fileName=s;
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Set return to true.
ret=true;
}else if(name=="cmdCancel"){
//Cancel means we can kill the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="lstFile"){
//Get a pointer to the listbox.
GUIListBox* obj1=lstFile;
//Make sure the option exist and change textfield to it.
if(obj1!=NULL && txtName!=NULL && obj1->value>=0 && obj1->value<(int)obj1->item.size()){
txtName->caption=obj1->item[obj1->value];
}
}else if(name=="lstSearchIn"){
//Get the searchpath listbox.
GUISingleLineListBox *obj1=dynamic_cast<GUISingleLineListBox*>(obj);
//Check if the entry exists.
if(obj1!=NULL && lstFile!=NULL && obj1->value>=0 && obj1->value<(int)searchPath.size()){
//Temp string.
string s;
//Get the new search path.
path=searchPath[obj1->value];
//Make sure it isn't empty.
if(!path.empty()){
//Process the filename.
s=processFileName(path);
}else{
//It's empty so we give the userpath.
s=getUserPath();
}
//Fill the list with files or directories.
if(files) {
lstFile->item=enumAllFiles(s,extension);
}else
lstFile->item=enumAllDirs(s);
//Remove any selection from the list.
lstFile->value=-1;
}
}
}
};
bool fileDialog(string& fileName,const char* title,const char* extension,const char* path,bool isSave,bool verifyFile,bool files){
//Pointer to GUIObject to make the GUI with.
GUIObject* obj;
//Pointer to the current GUIObjectRoot.
//We keep it so we can put it back after closing the fileDialog.
GUIObject* tmp=GUIObjectRoot;
//Create the fileDialogHandler, used for event handling.
fileDialogHandler objHandler(isSave,verifyFile,files);
//Vector containing the pathNames.
vector<string> pathNames;
//Set the extension of the objHandler.
objHandler.extension=extension;
//We now need to splits the given path into multiple path names.
if(path && path[0]){
//The string isn't empty.
//Pointer to the paths string.
char* lp=(char*)path;
//Pointer to the first newline.
char* lps=strchr(lp,'\n');
//Pointer used for checking if their's another newline.
//It will indicate if it's the last set or not.
char* lpe;
//Check for a newline.
if(lps){
//We have newline(s) so loop forever.
//We can only break out of the loop when the string ends.
for(;;){
//Add the first searchpath.
//This is the beginning of the string (lp) to the first newline. (lps)
objHandler.searchPath.push_back(string(lp,lps-lp));
//We should have another newline so search for it.
lpe=strchr(lps+1,'\n');
if(lpe){
//We found it so we add that to the pathname.
pathNames.push_back(string(lps+1,lpe-lps-1));
//And start over again by setting lp to the start of a new set of searchPath/pathName.
lp=lpe+1;
}else{
//There is no newline anymore, meaning the last entry, the rest of the string must be the pathName.
pathNames.push_back(string(lps+1));
//And break out of the loop.
break;
}
//We haven't broken out so search for a newline.
lps=strchr(lp,'\n');
//If there isn't a newline break.
if(!lps)
break;
}
}else{
//There is no newline thus the whole string is the searchPath.
objHandler.searchPath.push_back(path);
}
}else{
//Empty so put an empty string as searchPath.
objHandler.searchPath.push_back(string());
}
//It's time to create the GUI.
//If there are more than one pathNames we need to add a GUISingleLineListBox.
int base_y=pathNames.size()>0?60:20;
//Create the frame.
GUIObjectRoot=new GUIObject(100,100-base_y/2,600,400+base_y,GUIObjectFrame,title?title:(isSave?"Save File":"Load File"));
//Create the search path list box if needed.
if(pathNames.size()>0){
GUIObjectRoot->childControls.push_back(new GUIObject(8,40,184,36,GUIObjectLabel,"Search In"));
GUISingleLineListBox* obj1=new GUISingleLineListBox(160,40,432,36);
obj1->item=pathNames;
obj1->value=0;
obj1->name="lstSearchIn";
obj1->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj1);
}
//Add the FileName label and textfield.
GUIObjectRoot->childControls.push_back(new GUIObject(8,20+base_y,184,36,GUIObjectLabel,"File Name"));
{
//Fill the textbox with the given fileName.
string s=fileName;
if(!isSave){
//But only if it isn't empty.
if(s.empty() && extension && extension[0])
s=string("*.")+string(extension);
}
//Create the textbox and add it to the GUI.
objHandler.txtName=new GUIObject(160,20+base_y,432,36,GUIObjectTextBox,s.c_str());
GUIObjectRoot->childControls.push_back(objHandler.txtName);
}
//Now we add the ListBox containing the files or directories.
{
GUIListBox* obj1=new GUIListBox(8,60+base_y,584,292);
//Get the searchPath.
string s=objHandler.searchPath[0];
//Make sure it isn't empty.
if(!s.empty()){
objHandler.path=s;
s=processFileName(s);
}else{
s=getUserPath();
}
//Check if we should list files or directories.
if(files){
//Fill the list with files.
obj1->item=enumAllFiles(s,extension);
}else{
//Fill the list with directories.
obj1->item=enumAllDirs(s);
}
obj1->name="lstFile";
obj1->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj1);
objHandler.lstFile=obj1;
}
//Now create the OK and Cancel buttons.
obj=new GUIObject(200,360+base_y,192,36,GUIObjectButton,"OK");
obj->name="cmdOK";
obj->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(400,360+base_y,192,36,GUIObjectButton,"Cancel");
obj->name="cmdCancel";
obj->eventCallback=&objHandler;
GUIObjectRoot->childControls.push_back(obj);
//Now we keep rendering and updating the GUI.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event))
GUIObjectHandleEvents(true);
if(GUIObjectRoot)
GUIObjectRoot->render();
SDL_Flip(screen);
SDL_Delay(30);
}
//The while loop ended meaning we can restore the previous GUI.
GUIObjectRoot=tmp;
//Now determine what the return value is (and if there is one).
if(objHandler.ret)
fileName=objHandler.fileName;
return objHandler.ret;
}
diff --git a/src/LevelEditSelect.cpp b/src/LevelEditSelect.cpp
index 623104b..60510d1 100644
--- a/src/LevelEditSelect.cpp
+++ b/src/LevelEditSelect.cpp
@@ -1,688 +1,688 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "LevelEditSelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "Game.h"
#include <SDL/SDL_ttf.h>
#include <SDL/SDL.h>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <iostream>
using namespace std;
LevelEditSelect::LevelEditSelect():LevelSelect("Map Editor"){
//The levelpack name text field.
levelpackName=new GUIObject(280,104,240,32,GUIObjectTextBox);
levelpackName->eventCallback=this;
levelpackName->visible=false;
GUIObjectRoot->childControls.push_back(levelpackName);
//Create the six buttons at the bottom of the screen.
GUIObject* obj=new GUIObject(20,480,240,32,GUIObjectButton,"New Levelpack");
obj->name="cmdNewLvlpack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
propertiesPack=new GUIObject(280,480,240,32,GUIObjectButton,"Pack Properties");
propertiesPack->name="cmdLvlpackProp";
propertiesPack->eventCallback=this;
GUIObjectRoot->childControls.push_back(propertiesPack);
removePack=new GUIObject(540,480,240,32,GUIObjectButton,"Remove Pack");
removePack->name="cmdRmLvlpack";
removePack->eventCallback=this;
GUIObjectRoot->childControls.push_back(removePack);
move=new GUIObject(20,540,240,32,GUIObjectButton,"Move Map");
move->name="cmdMoveMap";
move->eventCallback=this;
move->enabled=false;
GUIObjectRoot->childControls.push_back(move);
remove=new GUIObject(280,540,240,32,GUIObjectButton,"Remove Map");
remove->name="cmdRmMap";
remove->eventCallback=this;
remove->enabled=false;
GUIObjectRoot->childControls.push_back(remove);
edit=new GUIObject(540,540,240,32,GUIObjectButton,"Edit Map");
edit->name="cmdEdit";
edit->eventCallback=this;
edit->enabled=false;
GUIObjectRoot->childControls.push_back(edit);
//NOTE: We are changing the available list of levelpacks to prevent editing the main/addons levelpacks.
listPacks();
//show level list
refresh();
}
LevelEditSelect::~LevelEditSelect(){
selectedNumber=NULL;
}
void LevelEditSelect::listPacks(){
levelpackLocations.clear();
levelpacks->item.clear();
levelpacks->value=0;
- vector<string> v=enumAllDirs(getUserPath()+"custom/levelpacks/");
+ vector<string> v=enumAllDirs(getUserPath(USER_DATA)+"custom/levelpacks/");
v.push_back("Levels");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
- levelpackLocations[*i]=getUserPath()+"custom/levelpacks/"+*i;
+ levelpackLocations[*i]=getUserPath(USER_DATA)+"custom/levelpacks/"+*i;
//Check if we can find the lastlevelpack.
if(*i==getSettings()->getValue("lastlevelpack")){
levelpacks->value=i-v.begin();
packName=*i;
}
}
levelpacks->item=v;
//And call changePack since we changed the levelpack.
changePack();
}
void LevelEditSelect::changePack(){
packName=levelpacks->item[levelpacks->value];
if(packName=="Levels"){
//Clear the current levels.
levels.clear();
levels.setCurrentLevel(0);
levels.levelpackPath="";
//List the custom levels and add them one for one.
- vector<string> v=enumAllFiles(getUserPath()+"custom/levels/");
+ vector<string> v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
- levels.addLevel(getUserPath()+"custom/levels/"+*i);
+ levels.addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
}else{
//Load the levelpack in the normal way.
if(!levels.loadLevels(levelpackLocations[packName]+"/levels.lst")){
msgBox("Can't load level pack:\n"+packName,MsgBoxOKOnly,"Error");
}
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
}
void LevelEditSelect::packProperties(){
//Open a message popup.
//Pointer to the current GUIObjectRoot.
//We keep it so we can put it back after closing the fileDialog.
GUIObject* tmp=GUIObjectRoot;
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-300)/2,600,300,GUIObjectFrame,"Properties");
GUIObject* obj;
obj=new GUIObject(40,80,240,36,GUIObjectLabel,"Name:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(300,80,240,36,GUIObjectTextBox,packName.c_str());
obj->name="LvlpackName";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,120,240,36,GUIObjectLabel,"Description:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(300,120,240,36,GUIObjectTextBox,levels.levelpackDescription.c_str());
obj->name="LvlpackDescription";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(40,160,240,36,GUIObjectLabel,"Congratulation text:");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(300,160,240,36,GUIObjectTextBox,levels.congratulationText.c_str());
obj->name="LvlpackCongratulation";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,300-44,150,36,GUIObjectButton,"OK");
obj->name="cfgOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,300-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
SDL_Flip(screen);
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=tmp;
}
void LevelEditSelect::addLevel(){
//Open a message popup.
//Pointer to the current GUIObjectRoot.
//We keep it so we can put it back after closing the fileDialog.
GUIObject* tmp=GUIObjectRoot;
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,"Add level");
GUIObject* obj;
obj=new GUIObject(40,80,240,36,GUIObjectLabel,"File name:");
GUIObjectRoot->childControls.push_back(obj);
char s[64];
sprintf(s,"map%02d.map",levels.getLevelCount()+1);
obj=new GUIObject(300,80,240,36,GUIObjectTextBox,s);
obj->name="LvlFile";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgAddOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgAddCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
SDL_Flip(screen);
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=tmp;
}
void LevelEditSelect::moveLevel(){
//Open a message popup.
//Pointer to the current GUIObjectRoot.
//We keep it so we can put it back after closing the fileDialog.
GUIObject* tmp=GUIObjectRoot;
GUIObjectRoot=new GUIObject(100,(SCREEN_HEIGHT-200)/2,600,200,GUIObjectFrame,"Move level");
GUIObject* obj;
obj=new GUIObject(40,80,240,36,GUIObjectLabel,"Level: ");
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(300,80,240,36,GUIObjectTextBox,"1");
obj->name="MoveLevel";
GUIObjectRoot->childControls.push_back(obj);
obj=new GUISingleLineListBox(40,120,240,36);
obj->name="lstPlacement";
vector<string> v;
v.push_back("Before");
v.push_back("After");
v.push_back("Swap");
(dynamic_cast<GUISingleLineListBox*>(obj))->item=v;
obj->value=0;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,200-44,150,36,GUIObjectButton,"OK");
obj->name="cfgMoveOK";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(350,200-44,150,36,GUIObjectButton,"Cancel");
obj->name="cfgMoveCancel";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Dim the screen using the tempSurface.
SDL_FillRect(tempSurface,NULL,0);
SDL_SetAlpha(tempSurface,SDL_SRCALPHA,155);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
while(GUIObjectRoot){
while(SDL_PollEvent(&event)) GUIObjectHandleEvents(true);
if(GUIObjectRoot) GUIObjectRoot->render();
SDL_Flip(screen);
SDL_Delay(30);
}
//We're done so set the original GUIObjectRoot back.
GUIObjectRoot=tmp;
}
void LevelEditSelect::refresh(){
int m=levels.getLevelCount();
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
selectedNumber=NULL;
}
//Disable the level specific buttons.
move->enabled=false;
remove->enabled=false;
edit->enabled=false;
for(int n=0;n<=m;n++){
numbers.push_back(Number());
}
for(int n=0;n<m;n++){
SDL_Rect box={(n%10)*64+80,(n/10)*64+184,0,0};
numbers[n].init(n,box);
}
SDL_Rect box={(m%10)*64+80,(m/10)*64+184,0,0};
numbers[m].init("+",box);
m++; //including the "+" button
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+9)/10;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
levelpackDescription->caption=levels.levelpackDescription;
int width,height;
TTF_SizeText(fontText,levels.levelpackDescription.c_str(),&width,&height);
levelpackDescription->left=(800-width)/2;
}
void LevelEditSelect::selectNumber(unsigned int number,bool selected){
if(selected){
levels.setCurrentLevel(number);
setNextState(STATE_LEVEL_EDITOR);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}else{
if(number==numbers.size()-1){
addLevel();
}else if(number>=0 && number<numbers.size()){
selectedNumber=&numbers[number];
//Enable the level specific buttons.
//NOTE: We check if 'remove levelpack' is enabled, if not then it's the Levels levelpack.
if(removePack->enabled)
move->enabled=true;
remove->enabled=true;
edit->enabled=true;
}
}
}
void LevelEditSelect::render(){
//Let the levelselect render.
LevelSelect::render();
}
void LevelEditSelect::renderTooltip(unsigned int number,int dy){
SDL_Color fg={0,0,0};
SDL_Surface* name;
if(number==(unsigned)levels.getLevelCount()){
//Render the name of the level.
name=TTF_RenderText_Blended(fontText,"Add level",fg);
}else{
//Render the name of the level.
name=TTF_RenderText_Blended(fontText,levels.getLevelName(number).c_str(),fg);
}
//Check if name isn't null.
if(name==NULL)
return;
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
r.w=name->w;
r.h=name->h;
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=name->h+4;
}else{
r.y+=numbers[number].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFF00|240;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,screen,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(name!=NULL){
//Draw the name.
SDL_BlitSurface(name,NULL,screen,&r2);
}
//And free the surfaces.
SDL_FreeSurface(name);
}
void LevelEditSelect::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//NOTE: We check for the levelpack change to enable/disable some levelpack buttons.
if(name=="cmdLvlPack"){
//Set the name of the levelpack.
packName=((GUISingleLineListBox*)obj)->item[obj->value];
//Check if it matches the Levels levelpack.
if(((GUISingleLineListBox*)obj)->item[obj->value]=="Levels"){
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
getSettings()->setValue("lastlevelpack","Levels");
//We call changepack and return to prevent the LevelSelect to undo what we did.
changePack();
refresh();
return;
}else{
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
}
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(name,obj,eventType);
//Check for the edit button.
if(name=="cmdNewLvlpack"){
//Clear the current pack.
packName.clear();
levels.clear();
//Create a new pack.
packProperties();
}else if(name=="cmdLvlpackProp"){
//Show the pack properties.
packProperties();
}else if(name=="cmdRmLvlpack"){
//Show an "are you sure" message.
if(msgBox("Are you sure?",MsgBoxYesNo,"Remove prompt")==MsgBoxYes){
//Remove the directory.
if(!removeDirectory(levelpackLocations[packName].c_str())){
cerr<<"ERROR: Unable to remove levelpack directory "<<levelpackLocations[packName]<<endl;
}
//Remove it from the vector (levelpack list).
vector<string>::iterator it;
it=find(levelpacks->item.begin(),levelpacks->item.end(),packName);
if(it!=levelpacks->item.end()){
levelpacks->item.erase(it);
}
//And call changePack.
levelpacks->value=levelpacks->item.size()-1;
changePack();
refresh();
}
}else if(name=="cmdMoveMap"){
if(selectedNumber!=NULL){
moveLevel();
}
}else if(name=="cmdRmMap"){
if(selectedNumber!=NULL){
if(packName!="Levels"){
if(!removeFile((levelpackLocations[packName]+"/"+levels.getLevel(selectedNumber->getNumber())->file).c_str())){
cerr<<"ERROR: Unable to remove level "<<(levelpackLocations[packName]+"/"+levels.getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
levels.removeLevel(selectedNumber->getNumber());
levels.saveLevels(levelpackLocations[packName]+"/levels.lst");
}else{
//This is the levels levelpack so we just remove the file.
if(!removeFile(levels.getLevel(selectedNumber->getNumber())->file.c_str())){
cerr<<"ERROR: Unable to remove level "<<(levelpackLocations[packName]+"/"+levels.getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
changePack();
}
//And refresh the selection screen.
refresh();
}
}else if(name=="cmdEdit"){
if(selectedNumber!=NULL){
levels.setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_LEVEL_EDITOR);
//Pick music from the current music list.
getMusicManager()->pickMusic();
}
}
//Check for levelpack properties events.
if(name=="cfgOK"){
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlpackName"){
//Check if the name changed.
if(packName!=GUIObjectRoot->childControls[i]->caption){
//Delete the old one.
if(!packName.empty()){
- if(!renameDirectory((getUserPath()+"custom/levelpacks/"+packName).c_str(),(getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
- cerr<<"ERROR: Unable to move levelpack directory "<<(getUserPath()+"custom/levelpacks/"+packName)<<" to "<<(getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
+ if(!renameDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+packName).c_str(),(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
+ cerr<<"ERROR: Unable to move levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+packName)<<" to "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
//Also change the levelpack location.
map<string,string>::iterator it;
it=levelpackLocations.find(packName);
if(it!=levelpackLocations.end()){
levelpackLocations.erase(it);
}
//And the levelpack list.
vector<string>::iterator it1;
it1=find(levelpacks->item.begin(),levelpacks->item.end(),packName);
if(it1!=levelpacks->item.end()){
levelpacks->item.erase(it1);
if((unsigned)levelpacks->value>levelpacks->item.size())
levelpacks->value=levelpacks->item.size()-1;
}
//Also add the levelpack location
- levelpackLocations[GUIObjectRoot->childControls[i]->caption]=(getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption);
+ levelpackLocations[GUIObjectRoot->childControls[i]->caption]=(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption);
levelpacks->item.push_back(GUIObjectRoot->childControls[i]->caption);
levelpacks->value=levelpacks->item.size()-1;
}else{
- if(!createDirectory((getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
- cerr<<"ERROR: Unable to create levelpack directory "<<(getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
+ if(!createDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
+ cerr<<"ERROR: Unable to create levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
- if(!createFile((getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst").c_str())){
- cerr<<"ERROR: Unable to create levelpack file "<<(getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst")<<endl;
+ if(!createFile((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst").c_str())){
+ cerr<<"ERROR: Unable to create levelpack file "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst")<<endl;
}
//Also add the levelpack location.
- levelpackLocations[GUIObjectRoot->childControls[i]->caption]=(getUserPath()+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption);
+ levelpackLocations[GUIObjectRoot->childControls[i]->caption]=(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption);
levelpacks->item.push_back(GUIObjectRoot->childControls[i]->caption);
levelpacks->value=levelpacks->item.size()-1;
}
//And set the new name.
packName=GUIObjectRoot->childControls[i]->caption;
changePack();
refresh();
}
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackDescription"){
levels.levelpackDescription=GUIObjectRoot->childControls[i]->caption;
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackCongratulation"){
levels.congratulationText=GUIObjectRoot->childControls[i]->caption;
}
}
//Save the configuration.
- levels.saveLevels(getUserPath()+"custom/levelpacks/"+packName+"/levels.lst");
+ levels.saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgCancel"){
//Check if packName is empty, if so it was a new levelpack and we need to revert to an existing one.
if(packName.empty()){
packName=levelpacks->item[levelpacks->value];
changePack();
}
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for add level events.
if(name=="cfgAddOK"){
//Check if the file name isn't null.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlFile"){
if(GUIObjectRoot->childControls[i]->caption.empty()){
msgBox("No file name given for the new level.",MsgBoxOKOnly,"Missing file name");
return;
}else{
string tmp_caption = GUIObjectRoot->childControls[i]->caption;
//Replace all spaces with a underline.
size_t j;
for(;(j=tmp_caption.find(" "))!=string::npos;){
tmp_caption.replace(j,1,"_");
}
//If there isn't ".map" extension add it.
size_t found=tmp_caption.find_first_of(".");
if(found!=string::npos)
tmp_caption.replace(tmp_caption.begin()+found+1,tmp_caption.end(),"map");
else if (tmp_caption.substr(found+1)!="map")
tmp_caption.append(".map");
/* Create path and file in it */
string path=(levelpackLocations[packName]+"/"+tmp_caption);
if(packName=="Levels"){
- path=(getUserPath()+"/custom/levels/"+tmp_caption);
+ path=(getUserPath(USER_DATA)+"/custom/levels/"+tmp_caption);
}
if(!createFile(path.c_str())){
cerr<<"ERROR: Unable to create level file "<<path<<endl;
}
levels.addLevel(path);
if(packName!="Levels")
- levels.saveLevels(getUserPath()+"custom/levelpacks/"+packName+"/levels.lst");
+ levels.saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh();
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
}
}
}
}else if(name=="cfgAddCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for move level events.
if(name=="cfgMoveOK"){
//Check if the entered level number is valid.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
int level=0;
int placement=0;
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="MoveLevel"){
level=atoi(GUIObjectRoot->childControls[i]->caption.c_str());
if(level<=0 || level>levels.getLevelCount()){
msgBox("The entered level number isn't valid!",MsgBoxOKOnly,"Illegal number");
return;
}
}
if(GUIObjectRoot->childControls[i]->name=="lstPlacement"){
placement=GUIObjectRoot->childControls[i]->value;
}
}
//Now we execute the swap/move.
//Check for the place before.
if(placement==0){
//We place the selected level before the entered level.
levels.moveLevel(selectedNumber->getNumber(),level-1);
}else if(placement==1){
//We place the selected level after the entered level.
if(level<selectedNumber->getNumber())
levels.moveLevel(selectedNumber->getNumber(),level);
else
levels.moveLevel(selectedNumber->getNumber(),level+1);
}else if(placement==2){
//We swap the selected level with the entered level.
levels.swapLevel(selectedNumber->getNumber(),level-1);
}
//And save the change.
if(packName!="Levels")
- levels.saveLevels(getUserPath()+"custom/levelpacks/"+packName+"/levels.lst");
+ levels.saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh();
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgMoveCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
diff --git a/src/LevelSelect.cpp b/src/LevelSelect.cpp
index 6a990bb..de4fa9d 100644
--- a/src/LevelSelect.cpp
+++ b/src/LevelSelect.cpp
@@ -1,377 +1,377 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "LevelSelect.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "Game.h"
#include <SDL/SDL_ttf.h>
#include <SDL/SDL.h>
#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
////////////////////NUMBER////////////////////////
Number::Number(){
image=NULL;
number=0;
medal=0;
selected=false;
locked=false;
//Set the default dimensions.
box.x=0;
box.y=0;
box.h=50;
box.w=50;
//Load the medals image.
background=loadImage(getDataPath()+"gfx/level.png");
backgroundLocked=loadImage(getDataPath()+"gfx/levellocked.png");
medals=loadImage(getDataPath()+"gfx/medals.png");
}
Number::~Number(){
//We only need to free the SDLSurface.
if(image) SDL_FreeSurface(image);
}
void Number::init(int number,SDL_Rect box){
//First set the number and update our status.
this->number=number;
//Write our text, number+1 since the counting doens't start with 0, but with 1.
std::stringstream text;
number++;
text<<number;
//Create the text image.
SDL_Color black={0,0,0};
if(image) SDL_FreeSurface(image);
//Create the text image.
//Also check which font to use, if the number is higher than 100 use the small font.
image=TTF_RenderText_Blended(fontGUI,text.str().c_str(),black);
//Set the new location of the number.
this->box.x=box.x;
this->box.y=box.y;
}
void Number::init(std::string text,SDL_Rect box){
//First set the number and update our status.
this->number=-1;
//Create the text image.
SDL_Color black={0,0,0};
if(image) SDL_FreeSurface(image);
image=TTF_RenderText_Blended(fontGUI,text.c_str(),black);
//Set the new location of the number.
this->box.x=box.x;
this->box.y=box.y;
}
void Number::show(int dy){
//First draw the background, also apply the yOffset(dy).
if(!locked)
applySurface(box.x,box.y-dy,background,screen,NULL);
else
applySurface(box.x,box.y-dy,backgroundLocked,screen,NULL);
//Now draw the text image over the background.
//We draw it centered inside the box.
applySurface((box.x+25-(image->w/2)),box.y+((TTF_FontAscent(fontGUI)+TTF_FontDescent(fontGUI))/2)-dy,image,screen,NULL);
//Draw the selection mark.
if(selected){
drawGUIBox(box.x,box.y-dy,50,50,screen,0xFFFFFF23);
}
//Draw the medal.
if(medal>0){
SDL_Rect r={(medal-1)*30,0,30,30};
applySurface(box.x+30,(box.y+30)-dy,medals,screen,&r);
}
}
void Number::setLocked(bool locked){
this->locked=locked;
}
void Number::setMedal(int medal){
this->medal=medal;
}
/////////////////////LEVEL SELECT/////////////////////
LevelSelect::LevelSelect(string titleText){
//clear the selected level
selectedNumber=NULL;
//Load the background image.
background=loadImage(getDataPath()+"gfx/menu/background.png");
//Render the title.
SDL_Color black={0,0,0};
title=TTF_RenderText_Blended(fontTitle,titleText.c_str(),black);
//create GUI (test only)
GUIObject* obj;
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(0,0,800,600);
//the level select scroll bar
levelScrollBar=new GUIScrollBar(768,184,16,242,ScrollBarVertical,0,0,0,1,4,true,false);
GUIObjectRoot->childControls.push_back(levelScrollBar);
//level pack description
levelpackDescription=new GUIObject(60,140,800,32,GUIObjectLabel);
GUIObjectRoot->childControls.push_back(levelpackDescription);
levelpacks=new GUISingleLineListBox(150,104,500,32);
levelpacks->name="cmdLvlPack";
levelpacks->eventCallback=this;
vector<string> v=enumAllDirs(getDataPath()+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelpackLocations[*i]=getDataPath()+"levelpacks/"+*i;
}
- vector<string> v2=enumAllDirs(getUserPath()+"levelpacks/");
+ vector<string> v2=enumAllDirs(getUserPath(USER_DATA)+"levelpacks/");
for(vector<string>::iterator i=v2.begin(); i!=v2.end(); ++i){
- levelpackLocations[*i]=getUserPath()+"levelpacks/"+*i;
+ levelpackLocations[*i]=getUserPath(USER_DATA)+"levelpacks/"+*i;
}
- vector<string> v3=enumAllDirs(getUserPath()+"custom/levelpacks/");
+ vector<string> v3=enumAllDirs(getUserPath(USER_DATA)+"custom/levelpacks/");
for(vector<string>::iterator i=v3.begin(); i!=v3.end(); ++i){
- levelpackLocations[*i]=getUserPath()+"custom/levelpacks/"+*i;
+ levelpackLocations[*i]=getUserPath(USER_DATA)+"custom/levelpacks/"+*i;
}
v.insert(v.end(),v2.begin(),v2.end());
v.insert(v.end(),v3.begin(),v3.end());
//Now we add a special levelpack that will contain the levels not in a levelpack.
v.push_back("Levels");
levelpacks->item=v;
levelpacks->value=0;
//Check if we can find the lastlevelpack.
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
if(*i==getSettings()->getValue("lastlevelpack")){
levelpacks->value=i-v.begin();
- string s1=getUserPath()+"progress/"+*i+".progress";
+ string s1=getUserPath(USER_DATA)+"progress/"+*i+".progress";
//Check if this is the special Levels levelpack.
if(*i=="Levels"){
//Clear the current levels.
levels.clear();
levels.setCurrentLevel(0);
//List the custom levels and add them one for one.
- vector<string> v=enumAllFiles(getUserPath()+"custom/levels/");
+ vector<string> v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
- levels.addLevel(getUserPath()+"custom/levels/"+*i);
+ levels.addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
//List the addon levels and add them one for one.
- v=enumAllFiles(getUserPath()+"levels/");
+ v=enumAllFiles(getUserPath(USER_DATA)+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
- levels.addLevel(getUserPath()+"levels/"+*i);
+ levels.addLevel(getUserPath(USER_DATA)+"levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
}else{
//This isn't so load the levelpack in the normal way.
if(!levels.loadLevels(levelpackLocations[*i]+"/levels.lst")){
msgBox("Can't load level pack:\n"+*i,MsgBoxOKOnly,"Error");
}
}
//Load the progress.
levels.loadProgress(s1);
}
}
GUIObjectRoot->childControls.push_back(levelpacks);
obj=new GUIObject(20,20,100,32,GUIObjectButton,"Back");
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
}
LevelSelect::~LevelSelect(){
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
levelScrollBar=NULL;
levelpackDescription=NULL;
selectedNumber=NULL;
//Free the rendered title surface.
SDL_FreeSurface(title);
}
void LevelSelect::handleEvents(){
//Check for an SDL_QUIT event.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check for a mouse click.
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
checkMouse();
}
//Check if escape is pressed.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
//Check for scrolling down and up.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN && levelScrollBar){
if(levelScrollBar->value<levelScrollBar->maxValue) levelScrollBar->value++;
return;
}else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP && levelScrollBar){
if(levelScrollBar->value>0) levelScrollBar->value--;
return;
}
}
void LevelSelect::checkMouse(){
int x,y,dy=0,m=numbers.size();
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
//Check if there's a scrollbar, if so get the value.
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
if(m>dy*10+LEVELS_DISPLAYED_IN_SCREEN)
m=dy*10+LEVELS_DISPLAYED_IN_SCREEN;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
for(int n=dy*10; n<m; n++){
if(!numbers[n].getLocked()){
if(checkCollision(mouse,numbers[n].box)==true){
if(numbers[n].selected){
selectNumber(n,true);
}else{
//Select current level
for(int i=0;i<levels.getLevelCount();i++){
numbers[i].selected=(i==n);
}
selectNumber(n,false);
}
break;
}
}
}
}
void LevelSelect::logic(){}
void LevelSelect::render(){
int x,y,dy=0,m=numbers.size();
int idx=-1;
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
if(m>dy*10+LEVELS_DISPLAYED_IN_SCREEN)
m=dy*10+LEVELS_DISPLAYED_IN_SCREEN;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
//Draw the background.
applySurface(0,0,background,screen,NULL);
//Draw the title.
applySurface((800-title->w)/2,40,title,screen,NULL);
//Loop through the level blocks and draw them.
for(int n=dy*10;n<m;n++){
numbers[n].show(dy*64);
if(numbers[n].getLocked()==false && checkCollision(mouse,numbers[n].box)==true)
idx=n;
}
//Show the tool tip text.
if(idx>=0){
renderTooltip(idx,dy);
}
}
void LevelSelect::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
string s;
if(name=="cmdLvlPack"){
s=levelpackLocations[((GUISingleLineListBox*)obj)->item[obj->value]];
getSettings()->setValue("lastlevelpack",((GUISingleLineListBox*)obj)->item[obj->value]);
}else if(name=="cmdBack"){
setNextState(STATE_MENU);
return;
}else{
return;
}
- string s1=getUserPath()+"progress/"+((GUISingleLineListBox*)obj)->item[obj->value]+".progress";
+ string s1=getUserPath(USER_DATA)+"progress/"+((GUISingleLineListBox*)obj)->item[obj->value]+".progress";
//Check if this is the special Levels levelpack.
if(((GUISingleLineListBox*)obj)->item[obj->value]=="Levels"){
//Clear the current levels.
levels.clear();
levels.setCurrentLevel(0);
//List the custom levels and add them one for one.
- vector<string> v=enumAllFiles(getUserPath()+"custom/levels/");
+ vector<string> v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
- levels.addLevel(getUserPath()+"custom/levels/"+*i);
+ levels.addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
//List the addon levels and add them one for one.
- v=enumAllFiles(getUserPath()+"levels/");
+ v=enumAllFiles(getUserPath(USER_DATA)+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
- levels.addLevel(getUserPath()+"levels/"+*i);
+ levels.addLevel(getUserPath(USER_DATA)+"levels/"+*i);
levels.setLocked(levels.getLevelCount()-1);
}
}else{
//This isn't so load the levelpack in the normal way.
if(!levels.loadLevels(levelpackLocations[((GUISingleLineListBox*)obj)->item[obj->value]]+"/levels.lst")){
msgBox("Can't load level pack:\n"+((GUISingleLineListBox*)obj)->item[obj->value],MsgBoxOKOnly,"Error");
}
}
//Load the progress file.
levels.loadProgress(s1);
//And refresh the numbers.
refresh();
}
diff --git a/src/Main.cpp b/src/Main.cpp
index c39ed3b..a68322c 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -1,179 +1,179 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "Functions.h"
#include "Timer.h"
#include "Objects.h"
#include "Globals.h"
#include "TitleMenu.h"
#include "GUIObject.h"
#include "InputManager.h"
#include "MD5.h"
#include <SDL/SDL.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
//Variables for recording.
//To enable picture recording uncomment the next line:
//#define RECORD_PICUTRE_SEQUENCE
#ifdef RECORD_PICUTRE_SEQUENCE
bool recordPictureSequence=false;
int recordPictureIndex=0;
#endif
int main(int argc, char** argv) {
#ifdef _MSC_VER
//Fix the non-latin file name bug under Visual Studio
setlocale(LC_ALL,"");
#endif
//First parse the comand line arguments.
if(!parseArguments(argc,argv)){
printf("Usage: %s [OPTIONS] ...\n",argv[0]);
printf("Avaliable options:\n");
printf(" %-20s %s\n","--data-dir <dir>","Specifies the data directory.");
printf(" %-20s %s\n","--user-dir <dir>","Specifies the user preferences directory.");
printf(" %-20s %s\n","--version","Display the version and quit.");
printf(" %-20s %s\n","--help","Display this help.");
return 0;
}
//Initialise some stuff like SDL, the window, SDL_Mixer.
if(init()==false) {
fprintf(stderr,"FATAL ERROR: Failed to initalize game.\n");
return 1;
}
//Try to configure the dataPath, userPath, etc...
if(configurePaths()==false){
fprintf(stderr,"FATAL ERROR: Failed to configure paths.\n");
return 1;
}
//Load some important files like the background music, default theme.
if(loadFiles()==false){
fprintf(stderr,"FATAL ERROR: Failed to load necessary files.\n");
return 1;
}
//Load the settings.
if(loadSettings()==false){
fprintf(stderr,"FATAL ERROR: Failed to load config file.\n");
return 1;
}
//Load key config. Then initalize joystick support.
inputMgr.loadConfig();
inputMgr.openAllJoysitcks();
//Load the configured music list.
getMusicManager()->loadMusicList((getDataPath()+"music/"+getSettings()->getValue("musiclist")+".list"));
getMusicManager()->setMusicList(getSettings()->getValue("musiclist"));
//Set the currentState id to the main menu and create it.
stateID=STATE_MENU;
currentState=new Menu();
//Seed random.
srand((unsigned)time(NULL));
//Check if sound is enabled.
if(getSettings()->getBoolValue("music"))
getMusicManager()->setEnabled();
//Check if we should go fullscreen.
if(getSettings()->getBoolValue("fullscreen"))
SDL_SetVideoMode(screen->w,screen->h,screen->format->BitsPerPixel, SDL_FULLSCREEN | SDL_HWSURFACE);
tempSurface=SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
screen->w,screen->h,screen->format->BitsPerPixel,
screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,0);
int fadeIn=0;
//Start the game loop.
while(stateID!=STATE_EXIT){
//We start the timer.
FPS.start();
//Loop the SDL events.
while(SDL_PollEvent(&event)){
#ifdef RECORD_PICUTRE_SEQUENCE
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_F10){
recordPictureSequence=!recordPictureSequence;
printf("Record Picture Sequence %s\n",recordPictureSequence?"ON":"OFF");
}
#endif
//Let the input manager handle the events.
inputMgr.updateState(true);
//Let the currentState handle the events.
currentState->handleEvents();
//Also pass the events to the GUI.
GUIObjectHandleEvents();
}
//update input state (??)
inputMgr.updateState(false);
//Now it's time for the state to do his logic.
currentState->logic();
currentState->render();
//TODO: Shouldn't the gamestate take care of rendering the GUI?
if(GUIObjectRoot) GUIObjectRoot->render();
if(fadeIn>0&&fadeIn<255){
SDL_BlitSurface(screen,NULL,tempSurface,NULL);
SDL_FillRect(screen,NULL,0);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA, fadeIn);
SDL_BlitSurface(tempSurface,NULL,screen,NULL);
fadeIn+=17;
}
#ifdef RECORD_PICUTRE_SEQUENCE
if(recordPictureSequence){
char s[64];
recordPictureIndex++;
sprintf(s,"pic%08d.bmp",recordPictureIndex);
printf("Save screen to %s\n",s);
- SDL_SaveBMP(screen,(getUserPath()+s).c_str());
+ SDL_SaveBMP(screen,(getUserPath(USER_CACHE)+s).c_str());
}
#endif
SDL_Flip(screen);
if(nextState!=STATE_NULL){
fadeIn=17;
changeState();
}
int t=FPS.getTicks();
t=(1000/g_FPS)-t;
if(t>0){
SDL_Delay(t);
}
}
//close all joysticks.
inputMgr.closeAllJoysticks();
//The game has ended, save the settings just to be sure.
saveSettings();
SDL_FreeSurface(tempSurface);
levels.saveLevelProgress();
clean();
//End of program.
return 0;
}
diff --git a/src/TitleMenu.cpp b/src/TitleMenu.cpp
index aa44877..c9a5c60 100644
--- a/src/TitleMenu.cpp
+++ b/src/TitleMenu.cpp
@@ -1,413 +1,413 @@
/****************************************************************************
** Copyright (C) 2011 Luka Horvat <redreaper132 at gmail.com>
** Copyright (C) 2011 Edward Lii <edward_iii at myway.com>
** Copyright (C) 2011 O. Bahri Gordebak <gordebak at gmail.com>
**
**
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
#include "Functions.h"
#include "GameState.h"
#include "Globals.h"
#include "TitleMenu.h"
#include "GUIListBox.h"
#include "InputManager.h"
#include <iostream>
#include <algorithm>
using namespace std;
/////////////////////////MAIN_MENU//////////////////////////////////
//Integer containing the highlighted/selected menu option.
static int highlight=0;
Menu::Menu(){
highlight=0;
animation=0;
//Load the background and the title image.
background=loadImage(getDataPath()+"gfx/menu/background.png");
title=loadImage(getDataPath()+"gfx/menu/title.png");
//Now render the five entries.
SDL_Color black={0,0,0};
entries[0]=TTF_RenderText_Blended(fontTitle,"Play",black);
entries[1]=TTF_RenderText_Blended(fontTitle,"Options",black);
entries[2]=TTF_RenderText_Blended(fontTitle,"Map Editor",black);
entries[3]=TTF_RenderText_Blended(fontTitle,"Addons",black);
entries[4]=TTF_RenderText_Blended(fontTitle,"Exit",black);
entries[5]=TTF_RenderText_Blended(fontTitle,">",black);
entries[6]=TTF_RenderText_Blended(fontTitle,"<",black);
}
Menu::~Menu(){
//We need to free the five text surfaceses.
for(unsigned int i=0;i<7;i++)
SDL_FreeSurface(entries[i]);
}
void Menu::handleEvents(){
//Get the x and y location of the mouse.
int x,y;
SDL_GetMouseState(&x,&y);
//Calculate which option is highlighted using the location of the mouse.
//Only if mouse is 'doing something'
if(event.type==SDL_MOUSEMOTION || event.type==SDL_MOUSEBUTTONDOWN){
if(x>=200&&x<600&&y>=200&&y<520){
highlight=(y-136)/64;
}
}
//Down/Up -arrows move highlight
if(inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
highlight++;
if(highlight>=6)
highlight=5;
}
if(inputMgr.isKeyDownEvent(INPUTMGR_UP)){
highlight--;
if(highlight<1)
highlight=1;
}
//Check if there's a press event.
if((event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT) ||
(event.type==SDL_KEYUP && (event.key.keysym.sym==SDLK_RETURN || event.key.keysym.sym==SDLK_KP_ENTER))){
//We have one so check which selected/highlighted option needs to be done.
switch(highlight){
case 1:
//Enter the levelSelect state.
setNextState(STATE_LEVEL_SELECT);
break;
case 2:
//Enter the options state.
setNextState(STATE_OPTIONS);
break;
case 3:
//Enter the levelEditor, but first set the level to a default leveledit map.
levelName="";
setNextState(STATE_LEVEL_EDIT_SELECT);
break;
case 4:
//Check if internet is enabled.
if(!getSettings()->getBoolValue("internet")){
msgBox("Enable internet in order to install addons.",MsgBoxOKOnly,"Internet disabled");
break;
}
//Enter the help state.
setNextState(STATE_ADDONS);
break;
case 5:
//We quit, so we enter the exit state.
setNextState(STATE_EXIT);
break;
}
}
//Check if we need to quit, if so we enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
}
//Nothing to do here
void Menu::logic(){
animation++;
if(animation>10)
animation=-10;
}
void Menu::render(){
applySurface(0,0,background,screen,NULL);
//Draw the title.
applySurface(90,40,title,screen,NULL);
//Draw the menu entries.
for(unsigned int i=0;i<5;i++){
applySurface((800-entries[i]->w)/2,200+64*i+(64-entries[i]->h)/2,entries[i],screen,NULL);
}
//Check if an option is selected/highlighted.
if(highlight>0){
//Draw the '>' sign, which is entry 5.
int x=(800-entries[highlight-1]->w)/2-(25-abs(animation)/2)-entries[5]->w;
int y=136+64*highlight+(64-entries[5]->h)/2;
applySurface(x,y,entries[5],screen,NULL);
//Draw the '<' sign, which is entry 6.
x=(800-entries[highlight-1]->w)/2+entries[highlight-1]->w+(25-abs(animation)/2);
y=136+64*highlight+(64-entries[6]->h)/2;
applySurface(x,y,entries[6],screen,NULL);
}
}
/////////////////////////OPTIONS_MENU//////////////////////////////////
//Some varables for the options.
static bool sound,music,fullscreen,leveltheme,internet;
static string themeName;
static bool useProxy;
static string internetProxy;
static bool restartFlag;
Options::Options(){
//Load the background image.
background=loadImage(getDataPath()+"gfx/menu/background.png");
//Render the title.
SDL_Color black={0,0,0};
title=TTF_RenderText_Blended(fontTitle,"Settings",black);
//Set some default settings.
music=getSettings()->getBoolValue("music");
sound=getSettings()->getBoolValue("sound");
fullscreen=getSettings()->getBoolValue("fullscreen");
themeName=processFileName(getSettings()->getValue("theme"));
leveltheme=getSettings()->getBoolValue("leveltheme");
internet=getSettings()->getBoolValue("internet");
internetProxy=getSettings()->getValue("internet-proxy");
useProxy=!internetProxy.empty();
//Set the restartFlag false.
restartFlag=false;
//Create the root element of the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GUIObjectNone);
//Now we create GUIObjects for every option.
GUIObject *obj=new GUIObject(150,150,240,36,GUIObjectCheckBox,"Music",music?1:0);
obj->name="chkMusic";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(150,190,240,36,GUIObjectCheckBox,"Sound",sound?1:0);
obj->name="chkSound";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(150,230,240,36,GUIObjectCheckBox,"Fullscreen",fullscreen?1:0);
obj->name="chkFullscreen";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(150,270,240,36,GUIObjectLabel,"Theme:");
obj->name="theme";
GUIObjectRoot->childControls.push_back(obj);
//Create the theme option gui element.
theme=new GUISingleLineListBox(370,270,300,36);
theme->name="lstTheme";
- vector<string> v=enumAllDirs(getUserPath()+"themes/");
+ vector<string> v=enumAllDirs(getUserPath(USER_DATA)+"themes/");
for(vector<string>::iterator i = v.begin(); i != v.end(); ++i){
- themeLocations[*i]=getUserPath()+"themes/"+*i;
+ themeLocations[*i]=getUserPath(USER_DATA)+"themes/"+*i;
}
vector<string> v2=enumAllDirs(getDataPath()+"themes/");
for(vector<string>::iterator i = v2.begin(); i != v2.end(); ++i){
themeLocations[*i]=getDataPath()+"themes/"+*i;
}
v.insert(v.end(), v2.begin(), v2.end());
//Try to find the configured theme so we can display it.
int value=-1;
for(vector<string>::iterator i = v.begin(); i != v.end(); ++i){
if(themeLocations[*i]==themeName) {
value=i-v.begin();
}
}
theme->item=v;
if(value==-1)
value=theme->item.size()-1;
theme->value=value;
//NOTE: We call the event handling method to correctly set the themename.
GUIEventCallback_OnEvent("lstTheme",theme,GUIEventChange);
theme->eventCallback=this;
GUIObjectRoot->childControls.push_back(theme);
obj=new GUIObject(150,310,240,36,GUIObjectCheckBox,"Level themes",leveltheme?1:0);
obj->name="chkLeveltheme";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(150,350,240,36,GUIObjectCheckBox,"Internet",internet?1:0);
obj->name="chkInternet";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//new: proxy settings
obj=new GUIObject(150,390,240,36,GUIObjectLabel,"Internet proxy");
obj->name="chkProxy";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(370,390,300,36,GUIObjectTextBox,internetProxy.c_str());
obj->name="txtProxy";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//new: key settings
obj=new GUIObject(150,430,240,36,GUIObjectButton,"Config Keys");
obj->name="cmdKeys";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
//Reset progress settings.
obj=new GUIObject(410,430,240,36,GUIObjectButton,"Clear Progress");
obj->name="cmdReset";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(100,520,284,36,GUIObjectButton,"Cancel");
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
obj=new GUIObject(400,520,284,36,GUIObjectButton,"Save Changes");
obj->name="cmdSave";
obj->eventCallback=this;
GUIObjectRoot->childControls.push_back(obj);
}
Options::~Options(){
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//And free the title image.
SDL_FreeSurface(title);
}
void Options::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
//Check what type of event it was.
if(eventType==GUIEventClick){
if(name=="cmdBack"){
//TODO: Reset the key changes.
//And goto the main menu.
setNextState(STATE_MENU);
}else if(name=="cmdSave"){
//Save is pressed thus save
getSettings()->setValue("sound",sound?"1":"0");
getSettings()->setValue("music",music?"1":"0");
getMusicManager()->setEnabled(music);
getSettings()->setValue("fullscreen",fullscreen?"1":"0");
getSettings()->setValue("leveltheme",leveltheme?"1":"0");
getSettings()->setValue("internet",internet?"1":"0");
getSettings()->setValue("theme",themeName);
if(!useProxy)
internetProxy.clear();
getSettings()->setValue("internet-proxy",internetProxy);
//Save the key configuration.
inputMgr.saveConfig();
//Save the settings.
saveSettings();
//Before we return show a restart message, if needed.
if(restartFlag)
msgBox("Restart needed before the changes have effect.",MsgBoxOKOnly,"Restart needed");
//Now return to the main menu.
setNextState(STATE_MENU);
}else if(name=="cmdKeys"){
inputMgr.showConfig();
}else if(name=="cmdReset"){
if(msgBox("Do you really want to reset level progress?",MsgBoxYesNo,"Warning")==MsgBoxYes){
//We delete the progress folder.
#ifdef WIN32
removeDirectory((getUserPath()+"progress").c_str());
createDirectory((getUserPath()+"progress").c_str());
#else
- removeDirectory((getUserPath()+"/progress").c_str());
- createDirectory((getUserPath()+"/progress").c_str());
+ removeDirectory((getUserPath(USER_DATA)+"/progress").c_str());
+ createDirectory((getUserPath(USER_DATA)+"/progress").c_str());
#endif
}
return;
}else if(name=="chkMusic"){
music=obj->value?true:false;
}else if(name=="chkSound"){
sound=obj->value?true:false;
}else if(name=="chkFullscreen"){
fullscreen=obj->value?true:false;
//Check if fullscreen changed.
if(fullscreen==getSettings()->getBoolValue("fullscreen")){
//We disable the restart message flag.
restartFlag=false;
}else{
//We set the restart message flag.
restartFlag=true;
}
}else if(name=="chkLeveltheme"){
leveltheme=obj->value?true:false;
}else if(name=="chkInternet"){
internet=obj->value?true:false;
}else if(name=="chkProxy"){
useProxy=obj->value?true:false;
}
}
if(name=="lstTheme"){
if(theme!=NULL && theme->value>=0 && theme->value<(int)theme->item.size()){
//Check if the theme is installed in the data path.
if(themeLocations[theme->item[theme->value]].find(getDataPath())!=string::npos){
themeName="%DATA%/themes/"+fileNameFromPath(themeLocations[theme->item[theme->value]]);
- }else if(themeLocations[theme->item[theme->value]].find(getUserPath())!=string::npos){
+ }else if(themeLocations[theme->item[theme->value]].find(getUserPath(USER_DATA))!=string::npos){
themeName="%USER%/themes/"+fileNameFromPath(themeLocations[theme->item[theme->value]]);
}else{
themeName=themeLocations[theme->item[theme->value]];
}
}
}else if(name=="txtProxy"){
internetProxy=obj->caption;
//Check if the internetProxy field is empty.
useProxy=!internetProxy.empty();
}
}
void Options::handleEvents(){
//Check if we need to quit, if so enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check if the escape button is pressed, if so go back to the main menu.
if(inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
}
//Nothing to do here.
void Options::logic(){}
void Options::render(){
//Render the background image.
applySurface(0,0,background,screen,NULL);
//Now render the title.
applySurface((800-title->w)/2,40,title,screen,NULL);
//NOTE: The rendering of the GUI is done in Main.
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 16, 2:05 AM (2 w, 13 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
72899
Default Alt Text
(132 KB)

Event Timeline