• Main Page
  • Related Pages
  • Classes
  • Files
  • File List
  • File Members

gamedata.cc

Go to the documentation of this file.
00001 /*
00002    $Id: gamedata.cc,v 1.29 2003/01/12 23:24:29 ksterker Exp $
00003 
00004    Copyright (C) 2001/2002 by Kai Sterker <kaisterker@linuxgames.com>
00005    Part of the Adonthell Project http://adonthell.linuxgames.com
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 
00016 /**
00017  * @file   gamedata.cc
00018  * @author Kai Sterker <kaisterker@linuxgames.com>
00019  * 
00020  * @brief  Defines the gamedata and data classes.
00021  * 
00022  * 
00023  */ 
00024 
00025 
00026 #include <iostream> 
00027 #include <cstdio>
00028 #include <time.h>
00029 #include <unistd.h>
00030 #include <dirent.h>
00031 #include <sys/stat.h>
00032 
00033 #include "audio.h"
00034 #include "gamedata.h"
00035 #include "python_class.h"
00036 #include "event_handler.h"
00037 
00038 // File format versions of the various data files
00039 // *** Increase when changing file format! ***
00040 #define ENGINE_DAT_VER  5
00041 #define AUDIO_DAT_VER   2
00042 #define CHAR_DAT_VER    4
00043 #define QUEST_DAT_VER   1
00044 #define SAVE_DAT_VER    3
00045 
00046 vector<gamedata*> gamedata::saves;      // The list of available savegames
00047 string gamedata::user_data_dir_;        // The user's private adonthell directory
00048 string gamedata::game_data_dir_;        // The adonthell data directory
00049 string gamedata::game_name;             // The adonthell data directory
00050 u_int8 gamedata::quick_load;            // Whether Quick-load is active or not
00051 
00052 using namespace std; 
00053 
00054 
00055 gamedata::gamedata ()
00056 {
00057 }
00058 
00059 gamedata::gamedata (string dir, string desc, string time)
00060 {
00061     Timestamp = 0;
00062     Directory = dir;
00063     Description = desc;
00064     Gametime = time;
00065 }
00066 
00067 gamedata::~gamedata ()
00068 {
00069 }
00070 
00071 bool gamedata::get (igzstream& file)
00072 {
00073     if (!fileops::get_version (file, SAVE_DAT_VER, SAVE_DAT_VER, "save.data"))
00074         return false;
00075     
00076     Timestamp << file; 
00077     Description << file;
00078     Location << file;
00079     Gametime << file; 
00080     
00081     return true;
00082 }
00083 
00084 void gamedata::put (ogzstream& file)
00085 {
00086     fileops::put_version (file, SAVE_DAT_VER);
00087     
00088     // get current time for Quick-Loading
00089     Timestamp = time (NULL);
00090     
00091     Timestamp >> file;
00092     Description >> file;
00093     Location >> file;
00094     Gametime >> file; 
00095 }
00096 
00097 void gamedata::set_description (string desc)
00098 {
00099     Description = desc; 
00100 }
00101 
00102 void gamedata::set_directory (string dir)
00103 {
00104     Directory = dir;
00105 }
00106 
00107 void gamedata::set_gametime (string time)
00108 {
00109     Gametime = time;
00110 }
00111 
00112 bool gamedata::load_characters (u_int32 pos)
00113 {
00114     igzstream in;
00115     
00116     string filepath;
00117     character *mynpc;
00118 
00119     // try to open character.data
00120     filepath = saves[pos]->directory ();
00121     filepath += "/character.data";     
00122     in.open (filepath);
00123     
00124     if (!in.is_open ())
00125     {
00126         cerr << "Couldn't open \"" << filepath << "\" - stopping\n" <<  endl; 
00127         return false;
00128     }
00129     
00130     if (!fileops::get_version (in, CHAR_DAT_VER, CHAR_DAT_VER, filepath))
00131         return false;
00132     
00133     // load characters     
00134     char ctemp;
00135 
00136     // first, the player
00137     data::the_player = new character ();
00138     data::the_player->character_base::get_state (in);
00139     data::characters[data::the_player->get_id ().c_str ()] = data::the_player;
00140 
00141     // then all the others
00142     while (ctemp << in)
00143     {
00144         mynpc = new character;
00145         mynpc->character_base::get_state (in);
00146  
00147         // Make this character available to the engine
00148         data::characters[mynpc->get_id ().c_str ()] = mynpc;
00149     }
00150     in.close ();
00151 
00152     return true; 
00153 }
00154 
00155 bool gamedata::load_quests (u_int32 pos) 
00156 {
00157     igzstream in;
00158     
00159     string filepath;
00160     quest *myquest;
00161     
00162     // try to open quest.data
00163     filepath = saves[pos]->directory ();
00164     filepath += "/quest.data"; 
00165     in.open (filepath); 
00166 
00167     if (!in.is_open ())
00168     {
00169         cerr <<  "Couldn't open \"" << filepath << " - stopping\n" << endl; 
00170         return false;
00171     }
00172     
00173     if (!fileops::get_version (in, QUEST_DAT_VER, QUEST_DAT_VER, filepath))
00174         return false;
00175     
00176     // load quests
00177     char ctemp;
00178     while (ctemp << in) 
00179     {
00180         myquest = new quest;
00181         myquest->load (in);
00182 
00183         // Make this quest available to the engine
00184         data::quests[myquest->name.c_str ()] = myquest;
00185     }
00186 
00187     in.close ();
00188     
00189     return true; 
00190 }
00191 
00192 bool gamedata::load_mapengine (u_int32 pos) 
00193 {
00194     igzstream in;
00195     
00196     string filepath;
00197 
00198     // Load mapengine state
00199     filepath = saves[pos]->directory(); 
00200     filepath += "/mapengine.data";
00201     in.open (filepath); 
00202     
00203     if (!in.is_open ())
00204     {
00205         cerr <<  "Couldn't open \"" << filepath << " - stopping\n" << endl; 
00206         return false;
00207     }
00208     
00209     if (!fileops::get_version (in, ENGINE_DAT_VER, ENGINE_DAT_VER, filepath))
00210         return false;
00211 
00212     if (!data::engine->get_state(in))
00213     {
00214         cerr << "Couldn't load \"" << filepath << " - stopping\n" << endl;
00215         return false;
00216     }
00217     
00218     in.close (); 
00219 
00220     return true; 
00221 }
00222 
00223 bool gamedata::load_audio (u_int32 pos)
00224 {
00225     igzstream in;
00226     string filepath;
00227 
00228     // Load mapengine state
00229     filepath = saves[pos]->directory(); 
00230     filepath += "/audio.data";
00231     in.open (filepath); 
00232     
00233     if (!in.is_open ())
00234     {
00235         cerr <<  "Couldn't open \"" << filepath << " - stopping\n" << endl; 
00236         return false;
00237     }
00238     
00239     if (!fileops::get_version (in, AUDIO_DAT_VER, AUDIO_DAT_VER, filepath))
00240         return false;
00241 
00242     if (!audio::get_state (in))
00243     {
00244         cerr << "Couldn't load \"" << filepath << " - stopping\n" << endl;
00245         return false;
00246     }
00247     
00248     in.close (); 
00249 
00250     return true; 
00251 }
00252 
00253 bool gamedata::load (u_int32 pos)
00254 {
00255     // First, unload the current game
00256     unload ();
00257     
00258     if (!load_characters (pos)) return false;
00259     if (!load_quests (pos)) return false;
00260     if (!load_mapengine (pos)) return false; 
00261     if (!load_audio (pos)) return false;
00262     
00263     return true; 
00264 }
00265 
00266 bool gamedata::load_newest ()
00267 {
00268     // Quick-load off / no save game available
00269     if (!quick_load || saves.size () <= 1) return false;
00270     
00271     u_int32 timestamp = 0;
00272     u_int32 index = 0;
00273     u_int32 newest;
00274     
00275     for (vector<gamedata*>::iterator i = saves.begin (); i != saves.end (); i++)
00276     {
00277         if ((*i)->timestamp () > timestamp)
00278         {
00279             timestamp = (*i)->timestamp ();
00280             newest = index;
00281         }
00282         
00283         index++; 
00284     }
00285     
00286     return load (newest);
00287 }
00288 
00289 bool gamedata::save (u_int32 pos, string desc, string time)
00290 {
00291     gamedata *gdata;
00292     string filepath;
00293     char t[10]; 
00294     ogzstream file; 
00295     char vnbr; 
00296     
00297     // make sure we don't overwrite the default game
00298     if (pos == 0) return false;
00299     
00300     // see whether we're going to save to a new slot
00301     if (pos >= saves.size ())
00302     {
00303         int success = 1;
00304         
00305         // make sure we save to an unused directory
00306         while (success)
00307         {
00308             // that's the directory we're going to save to
00309             sprintf(t, "%03i", pos++);
00310             filepath = user_data_dir ();
00311             filepath += "/" + game_name + "-save-";
00312             filepath += t;
00313             
00314 #ifdef WIN32
00315             success = mkdir (filepath.c_str());
00316 #else
00317             success = mkdir (filepath.c_str(), 0700);
00318 #endif
00319             
00320             // prevent infinite loop if we can't write to the directory
00321             if (pos >= 1000) 
00322             {
00323                 cerr << "Save failed - seems like you have no write permission in\n"
00324                      << user_data_dir () << endl; 
00325                 return false;
00326             }
00327         }
00328         
00329         // we'll need a new gamedata record
00330         gdata = new gamedata (filepath, desc, time);
00331     }
00332     else
00333     {
00334         gdata = saves[pos];
00335         gdata->set_description (desc);
00336         gdata->set_gametime (time);
00337     }
00338 
00339     // save characters 
00340     filepath = gdata->directory ();
00341     filepath += "/character.data"; 
00342     file.open (filepath); 
00343 
00344     if (!file.is_open ())
00345     {
00346         cerr << "Couldn't create \"" << filepath << "\" - save failed\n"; 
00347         return false;
00348     }
00349     
00350     fileops::put_version (file, CHAR_DAT_VER);
00351 
00352     // save the player first
00353     data::the_player->character_base::put_state (file);
00354 
00355     // now save all the other characters
00356     dictionary <character *>::iterator itc;
00357     for (itc = data::characters.begin (); itc != data::characters.end (); itc++) 
00358     {
00359         // don't save the player
00360         if (itc->second == (character*) data::the_player) continue;
00361         
00362         // tell the character.data loader that another entry follows
00363         vnbr = 1; 
00364         vnbr >> file; 
00365         
00366         // append the character data
00367         itc->second->character_base::put_state (file);
00368     }
00369 
00370     // write EOF
00371     vnbr = 0; 
00372     vnbr >> file;
00373     file.close (); 
00374 
00375     // save quests
00376     filepath = gdata->directory ();
00377     filepath += "/quest.data"; 
00378     file.open (filepath); 
00379     
00380     if (!file.is_open ())
00381     {
00382         cerr << "Couldn't create \"" << filepath << "\" - save failed\n";
00383         return false;
00384     }
00385 
00386     fileops::put_version (file, QUEST_DAT_VER);
00387 
00388     dictionary <quest *>::iterator itq;
00389     for (itq = data::quests.begin (); itq != data::quests.end (); itq++)
00390     {
00391         // tell the quest.data loader that another entry follows
00392         vnbr = 1;
00393         vnbr >> file; 
00394         
00395         // append the character data
00396         itq->second->save (file);
00397     }
00398 
00399     // write EOF
00400     vnbr = 0; 
00401     vnbr >> file; 
00402     file.close ();
00403     
00404     // Save mapengine state
00405     filepath = gdata->directory(); 
00406     filepath += "/mapengine.data";
00407     file.open (filepath); 
00408     
00409     if (!file.is_open ())
00410     {
00411         cerr << "Couldn't create \"" << filepath << "\" - save failed\n"; 
00412         return false;
00413     }
00414 
00415     fileops::put_version (file, ENGINE_DAT_VER);
00416     data::engine->put_state(file);
00417     file.close (); 
00418 
00419     // save music
00420     filepath = gdata->directory ();
00421     filepath += "/audio.data";
00422     file.open (filepath); 
00423 
00424     if (!file.is_open ())
00425     {
00426         cerr << "Couldn't create \"" << filepath << "\" - save failed\n"; 
00427         return false;
00428     }
00429     
00430     fileops::put_version (file, AUDIO_DAT_VER);
00431     audio::put_state (file);
00432     file.close ();
00433     
00434     // save gamedata
00435     filepath = gdata->directory ();
00436     filepath += "/save.data"; 
00437 
00438     file.open (filepath); 
00439     if (!file.is_open ())
00440     {
00441         cerr << "Couldn't create \"" << filepath << "\" - save failed\n"; 
00442         return false;
00443     }
00444     
00445     gdata->put (file);
00446     file.close ();
00447     
00448     // only now it is safe to add the new record to the array
00449     if (pos >= saves.size ()) saves.push_back (gdata);
00450     
00451     return true;  
00452 }
00453 
00454 gamedata* gamedata::next_save ()
00455 {
00456     static vector<gamedata*>::iterator i = saves.begin ();
00457     static u_int32 size = saves.size ();
00458     
00459     // check whether a new save has been added
00460     if (size != saves.size ())
00461     {
00462         size = saves.size ();
00463         i = saves.begin ();
00464     }
00465     
00466     // check whether we reached the end of the list
00467     if (++i == saves.end ())
00468     {
00469         i = saves.begin ();
00470         return NULL;
00471     }
00472     
00473     return *i;
00474 }
00475 
00476 
00477 bool gamedata::init (string udir, string gdir, string gname, u_int8 qload)
00478 {
00479     DIR *dir;
00480     igzstream in;
00481     struct dirent *dirent;
00482     struct stat statbuf;
00483     gamedata *gdata;
00484  
00485     user_data_dir_ = udir; 
00486     game_data_dir_ = gdir; 
00487     game_name = gname; 
00488     quick_load = qload;
00489     
00490     // try to change into data directory
00491     if (chdir (game_data_dir ().c_str ()))
00492     {
00493         fprintf (stderr, "Seems like %s is no valid data directory.\n", game_data_dir ().c_str ());
00494         fprintf (stderr, "Please make sure that your Adonthell installation is correct.\n"); 
00495         return false;
00496     }
00497 
00498     // Add the default savegame used to start a new game to the list of saves
00499     gdata = new gamedata (gdir, "Start New Game", "Day 0 - 00:00");
00500     saves.push_back (gdata);
00501 
00502     // Read the user's saved games (if any) - they'll be located in
00503     // $HOME/.adonthell/ and called <game_name>-save-<xxx>
00504     if ((dir = opendir (user_data_dir ().c_str ())) != NULL)
00505     {
00506         while ((dirent = readdir (dir)) != NULL)
00507         {
00508             string filepath = user_data_dir () + "/";
00509             filepath += dirent->d_name; 
00510 
00511             string name_save = game_name + "-save-";
00512             
00513             if (stat (filepath.c_str (), &statbuf) != -1 && S_ISDIR (statbuf.st_mode) && 
00514                 strncmp (name_save.c_str (), dirent->d_name, name_save.length ()) == 0)
00515             {
00516                 // found a (possibly) valid saved game directory
00517                 filepath += "/save.data"; 
00518                 // Now try to read the saved game's data record
00519                 in.open (filepath); 
00520                 
00521                 if (in.is_open ())
00522                 {
00523                     // restore the pathname
00524                     filepath = user_data_dir ();
00525                     filepath += "/";
00526                     filepath += dirent->d_name;
00527                     
00528                     gdata = new gamedata;
00529                     if (gdata->get (in))
00530                     {
00531                         gdata->set_directory (filepath);
00532                         saves.push_back (gdata);
00533                     }
00534                     else delete gdata;
00535                     
00536                     in.close (); 
00537                 }
00538             }
00539         } 
00540         closedir (dir);
00541     }    
00542     return true; 
00543 }
00544 
00545 void gamedata::cleanup () 
00546 { 
00547     for (vector<gamedata*>::iterator i = saves.begin (); i != saves.end (); i++)
00548         delete *i;
00549     saves.clear (); 
00550     unload (); 
00551 }
00552 
00553 void gamedata::unload () 
00554 {
00555     // stop the music
00556     audio::fade_out_background (500);
00557     
00558     // delete all characters
00559     dictionary <character *>::iterator itc;
00560     for (itc = data::characters.begin (); itc != data::characters.end (); itc++)
00561     {
00562         itc->second->remove_from_map (); 
00563         delete itc->second;
00564     }
00565     data::characters.clear (); 
00566     
00567     data::the_player = NULL;
00568 
00569     // delete all quests
00570     dictionary <quest *>::iterator itq;
00571     for (itq = data::quests.begin (); itq != data::quests.end (); itq++) 
00572         delete itq->second;
00573     data::quests.clear ();
00574 }

Generated on Tue Jul 27 2010 for Adonthell by  doxygen 1.7.1