Adonthell 0.4
|
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 }