Adonthell  0.4
gamedata.cc
Go to the documentation of this file.
1 /*
2  $Id: gamedata.cc,v 1.29 2003/01/12 23:24:29 ksterker Exp $
3 
4  Copyright (C) 2001/2002 by Kai Sterker <kaisterker@linuxgames.com>
5  Part of the Adonthell Project http://adonthell.linuxgames.com
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 
16 /**
17  * @file gamedata.cc
18  * @author Kai Sterker <kaisterker@linuxgames.com>
19  *
20  * @brief Defines the gamedata and data classes.
21  *
22  *
23  */
24 
25 
26 #include <iostream>
27 #include <cstdio>
28 #include <time.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <sys/stat.h>
32 
33 #include "audio.h"
34 #include "gamedata.h"
35 #include "python_class.h"
36 #include "event_handler.h"
37 
38 // File format versions of the various data files
39 // *** Increase when changing file format! ***
40 #define ENGINE_DAT_VER 5
41 #define AUDIO_DAT_VER 2
42 #define CHAR_DAT_VER 4
43 #define QUEST_DAT_VER 1
44 #define SAVE_DAT_VER 3
45 
46 vector<gamedata*> gamedata::saves; // The list of available savegames
47 string gamedata::user_data_dir_; // The user's private adonthell directory
48 string gamedata::game_data_dir_; // The adonthell data directory
49 string gamedata::game_name; // The adonthell data directory
50 u_int8 gamedata::quick_load; // Whether Quick-load is active or not
51 
52 using namespace std;
53 
54 
56 {
57 }
58 
59 gamedata::gamedata (string dir, string desc, string time)
60 {
61  Timestamp = 0;
62  Directory = dir;
63  Description = desc;
64  Gametime = time;
65 }
66 
68 {
69 }
70 
72 {
73  if (!fileops::get_version (file, SAVE_DAT_VER, SAVE_DAT_VER, "save.data"))
74  return false;
75 
76  Timestamp << file;
77  Description << file;
78  Location << file;
79  Gametime << file;
80 
81  return true;
82 }
83 
85 {
86  fileops::put_version (file, SAVE_DAT_VER);
87 
88  // get current time for Quick-Loading
89  Timestamp = time (NULL);
90 
91  Timestamp >> file;
92  Description >> file;
93  Location >> file;
94  Gametime >> file;
95 }
96 
97 void gamedata::set_description (string desc)
98 {
99  Description = desc;
100 }
101 
102 void gamedata::set_directory (string dir)
103 {
104  Directory = dir;
105 }
106 
107 void gamedata::set_gametime (string time)
108 {
109  Gametime = time;
110 }
111 
113 {
114  igzstream in;
115 
116  string filepath;
117  character *mynpc;
118 
119  // try to open character.data
120  filepath = saves[pos]->directory ();
121  filepath += "/character.data";
122  in.open (filepath);
123 
124  if (!in.is_open ())
125  {
126  cerr << "Couldn't open \"" << filepath << "\" - stopping\n" << endl;
127  return false;
128  }
129 
130  if (!fileops::get_version (in, CHAR_DAT_VER, CHAR_DAT_VER, filepath))
131  return false;
132 
133  // load characters
134  char ctemp;
135 
136  // first, the player
137  data::the_player = new character ();
138  data::the_player->character_base::get_state (in);
139  data::characters[data::the_player->get_id ().c_str ()] = data::the_player;
140 
141  // then all the others
142  while (ctemp << in)
143  {
144  mynpc = new character;
145  mynpc->character_base::get_state (in);
146 
147  // Make this character available to the engine
148  data::characters[mynpc->get_id ().c_str ()] = mynpc;
149  }
150  in.close ();
151 
152  return true;
153 }
154 
156 {
157  igzstream in;
158 
159  string filepath;
160  quest *myquest;
161 
162  // try to open quest.data
163  filepath = saves[pos]->directory ();
164  filepath += "/quest.data";
165  in.open (filepath);
166 
167  if (!in.is_open ())
168  {
169  cerr << "Couldn't open \"" << filepath << " - stopping\n" << endl;
170  return false;
171  }
172 
173  if (!fileops::get_version (in, QUEST_DAT_VER, QUEST_DAT_VER, filepath))
174  return false;
175 
176  // load quests
177  char ctemp;
178  while (ctemp << in)
179  {
180  myquest = new quest;
181  myquest->load (in);
182 
183  // Make this quest available to the engine
184  data::quests[myquest->name.c_str ()] = myquest;
185  }
186 
187  in.close ();
188 
189  return true;
190 }
191 
193 {
194  igzstream in;
195 
196  string filepath;
197 
198  // Load mapengine state
199  filepath = saves[pos]->directory();
200  filepath += "/mapengine.data";
201  in.open (filepath);
202 
203  if (!in.is_open ())
204  {
205  cerr << "Couldn't open \"" << filepath << " - stopping\n" << endl;
206  return false;
207  }
208 
209  if (!fileops::get_version (in, ENGINE_DAT_VER, ENGINE_DAT_VER, filepath))
210  return false;
211 
212  if (!data::engine->get_state(in))
213  {
214  cerr << "Couldn't load \"" << filepath << " - stopping\n" << endl;
215  return false;
216  }
217 
218  in.close ();
219 
220  return true;
221 }
222 
224 {
225  igzstream in;
226  string filepath;
227 
228  // Load mapengine state
229  filepath = saves[pos]->directory();
230  filepath += "/audio.data";
231  in.open (filepath);
232 
233  if (!in.is_open ())
234  {
235  cerr << "Couldn't open \"" << filepath << " - stopping\n" << endl;
236  return false;
237  }
238 
239  if (!fileops::get_version (in, AUDIO_DAT_VER, AUDIO_DAT_VER, filepath))
240  return false;
241 
242  if (!audio::get_state (in))
243  {
244  cerr << "Couldn't load \"" << filepath << " - stopping\n" << endl;
245  return false;
246  }
247 
248  in.close ();
249 
250  return true;
251 }
252 
254 {
255  // First, unload the current game
256  unload ();
257 
258  if (!load_characters (pos)) return false;
259  if (!load_quests (pos)) return false;
260  if (!load_mapengine (pos)) return false;
261  if (!load_audio (pos)) return false;
262 
263  return true;
264 }
265 
267 {
268  // Quick-load off / no save game available
269  if (!quick_load || saves.size () <= 1) return false;
270 
271  u_int32 timestamp = 0;
272  u_int32 index = 0;
273  u_int32 newest;
274 
275  for (vector<gamedata*>::iterator i = saves.begin (); i != saves.end (); i++)
276  {
277  if ((*i)->timestamp () > timestamp)
278  {
279  timestamp = (*i)->timestamp ();
280  newest = index;
281  }
282 
283  index++;
284  }
285 
286  return load (newest);
287 }
288 
289 bool gamedata::save (u_int32 pos, string desc, string time)
290 {
291  gamedata *gdata;
292  string filepath;
293  char t[10];
294  ogzstream file;
295  char vnbr;
296 
297  // make sure we don't overwrite the default game
298  if (pos == 0) return false;
299 
300  // see whether we're going to save to a new slot
301  if (pos >= saves.size ())
302  {
303  int success = 1;
304 
305  // make sure we save to an unused directory
306  while (success)
307  {
308  // that's the directory we're going to save to
309  sprintf(t, "%03i", pos++);
310  filepath = user_data_dir ();
311  filepath += "/" + game_name + "-save-";
312  filepath += t;
313 
314 #ifdef WIN32
315  success = mkdir (filepath.c_str());
316 #else
317  success = mkdir (filepath.c_str(), 0700);
318 #endif
319 
320  // prevent infinite loop if we can't write to the directory
321  if (pos >= 1000)
322  {
323  cerr << "Save failed - seems like you have no write permission in\n"
324  << user_data_dir () << endl;
325  return false;
326  }
327  }
328 
329  // we'll need a new gamedata record
330  gdata = new gamedata (filepath, desc, time);
331  }
332  else
333  {
334  gdata = saves[pos];
335  gdata->set_description (desc);
336  gdata->set_gametime (time);
337  }
338 
339  // save characters
340  filepath = gdata->directory ();
341  filepath += "/character.data";
342  file.open (filepath);
343 
344  if (!file.is_open ())
345  {
346  cerr << "Couldn't create \"" << filepath << "\" - save failed\n";
347  return false;
348  }
349 
350  fileops::put_version (file, CHAR_DAT_VER);
351 
352  // save the player first
353  data::the_player->character_base::put_state (file);
354 
355  // now save all the other characters
357  for (itc = data::characters.begin (); itc != data::characters.end (); itc++)
358  {
359  // don't save the player
360  if (itc->second == (character*) data::the_player) continue;
361 
362  // tell the character.data loader that another entry follows
363  vnbr = 1;
364  vnbr >> file;
365 
366  // append the character data
367  itc->second->character_base::put_state (file);
368  }
369 
370  // write EOF
371  vnbr = 0;
372  vnbr >> file;
373  file.close ();
374 
375  // save quests
376  filepath = gdata->directory ();
377  filepath += "/quest.data";
378  file.open (filepath);
379 
380  if (!file.is_open ())
381  {
382  cerr << "Couldn't create \"" << filepath << "\" - save failed\n";
383  return false;
384  }
385 
386  fileops::put_version (file, QUEST_DAT_VER);
387 
389  for (itq = data::quests.begin (); itq != data::quests.end (); itq++)
390  {
391  // tell the quest.data loader that another entry follows
392  vnbr = 1;
393  vnbr >> file;
394 
395  // append the character data
396  itq->second->save (file);
397  }
398 
399  // write EOF
400  vnbr = 0;
401  vnbr >> file;
402  file.close ();
403 
404  // Save mapengine state
405  filepath = gdata->directory();
406  filepath += "/mapengine.data";
407  file.open (filepath);
408 
409  if (!file.is_open ())
410  {
411  cerr << "Couldn't create \"" << filepath << "\" - save failed\n";
412  return false;
413  }
414 
415  fileops::put_version (file, ENGINE_DAT_VER);
416  data::engine->put_state(file);
417  file.close ();
418 
419  // save music
420  filepath = gdata->directory ();
421  filepath += "/audio.data";
422  file.open (filepath);
423 
424  if (!file.is_open ())
425  {
426  cerr << "Couldn't create \"" << filepath << "\" - save failed\n";
427  return false;
428  }
429 
430  fileops::put_version (file, AUDIO_DAT_VER);
431  audio::put_state (file);
432  file.close ();
433 
434  // save gamedata
435  filepath = gdata->directory ();
436  filepath += "/save.data";
437 
438  file.open (filepath);
439  if (!file.is_open ())
440  {
441  cerr << "Couldn't create \"" << filepath << "\" - save failed\n";
442  return false;
443  }
444 
445  gdata->put (file);
446  file.close ();
447 
448  // only now it is safe to add the new record to the array
449  if (pos >= saves.size ()) saves.push_back (gdata);
450 
451  return true;
452 }
453 
455 {
456  static vector<gamedata*>::iterator i = saves.begin ();
457  static u_int32 size = saves.size ();
458 
459  // check whether a new save has been added
460  if (size != saves.size ())
461  {
462  size = saves.size ();
463  i = saves.begin ();
464  }
465 
466  // check whether we reached the end of the list
467  if (++i == saves.end ())
468  {
469  i = saves.begin ();
470  return NULL;
471  }
472 
473  return *i;
474 }
475 
476 
477 bool gamedata::init (string udir, string gdir, string gname, u_int8 qload)
478 {
479  DIR *dir;
480  igzstream in;
481  struct dirent *dirent;
482  struct stat statbuf;
483  gamedata *gdata;
484 
485  user_data_dir_ = udir;
486  game_data_dir_ = gdir;
487  game_name = gname;
488  quick_load = qload;
489 
490  // try to change into data directory
491  if (chdir (game_data_dir ().c_str ()))
492  {
493  fprintf (stderr, "Seems like %s is no valid data directory.\n", game_data_dir ().c_str ());
494  fprintf (stderr, "Please make sure that your Adonthell installation is correct.\n");
495  return false;
496  }
497 
498  // Add the default savegame used to start a new game to the list of saves
499  gdata = new gamedata (gdir, "Start New Game", "Day 0 - 00:00");
500  saves.push_back (gdata);
501 
502  // Read the user's saved games (if any) - they'll be located in
503  // $HOME/.adonthell/ and called <game_name>-save-<xxx>
504  if ((dir = opendir (user_data_dir ().c_str ())) != NULL)
505  {
506  while ((dirent = readdir (dir)) != NULL)
507  {
508  string filepath = user_data_dir () + "/";
509  filepath += dirent->d_name;
510 
511  string name_save = game_name + "-save-";
512 
513  if (stat (filepath.c_str (), &statbuf) != -1 && S_ISDIR (statbuf.st_mode) &&
514  strncmp (name_save.c_str (), dirent->d_name, name_save.length ()) == 0)
515  {
516  // found a (possibly) valid saved game directory
517  filepath += "/save.data";
518  // Now try to read the saved game's data record
519  in.open (filepath);
520 
521  if (in.is_open ())
522  {
523  // restore the pathname
524  filepath = user_data_dir ();
525  filepath += "/";
526  filepath += dirent->d_name;
527 
528  gdata = new gamedata;
529  if (gdata->get (in))
530  {
531  gdata->set_directory (filepath);
532  saves.push_back (gdata);
533  }
534  else delete gdata;
535 
536  in.close ();
537  }
538  }
539  }
540  closedir (dir);
541  }
542  return true;
543 }
544 
546 {
547  for (vector<gamedata*>::iterator i = saves.begin (); i != saves.end (); i++)
548  delete *i;
549  saves.clear ();
550  unload ();
551 }
552 
554 {
555  // stop the music
556  audio::fade_out_background (500);
557 
558  // delete all characters
560  for (itc = data::characters.begin (); itc != data::characters.end (); itc++)
561  {
562  itc->second->remove_from_map ();
563  delete itc->second;
564  }
565  data::characters.clear ();
566 
567  data::the_player = NULL;
568 
569  // delete all quests
571  for (itq = data::quests.begin (); itq != data::quests.end (); itq++)
572  delete itq->second;
573  data::quests.clear ();
574 }
static bool save(u_int32 pos, string desc, string time)
Save a game.
Definition: gamedata.cc:289
Class to write data from a Gzip compressed file.
Definition: fileops.h:223
void close()
Close the file that was opened.
Definition: fileops.cc:59
void set_description(string)
Sets the description for this game.
Definition: gamedata.cc:97
Declares the event_handler class.
static void cleanup()
Cleanup the saved game array.
Definition: gamedata.cc:545
Class to read data from a Gzip compressed file.
Definition: fileops.h:131
static bool load(u_int32 pos)
Loads a previously saved game.
Definition: gamedata.cc:253
const char * directory()
A bunch of methods to access the private attributes.
Definition: gamedata.h:103
Class holding game characters.
Definition: character.h:35
static bool load_characters(u_int32 pos)
Load the characters state from a saved game.
Definition: gamedata.cc:112
Definition: quest.h:23
gamedata()
Default constructor.
Definition: gamedata.cc:55
static bool init(string udir, string gdir, string gname, u_int8 qload)
Initialise the saved games array.
Definition: gamedata.cc:477
Definition: str_hash.h:36
string get_id()
Returns an unique identifier of the character.
#define u_int32
32 bits long unsigned integer
Definition: types.h:35
#define u_int8
8 bits long unsigned integer
Definition: types.h:29
Stores objects of any kind.
Definition: storage.h:227
static bool load_audio(u_int32 pos)
Load the audio system state from a saved game.
Definition: gamedata.cc:223
static gamedata * next_save()
Returns a pointer to the next saved game.
Definition: gamedata.cc:454
bool open(const string &fname)
Opens a file for write access.
Definition: fileops.cc:262
bool open(const string &fname)
Opens a file for read access.
Definition: fileops.cc:77
static bool get_version(igzstream &file, u_int16 min, u_int16 max, string name)
Definition: fileops.cc:365
Declares the gamedata and data classes.
bool is_open()
Returns whether the file is opened or not.
Definition: fileops.h:99
static bool load_newest()
Loads the most recent saved game.
Definition: gamedata.cc:266
Defines the python class. This file is named this way so it doesn&#39;t conflicts with Python...
~gamedata()
Destructor.
Definition: gamedata.cc:67
bool get(igzstream &)
Load a record from an opened file.
Definition: gamedata.cc:71
static bool load_quests(u_int32 pos)
Load the quests state from a saved game.
Definition: gamedata.cc:155
static void put_version(ogzstream &file, u_int16 version)
Sets the version number of a file.
Definition: fileops.cc:357
s_int8 put_state(ogzstream &file)
Save the engine&#39;s state.
Definition: adonthell.cc:177
void set_gametime(string)
Set the in-game time of the saved game.
Definition: gamedata.cc:107
static void unload()
Unloads the current game, resetting the engine to it&#39;s initial state.
Definition: gamedata.cc:553
void put(ogzstream &)
Save a record to an opened file.
Definition: gamedata.cc:84
static bool load_mapengine(u_int32 pos)
Load the mapengine state from a saved game.
Definition: gamedata.cc:192
void set_directory(string)
Sets the directory for this game.
Definition: gamedata.cc:102
Contains all the attributes related to a saved game and the high level methods for loading/saving the...
Definition: gamedata.h:48