Newfile_gump.cc

Go to the documentation of this file.
00001 /*
00002  *  Copyright (C) 2001-2002  The Exult Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #  include <config.h>
00021 #endif
00022 
00023 #ifndef ALPHA_LINUX_CXX
00024 #  include <cstring>
00025 #  include <ctime>
00026 #endif 
00027 
00028 #include "exult_flx.h"
00029 
00030 #include "Audio.h"
00031 #include "Configuration.h"
00032 #include "Gump_button.h"
00033 #include "Newfile_gump.h"
00034 #include "Yesno_gump.h"
00035 #include "actors.h"
00036 #include "exult.h"
00037 #include "game.h"
00038 #include "gameclk.h"
00039 #include "gamewin.h"
00040 #include "listfiles.h"
00041 #include "mouse.h"
00042 #include "party.h"
00043 #include "Text_button.h"
00044 
00045 #ifndef UNDER_CE
00046 using std::atoi;
00047 using std::cout;
00048 using std::endl;
00049 using std::isdigit;
00050 using std::memcpy;
00051 using std::memset;
00052 using std::memmove;
00053 using std::qsort;
00054 using std::string;
00055 using std::strlen;
00056 using std::strncpy;
00057 using std::strcpy;
00058 using std::strcat;
00059 using std::time_t;
00060 using std::tm;
00061 #endif
00062 
00063 /*
00064  *  Macros:
00065  */
00066 
00067 /*
00068  *  Statics:
00069  */
00070 // Button Coords
00071 const short Newfile_gump::btn_rows[5] = {186, 2, 15, 158, 171};
00072 const short Newfile_gump::btn_cols[5] = {2, 46, 88, 150, 210};
00073 
00074 // Text field info
00075 const short Newfile_gump::fieldx = 2;   // Start Y of each field
00076 const short Newfile_gump::fieldy = 2;   // Start X of first
00077 const short Newfile_gump::fieldw = 207;   // Width of each field
00078 const short Newfile_gump::fieldh = 12;    // Height of each field
00079 const short Newfile_gump::fieldgap = 1;   // Gap between fields
00080 const short Newfile_gump::fieldcount = 14;  // Number of fields
00081 const short Newfile_gump::textx = 12;   // X Offset in field
00082 const short Newfile_gump::texty = 2;    // Y Offset in field
00083 const short Newfile_gump::textw = 190;    // Maximum allowable width of text (pixels)
00084 const short Newfile_gump::iconx = 2;    // X Offset in field
00085 const short Newfile_gump::icony = 2;    // Y Offset in field
00086 
00087 // Scrollbar and Slider Info
00088 const short Newfile_gump::scrollx = 212;  // X Offset
00089 const short Newfile_gump::scrolly = 28;   // Y Offset
00090 const short Newfile_gump::scrollh = 129;  // Height of Scroll Bar
00091 const short Newfile_gump::sliderw = 7;    // Width of Slider
00092 const short Newfile_gump::sliderh = 7;    // Height of Slider
00093 
00094 const short Newfile_gump::infox = 224;
00095 const short Newfile_gump::infoy = 67;
00096 const short Newfile_gump::infow = 92;
00097 const short Newfile_gump::infoh = 79;
00098 const char Newfile_gump::infostring[] = "Avatar: %s\n"
00099           "Exp: %i  Hp: %i\n"
00100           "Str: %i  Dxt: %i\n"
00101           "Int: %i  Trn: %i\n"
00102           "\n"
00103           "Game Day: %i\n"
00104           "Game Time: %02i:%02i\n"
00105           "\n"
00106           "Save Count: %i\n"
00107           "Date: %i%s %s %04i\n"
00108           "Time: %02i:%02i";
00109 
00110 const char *Newfile_gump::months[12] = {"Jan",
00111           "Feb",
00112           "March",
00113           "April",
00114           "May",
00115           "June",
00116           "July",
00117           "Aug",
00118           "Sept",
00119           "Oct",
00120           "Nov",
00121           "Dec" };
00122 
00123 static const char *loadtext = "LOAD";
00124 static const char *savetext = "SAVE";
00125 static const char *deletetext = "DELETE";
00126 static const char *canceltext = "CANCEL";
00127 
00128 #ifdef UNDER_CE
00129 struct tm * __cdecl localtime(const time_t *it)
00130 {
00131   static tm t;
00132   memset(&t,0,sizeof(t));
00133 
00134   SYSTEMTIME systime;
00135   FILETIME filetime;
00136   FILETIME local_filetime;
00137   LONGLONG time;
00138 
00139   SYSTEMTIME systime_1970;
00140   FILETIME filetime_1970;
00141   LONGLONG time_1970;
00142 
00143   systime_1970.wYear = 1970;
00144   systime_1970.wMonth = 1;
00145   systime_1970.wDay = 1;
00146   systime_1970.wHour = 0;
00147   systime_1970.wMinute = 0;
00148   systime_1970.wSecond = 0;
00149   systime_1970.wMilliseconds = 0;
00150   SystemTimeToFileTime(&systime_1970, &filetime_1970);
00151   memcpy(&time_1970, &filetime_1970, 8);
00152 
00153   // Seconds to 100 nanoseconds
00154   time = *it;
00155   time *= 10000000;
00156   time += time_1970;
00157 
00158   memcpy(&filetime, &time, 8);
00159   FileTimeToLocalFileTime(&filetime, &local_filetime);
00160   FileTimeToSystemTime(&local_filetime, &systime);
00161 
00162   t.tm_sec = systime.wSecond;
00163   t.tm_min = systime.wMinute;
00164   t.tm_hour = systime.wHour;
00165   t.tm_isdst = 0;
00166   t.tm_wday = systime.wDayOfWeek;
00167   t.tm_mday = systime.wDay;
00168   t.tm_mon = systime.wMonth - 1;
00169   t.tm_yday = 0;
00170   t.tm_year = systime.wYear - 1900;
00171 
00172   return &t;
00173 }
00174 
00175 time_t __cdecl time(time_t *t)
00176 {
00177   SYSTEMTIME systime;
00178   FILETIME filetime;
00179   LONGLONG time;
00180 
00181   SYSTEMTIME systime_1970;
00182   FILETIME filetime_1970;
00183   LONGLONG time_1970;
00184 
00185   GetSystemTime(&systime);
00186   SystemTimeToFileTime(&systime, &filetime);
00187   memcpy(&time, &filetime, 8);
00188 
00189   systime_1970.wYear = 1970;
00190   systime_1970.wMonth = 1;
00191   systime_1970.wDay = 1;
00192   systime_1970.wHour = 0;
00193   systime_1970.wMinute = 0;
00194   systime_1970.wSecond = 0;
00195   systime_1970.wMilliseconds = 0;
00196   SystemTimeToFileTime(&systime_1970, &filetime_1970);
00197   memcpy(&time_1970, &filetime_1970, 8);
00198 
00199   // Time since Jan First 1970
00200   time -= time_1970;
00201   if (time < 0) time = 0;
00202 
00203   // 100 nanoseconds to Seconds
00204   time /= 10000000;
00205 
00206   if (t) *t = time;
00207 
00208   return time;
00209 }
00210 #endif
00211 
00212 
00213 /*
00214  *  One of our buttons.
00215  */
00216 class Newfile_button : public Gump_button
00217 {
00218 public:
00219   Newfile_button(Gump *par, int px, int py, int shapenum)
00220     : Gump_button(par, shapenum, px, py, SF_EXULT_FLX)
00221   { }
00222           // What to do when 'clicked':
00223   virtual void activate();
00224 };
00225 
00226 class Newfile_Textbutton : public Text_button
00227 {
00228 public:
00229   Newfile_Textbutton(Gump *par, string text, int px, int py, int width)
00230     : Text_button(par, text, px, py, width)
00231   { }
00232 
00233   virtual void activate();
00234 };
00235 
00236 /*
00237  *  Clicked a 'load' or 'save' button.
00238  */
00239 
00240 void Newfile_button::activate
00241   (
00242   )
00243 {
00244   int shapenum = get_shapenum();
00245   if (shapenum == EXULT_FLX_SAV_DOWNDOWN_SHP)
00246     ((Newfile_gump *) parent)->scroll_page(1);
00247   else if (shapenum == EXULT_FLX_SAV_DOWN_SHP)
00248     ((Newfile_gump *) parent)->scroll_line(1);
00249   else if (shapenum == EXULT_FLX_SAV_UP_SHP)
00250     ((Newfile_gump *) parent)->scroll_line(-1);
00251   else if (shapenum == EXULT_FLX_SAV_UPUP_SHP)
00252     ((Newfile_gump *) parent)->scroll_page(-1);
00253 }
00254 
00255 void Newfile_Textbutton::activate()
00256 {
00257   if (text == loadtext)
00258     ((Newfile_gump *) parent)->load();
00259   else if (text == savetext)
00260     ((Newfile_gump *) parent)->save();
00261   else if (text == deletetext)
00262     ((Newfile_gump *) parent)->delete_file();
00263   else if (text == canceltext)
00264     parent->close();
00265 }
00266 
00267 /*
00268  *  Create the load/save box.
00269  */
00270 
00271 
00272 Newfile_gump::Newfile_gump
00273   (
00274   ) : Modal_gump(0, gwin->get_width()/2-160,
00275       gwin->get_height()/2-100,
00276       EXULT_FLX_SAVEGUMP_SHP, SF_EXULT_FLX),
00277     restored(0), games(0), num_games(0), first_free(0),
00278     cur_shot(0), cur_details(0), cur_party(0),
00279     gd_shot(0), gd_details(0), gd_party(0),
00280     screenshot(0), details(0), party(0), is_readable(false), filename(0),
00281     list_position(-2), selected(-3), cursor(0), slide_start(-1)
00282 
00283 {
00284   set_object_area(Rectangle(0,0,320,200), -22, 190);//+++++ ???
00285 
00286   newname[0] = 0;
00287 
00288   gwin->get_tqueue()->pause(SDL_GetTicks());
00289   back = gwin->get_win()->create_buffer(gwin->get_width(), gwin->get_height());
00290   gwin->get_win()->get(back, 0, 0);
00291 
00292   // Load/Save/Delete
00293   buttons[0] = buttons[1] = buttons[2] = 0;
00294 
00295   // Cancel
00296   buttons[3] = new Newfile_Textbutton(this, canceltext,
00297                     btn_cols[3], btn_rows[0], 59);
00298 
00299   // Scrollers.
00300   buttons[4] = new Newfile_button(this, btn_cols[4], btn_rows[1], EXULT_FLX_SAV_UPUP_SHP);
00301   buttons[5] = new Newfile_button(this, btn_cols[4], btn_rows[2], EXULT_FLX_SAV_UP_SHP);
00302   buttons[6] = new Newfile_button(this, btn_cols[4], btn_rows[3], EXULT_FLX_SAV_DOWN_SHP);
00303   buttons[7] = new Newfile_button(this, btn_cols[4], btn_rows[4], EXULT_FLX_SAV_DOWNDOWN_SHP);
00304 
00305   LoadSaveGameDetails();
00306 }
00307 
00308 /*
00309  *  Delete the load/save box.
00310  */
00311 
00312 Newfile_gump::~Newfile_gump
00313   (
00314   )
00315 {
00316   gwin->get_tqueue()->resume(SDL_GetTicks());
00317   size_t i;
00318   for (i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++)
00319     delete buttons[i];
00320 
00321   FreeSaveGameDetails();
00322 
00323   delete back;
00324 }
00325 
00326 /*
00327  *  'Load' clicked.
00328  */
00329 
00330 void Newfile_gump::load()
00331 {
00332   // Shouldn't ever happen.
00333   if (selected == -2 || selected == -3)
00334     return; 
00335 
00336 
00337   // Aborts if unsuccessful.
00338   if (selected != -1) gwin->restore_gamedat(games[selected].num);
00339 
00340   // Read Gamedat
00341   gwin->read();
00342 
00343   // Set Done
00344   done = true;
00345   restored = 1;
00346   
00347   // Reset Selection
00348   selected = -3;
00349 
00350   delete buttons[0];
00351   buttons[0] = 0;
00352   delete buttons[1];
00353   buttons[1] = 0;
00354   delete buttons[2];
00355   buttons[2] = 0;
00356 
00357   //Reread save game details (quick save gets overwritten)
00358   //FreeSaveGameDetails();
00359   //LoadSaveGameDetails();
00360   //paint();
00361   //gwin->set_painted();
00362 }
00363 
00364 /*
00365  *  'Save' clicked.
00366  */
00367 
00368 void Newfile_gump::save()
00369 {
00370   // Shouldn't ever happen.
00371   if (!strlen(newname) || selected == -3)
00372     return; 
00373 
00374 
00375   // Already a game in this slot? If so ask to delete
00376   if (selected != -2) if (!Yesno_gump::ask("Okay to write over existing saved game?"))
00377     return;
00378 
00379   // Write to gamedat
00380   gwin->write();
00381 
00382   // Now write to savegame file
00383   if (selected >= 0) gwin->save_gamedat(games[selected].num, newname);
00384   else if (selected == -2) gwin->save_gamedat(first_free, newname);
00385 
00386   cout << "Saved game #" << selected << " successfully." << endl;
00387 
00388   // Reset everything
00389   selected = -3;
00390 
00391   delete buttons[0];
00392   buttons[0] = 0;
00393   delete buttons[1];
00394   buttons[1] = 0;
00395   delete buttons[2];
00396   buttons[2] = 0;
00397 
00398   FreeSaveGameDetails();
00399   LoadSaveGameDetails();
00400   paint();
00401   gwin->set_painted();
00402 }
00403 
00404 /*
00405  *  'Delete' clicked.
00406  */
00407 
00408 void Newfile_gump::delete_file()
00409 {
00410   // Shouldn't ever happen.
00411   if (selected == -1 || selected == -2 || selected == -3)
00412     return; 
00413 
00414 
00415   // Ask to delete
00416   if (!Yesno_gump::ask("Okay to delete saved game?"))
00417     return;
00418 
00419   U7remove (games[selected].filename);
00420   filename = 0;
00421   is_readable = false;
00422 
00423   cout << "Deleted Save game #" << selected << " (" << games[selected].filename << ") successfully." << endl;
00424 
00425   // Reset everything
00426   selected = -3;
00427 
00428   delete buttons[0];
00429   buttons[0] = 0;
00430   delete buttons[1];
00431   buttons[1] = 0;
00432   delete buttons[2];
00433   buttons[2] = 0;
00434 
00435   FreeSaveGameDetails();
00436   LoadSaveGameDetails();
00437   paint();
00438   gwin->set_painted();
00439 }
00440 
00441 /*
00442  *  Scroll Line
00443  */
00444 
00445 void Newfile_gump::scroll_line(int dir)
00446 {
00447   list_position += dir;
00448 
00449   if (list_position > num_games-fieldcount)
00450     list_position = num_games-fieldcount;
00451 
00452   if (list_position < -2)
00453     list_position = -2;
00454 
00455 #ifdef DEBUG
00456   cout << "New list position " << list_position << endl;
00457 #endif
00458 
00459   paint();
00460   gwin->set_painted();
00461 }
00462 
00463 /*
00464  *  Scroll Page
00465  */
00466 
00467 void Newfile_gump::scroll_page(int dir)
00468 {
00469   scroll_line (dir * fieldcount);
00470 }
00471 
00472 void Newfile_gump::PaintSaveName (int line)
00473 {
00474 
00475   int actual_game = line+list_position;
00476 
00477   if (actual_game < -2 || actual_game >= num_games) return;
00478 
00479   char  *text;
00480 
00481   if (actual_game == -1)
00482     text = "Quick Save";
00483   else if (actual_game == -2 && selected != -2)
00484     text = "Empty Slot";
00485   else if (actual_game != selected || buttons[0])
00486     text = games[actual_game].savename;
00487   else
00488     text = newname;
00489 
00490   sman->paint_text (2, text, 
00491     x + fieldx + textx,
00492     y + fieldy + texty + line*(fieldh + fieldgap));
00493 
00494   // Being Edited? If so paint cursor
00495   if (selected == actual_game && cursor != -1)
00496     gwin->get_win()->fill8(0, 1, sman->get_text_height(2),
00497       x + fieldx + textx + sman->get_text_width(2, text, cursor),
00498         y + fieldy + texty + line*(fieldh + fieldgap));
00499 
00500   // If selected, show selected icon
00501   if (selected == actual_game)
00502   {
00503     ShapeID icon (EXULT_FLX_SAV_SELECTED_SHP, 0, SF_EXULT_FLX);
00504     icon.paint_shape ( x+fieldx+iconx,
00505           y+fieldy+icony+line*(fieldh+fieldgap));
00506   }
00507 
00508 }
00509 
00510 
00511 /*
00512  *  Paint on screen.
00513  */
00514 
00515 void Newfile_gump::paint
00516   (
00517   )
00518 {
00519   if (!games)
00520     return;     // No list, so skip out.
00521   Gump::paint();
00522 
00523   // Paint text objects.
00524   int i;
00525 
00526   for (i = 0; i < fieldcount; i++)
00527     PaintSaveName (i);
00528 
00529   // Paint Buttons
00530   for (i = 0; i < 8; i++) if (buttons[i])
00531     buttons[i]->paint();
00532 
00533   // Paint scroller
00534 
00535   // First thing, work out number of positions that the scroller can be in
00536   int num_pos = (2+num_games)-fieldcount;
00537   if (num_pos < 1) num_pos = 1;
00538 
00539   // Now work out the position
00540   int pos = ((scrollh-sliderh)*(list_position+2))/num_pos;
00541 
00542   ShapeID slider_shape(EXULT_FLX_SAV_SLIDER_SHP, 0, SF_EXULT_FLX);
00543   slider_shape.paint_shape(x+scrollx , y+scrolly+pos);
00544 
00545   // Now paint the savegame details
00546   if (screenshot) 
00547     sman->paint_shape(x + 222, y + 2, screenshot->get_frame(0));
00548 
00549   // Need to ensure that the avatar's shape actually exists
00550   if (party && party[0].shape_file == SF_BG_SISHAPES_VGA && 
00551     !sman->can_use_multiracial())
00552   {
00553     party[0].shape_file = SF_SHAPES_VGA;
00554 
00555     // Female if odd, male if even
00556     if (party[0].shape %2) party[0].shape = 989;
00557     else party[0].shape = 721;
00558   }
00559 
00560   if (details && party)
00561   {
00562     int i;
00563 
00564     for (i=0; i<4 && i<details->party_size; i++)
00565     {
00566       ShapeID shape(party[i].shape, 16, (ShapeFile) party[i].shape_file);
00567       shape.paint_shape(x + 249 + i*23, y + 169);
00568     }
00569 
00570     for (i=4; i<8 && i<details->party_size; i++)
00571     {
00572       ShapeID shape(party[i].shape, 16, (ShapeFile) party[i].shape_file);
00573       shape.paint_shape(x + 249 + (i-4)*23, y + 198);
00574     }
00575 
00576     char  info[320];
00577 
00578     char  *suffix = "th";
00579 
00580     if ((details->real_day%10) == 1 && details->real_day != 11)
00581       suffix = "st";
00582     else if ((details->real_day%10) == 2 && details->real_day != 12)
00583       suffix = "nd";
00584     else if ((details->real_day%10) == 3 && details->real_day != 13)
00585       suffix = "rd";
00586 
00587     snprintf (info, 320, infostring, party[0].name,
00588       party[0].exp, party[0].health,
00589       party[0].str, party[0].dext,
00590       party[0].intel, party[0].training,
00591       details->game_day, details->game_hour, details->game_minute,
00592       details->save_count,
00593       details->real_day, suffix, months[details->real_month-1], details->real_year,
00594       details->real_hour, details->real_minute);
00595 
00596     if (filename)
00597     {
00598       std::strncat (info, "\nFile: ", 320);
00599 
00600       int offset = strlen(filename);
00601       
00602       while (offset--)
00603       {
00604         if (filename[offset] == '/' || filename[offset] == '\\')
00605         {
00606           offset++;
00607           break;
00608         }
00609       }
00610       std::strncat (info, filename+offset, 320);
00611 
00612     }
00613 
00614     sman->paint_text_box (4, info, x+infox, y+infoy, infow, infoh);
00615 
00616   }
00617   else
00618   {
00619     if (filename)
00620     {
00621       char  info[64] = {0};
00622 
00623       std::strncat (info, "File: ", 64);
00624 
00625       int offset = strlen(filename);
00626       
00627       while (offset--)
00628       {
00629         if (filename[offset] == '/' || filename[offset] == '\\')
00630         {
00631           offset++;
00632           break;
00633         }
00634       }
00635       std::strncat (info, filename+offset, 64);
00636       sman->paint_text_box (4, info, x+infox, y+infoy, infow, infoh);
00637 
00638     }
00639 
00640     if (!is_readable)
00641     {
00642       sman->paint_text (2, "Unreadable", x+infox+(infow-sman->get_text_width(2, "Unreadable"))/2, y+infoy+(infoh-18)/2);
00643       sman->paint_text (2, "Savegame", x+infox+(infow-sman->get_text_width(2, "Savegame"))/2, y+infoy+(infoh)/2);
00644     }
00645     else 
00646     {
00647       sman->paint_text (4, "No Info", x+infox+(infow-sman->get_text_width(4, "No Info"))/2, y+infoy+(infoh-sman->get_text_height(4))/2);
00648     }
00649   }
00650   gwin->set_painted();
00651 }
00652 
00653 /*
00654  *  Handle mouse-down events.
00655  */
00656 
00657 void Newfile_gump::mouse_down
00658   (
00659   int mx, int my      // Position in window.
00660   )
00661 {
00662   slide_start = -1;
00663 
00664   pushed = Gump::on_button(mx, my);
00665         // Try buttons at bottom.
00666   if (!pushed) for (size_t i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++)
00667     if (buttons[i] && buttons[i]->on_button(mx, my))
00668     {
00669       pushed = buttons[i];
00670       break;
00671     }
00672 
00673   if (pushed)     // On a button?
00674   {
00675     pushed->push();
00676     return;
00677   }
00678 
00679   int gx = mx - x;
00680   int gy = my - y;
00681 
00682   // Check for scroller
00683   if (gx >= scrollx && gx < scrollx+sliderw && gy >= scrolly && gy < scrolly+scrollh)
00684   {
00685     int num_pos = (2+num_games)-fieldcount;
00686     if (num_pos < 1) num_pos = 1;
00687 
00688     // Now work out the position
00689     int pos = ((scrollh-sliderh)*(list_position+2))/num_pos;
00690 
00691     // Pressed above it
00692     if (gy < pos+scrolly)
00693     {
00694       scroll_page(-1);
00695       paint();
00696       return;
00697     }
00698     // Pressed below it
00699     else if (gy >= pos+scrolly+sliderh)
00700     {
00701       scroll_page(1);
00702       paint();
00703       return;
00704     }
00705     // Pressed on it
00706     else
00707     {
00708       slide_start = gy;
00709       return;
00710     }
00711   }
00712 
00713 
00714   // Now check for text fields
00715   if (gx < fieldx || gx >= fieldx+fieldw)
00716     return;
00717 
00718   int hit = -1;
00719   int i;
00720   for (i = 0; i < fieldcount; i++)
00721   {
00722     int fy = fieldy + i*(fieldh + fieldgap);
00723     if (gy >= fy && gy < fy+fieldh)
00724     {
00725       hit = i;
00726       break;
00727     }
00728   }
00729 
00730   if (hit == -1) return;
00731 
00732   if (hit+list_position >= num_games || hit+list_position < -2 || selected == hit+list_position) return;
00733 
00734 #ifdef DEBUG
00735   cout << "Hit a save game field" << endl;
00736 #endif
00737   selected = hit+list_position;
00738 
00739   int want_load = true;
00740   int want_delete = true;
00741   int want_save = true;
00742 
00743   if (selected == -2)
00744   {
00745     want_load = false;
00746     want_delete = false;
00747     want_save = false;
00748     screenshot = cur_shot;
00749     details = cur_details;
00750     party = cur_party;
00751     newname[0] = 0;
00752     cursor = 0;
00753     is_readable = true;
00754     filename = 0;
00755   }
00756   else if (selected == -1)
00757   {
00758     want_delete = false;
00759     screenshot = gd_shot;
00760     details = gd_details;
00761     party = gd_party;
00762     strcpy (newname, "Quick Save");
00763     cursor = -1; // No cursor
00764     is_readable = true;
00765     filename = 0;
00766   }
00767   else
00768   {
00769     screenshot = games[selected].screenshot;
00770     details = games[selected].details;
00771     party = games[selected].party;
00772     strcpy (newname, games[selected].savename);
00773     cursor = strlen (newname);
00774     is_readable = want_load = games[selected].readable;
00775     filename = games[selected].filename;
00776   }
00777 
00778   if (!buttons[0] && want_load)
00779     buttons[0] = new Newfile_Textbutton(this, loadtext, 
00780                       btn_cols[1], btn_rows[0], 39);
00781   else if (buttons[0] && !want_load)
00782   {
00783     delete buttons[0];
00784     buttons[0] = 0;
00785   }
00786 
00787   if (!buttons[1] && want_save)
00788     buttons[1] = new Newfile_Textbutton(this, savetext,
00789                       btn_cols[0], btn_rows[0], 40);
00790   else if (buttons[1] && !want_save)
00791   {
00792     delete buttons[1];
00793     buttons[1] = 0;
00794   }
00795 
00796   if (!buttons[2] && want_delete)
00797     buttons[2] = new Newfile_Textbutton(this, deletetext,
00798                     btn_cols[2], btn_rows[0], 59);
00799   else if (buttons[2] && !want_delete)
00800   {
00801     delete buttons[2];
00802     buttons[2] = 0;
00803   }
00804 
00805   paint();      // Repaint.
00806   gwin->set_painted();
00807           // See if on text field.
00808 }
00809 
00810 /*
00811  *  Handle mouse-up events.
00812  */
00813 
00814 void Newfile_gump::mouse_up
00815   (
00816   int mx, int my      // Position in window.
00817   )
00818 {
00819   slide_start = -1;
00820 
00821   if (pushed)     // Pushing a button?
00822   {
00823     pushed->unpush();
00824     if (pushed->on_button(mx, my))
00825       pushed->activate();
00826     pushed = 0;
00827   }
00828 }
00829 
00830 void Newfile_gump::mousewheel_up()
00831 {
00832   SDLMod mod = SDL_GetModState();
00833   if (mod & KMOD_ALT)
00834     scroll_page(-1);
00835   else
00836     scroll_line(-1);
00837 }
00838 
00839 void Newfile_gump::mousewheel_down()
00840 {
00841   SDLMod mod = SDL_GetModState();
00842   if (mod & KMOD_ALT)
00843     scroll_page(1);
00844   else
00845     scroll_line(1);
00846 }
00847 
00848 /*
00849  *  Mouse was dragged with left button down.
00850  */
00851 
00852 void Newfile_gump::mouse_drag
00853   (
00854   int mx, int my      // Where mouse is.
00855   )
00856 {
00857   // If not sliding don't do anything
00858   if (slide_start == -1) return;
00859 
00860   int gx = mx - x;
00861   int gy = my - y;
00862 
00863   // First if the position is too far away from the slider 
00864   // We'll put it back to the start
00865   int sy = gy - scrolly;
00866   if (gx < scrollx-20 || gx > scrollx+sliderw+20)
00867     sy = slide_start - scrolly;
00868 
00869   if (sy < sliderh/2) sy = sliderh/2;
00870   if (sy > scrollh-sliderh/2) sy = scrollh-sliderh/2;
00871   sy -= sliderh/2;
00872 
00873   // Now work out the number of positions
00874   int num_pos = (2+num_games)-fieldcount;
00875 
00876   // Can't scroll if there is less than 1 pos
00877   if (num_pos < 1) return;
00878 
00879   // Now work out the closest position to here position
00880   int new_pos = ((sy*num_pos*2)/(scrollh-sliderh)+1)/2-2;
00881 
00882   if (new_pos != list_position)
00883   {
00884     list_position = new_pos;
00885     paint();
00886   }
00887 }
00888 
00889 /*
00890  *  Handle character that was typed.
00891  */
00892 
00893 void Newfile_gump::text_input(int chr, int unicode)
00894 {
00895   bool update_details = false;
00896   int repaint = false;
00897 
00898   // Are we selected on some text?
00899   if (selected == -3)
00900     return;
00901 
00902 
00903   switch (chr) {
00904 
00905   case SDLK_RETURN:   // If only 'Save', do it.
00906     if (!buttons[0] && buttons[1])
00907     {
00908       buttons[1]->push();
00909       gwin->show(1);
00910       buttons[1]->unpush();
00911       gwin->show(1);
00912       buttons[1]->activate();
00913     }
00914     update_details = true;
00915     break;
00916 
00917   case SDLK_BACKSPACE:
00918     if (BackspacePressed())
00919     { 
00920       // Can't restore/delete now.
00921       delete buttons[0];
00922       delete buttons[2];
00923       buttons[0] = buttons[2] = 0;
00924 
00925       // If no chars cant save either
00926       if (!newname[0])
00927       { 
00928         delete buttons[1];
00929         buttons[1] = 0;
00930       }
00931       update_details = true;
00932     }
00933     break;
00934 
00935   case SDLK_DELETE:
00936     if (DeletePressed())
00937     { 
00938       // Can't restore/delete now.
00939       delete buttons[0];
00940       delete buttons[2];
00941       buttons[0] = buttons[2] = 0;
00942 
00943       // If no chars cant save either
00944       if (!newname[0])
00945       { 
00946         delete buttons[1];
00947         buttons[1] = 0;
00948       }
00949       update_details = true;
00950     }
00951     break;
00952 
00953   case SDLK_LEFT:
00954     repaint = MoveCursor(-1);
00955     break;
00956 
00957   case SDLK_RIGHT:
00958     repaint = MoveCursor(1);
00959     break;
00960 
00961   case SDLK_HOME:
00962     repaint = MoveCursor(-MAX_SAVEGAME_NAME_LEN);
00963     break;
00964 
00965   case SDLK_END:
00966     repaint = MoveCursor(MAX_SAVEGAME_NAME_LEN);
00967     break;
00968 
00969   default:
00970 
00971     if ((unicode & 0xFF80) == 0 )
00972       chr = unicode & 0x7F;
00973     else
00974       chr = 0;
00975 
00976     if (chr < ' ')
00977       return;     // Ignore other special chars.
00978 
00979     if (chr < 256 && isascii(chr))
00980     {
00981       if (AddCharacter (chr))
00982       {
00983         // Added first character?  Need 'Save' button.
00984         if (newname[0] && !buttons[1])
00985         {
00986           buttons[1] = new Newfile_Textbutton(this, savetext,
00987                             btn_cols[0], 
00988                             btn_rows[0], 40);
00989           buttons[1]->paint();
00990         }
00991 
00992         // Remove Load and Delete Button
00993         if (buttons[0] || buttons[2])
00994         {
00995           delete buttons[0];
00996           delete buttons[2];
00997           buttons[0] = buttons[2] = 0;
00998         }
00999         update_details = true;
01000       }
01001     }
01002     break;
01003   }
01004 
01005   // This sets the game details to the cur set
01006   if (update_details)
01007   {
01008     screenshot = cur_shot;
01009     details = cur_details;
01010     party = cur_party;
01011     repaint = true;
01012   }
01013   if (repaint)
01014   {
01015     paint();
01016     gwin->set_painted();
01017   }
01018 }
01019 
01020 int Newfile_gump::BackspacePressed()
01021 {
01022   if (cursor == -1 || cursor == 0) return 0;
01023   cursor--;
01024   return DeletePressed();
01025 }
01026 int Newfile_gump::DeletePressed()
01027 {
01028   if (cursor == -1 || cursor == strlen (newname)) return 0;
01029   for (int i = cursor; i < strlen (newname); i++)
01030     newname[i] = newname[i+1];
01031 
01032   return 1;
01033 }
01034 int Newfile_gump::MoveCursor(int count)
01035 {
01036   if (cursor == -1) return 0;
01037 
01038   cursor += count;
01039   if (cursor < 0) cursor = 0;
01040   if (cursor > strlen (newname)) cursor = strlen (newname);
01041 
01042   return 1;
01043 }
01044 int Newfile_gump::AddCharacter(char c)
01045 {
01046   if (cursor == -1 || cursor == MAX_SAVEGAME_NAME_LEN-1) return 0;
01047 
01048   char  text[MAX_SAVEGAME_NAME_LEN];
01049 
01050   strcpy (text, newname);
01051   text[cursor+1] = 0;
01052   text[cursor] = c;
01053   strcat (text, newname+cursor);
01054 
01055   //Now check the width of the text
01056   if (sman->get_text_width(2, text) >= textw)
01057     return 0;
01058 
01059   cursor++;
01060   strcpy (newname, text);
01061   return 1;
01062 }
01063 
01064 void Newfile_gump::LoadSaveGameDetails()
01065 {
01066   int   i;
01067 
01068 
01069   // Gamedat Details
01070   gwin->get_saveinfo(gd_shot, gd_details, gd_party);
01071 
01072   // Current screenshot
01073   cur_shot = gwin->create_mini_screenshot();
01074 
01075   // Current Details
01076   cur_details = new SaveGame_Details;
01077   memset (cur_details, 0, sizeof(SaveGame_Details));
01078 
01079   gwin->get_win()->put(back, 0, 0);
01080 
01081   if (gd_details) cur_details->save_count = gd_details->save_count;
01082   else cur_details->save_count = 0;
01083 
01084   cur_details->party_size = partyman->get_count()+1;
01085   cur_details->game_day = gclock->get_total_hours() / 24;;
01086   cur_details->game_hour = gclock->get_hour();
01087   cur_details->game_minute = gclock->get_minute();
01088   
01089   time_t t = std::time(0);
01090   struct tm *timeinfo = std::localtime (&t);  
01091 
01092   cur_details->real_day = timeinfo->tm_mday;
01093   cur_details->real_hour = timeinfo->tm_hour;
01094   cur_details->real_minute = timeinfo->tm_min;
01095   cur_details->real_month = timeinfo->tm_mon+1;
01096   cur_details->real_year = timeinfo->tm_year + 1900;
01097   cur_details->real_second = timeinfo->tm_sec;
01098 
01099   // Current Party
01100   cur_party = new SaveGame_Party[cur_details->party_size];
01101   for (i=0; i<cur_details->party_size ; i++)
01102   {
01103     Actor *npc;
01104     if (i == 0)
01105       npc = gwin->get_main_actor();
01106     else
01107       npc = (Npc_actor *) gwin->get_npc(partyman->get_member(i-1));
01108 
01109     std::string namestr = npc->get_npc_name();
01110     strncpy (cur_party[i].name, namestr.c_str(), 18);
01111     cur_party[i].shape = npc->get_shapenum();
01112     cur_party[i].shape_file = npc->get_shapefile();
01113 
01114     cur_party[i].dext = npc->get_property(Actor::dexterity);
01115     cur_party[i].str = npc->get_property(Actor::strength);
01116     cur_party[i].intel = npc->get_property(Actor::intelligence);
01117     cur_party[i].health = npc->get_property(Actor::health);
01118     cur_party[i].combat = npc->get_property(Actor::combat);
01119     cur_party[i].mana = npc->get_property(Actor::mana);
01120     cur_party[i].magic = npc->get_property(Actor::magic);
01121     cur_party[i].training = npc->get_property(Actor::training);
01122     cur_party[i].exp = npc->get_property(Actor::exp);
01123     cur_party[i].food = npc->get_property(Actor::food_level);
01124     cur_party[i].flags = npc->get_flags();
01125     cur_party[i].flags2 = npc->get_flags2();
01126   }
01127 
01128   party = cur_party;
01129   screenshot = cur_shot;
01130   details = cur_details;
01131 
01132   // Now read save game details
01133   char  mask[256];
01134 
01135   snprintf(mask, 256, SAVENAME2, Game::get_game_type() == BLACK_GATE ? "bg" : "si");
01136 
01137   FileList filenames;
01138   U7ListFiles (mask, filenames);
01139   num_games = filenames.size();
01140   
01141   games = new SaveInfo[num_games];
01142 
01143   // Setup basic details
01144   for (i = 0; i<num_games; i++)
01145   {
01146     games[i].filename = new char[filenames[i].length()+1];
01147     strcpy (games[i].filename, filenames[i].c_str());
01148     games[i].SetSeqNumber();
01149   }
01150 
01151   // First sort thet games so the will be sorted by number
01152   // This is so I can work out the first free game
01153   if (num_games)
01154     qsort(games, num_games, sizeof(SaveInfo), SaveInfo::CompareGames);
01155 
01156   // Reand and cache all details
01157   first_free = -1;
01158   for (i = 0; i<num_games; i++)
01159   {
01160     games[i].readable = gwin->get_saveinfo(games[i].num, games[i].savename, games[i].screenshot,
01161       games[i].details, games[i].party);
01162 
01163     if (first_free == -1 && i != games[i].num) first_free = i;
01164   }
01165 
01166   if (first_free == -1) first_free = num_games;
01167 
01168   // Now sort it again, with all the details so it can be done by date
01169   if (num_games) qsort(games, num_games, sizeof(SaveInfo), SaveInfo::CompareGames);
01170 
01171 
01172 
01173   // We'll now output the info if debugging
01174 #ifdef DEBUG
01175   cout << "Listing " << num_games << " Save games" << endl;
01176   for (i = 0; i<num_games; i++)
01177     cout << i << " = " << games[i].num << " : " << games[i].filename << " : " << games[i].savename << endl;
01178 
01179   cout << "First Free Game " << first_free << endl;
01180 #endif
01181 }
01182 
01183 void Newfile_gump::FreeSaveGameDetails()
01184 {
01185   delete cur_shot;
01186   cur_shot = 0;
01187   delete cur_details;
01188   cur_details = 0;
01189   delete [] cur_party;
01190   cur_party = 0;
01191 
01192   delete gd_shot;
01193   gd_shot = 0;
01194   delete gd_details;
01195   gd_details = 0;
01196   delete [] gd_party;
01197   gd_party = 0;
01198 
01199   filename = 0;
01200 
01201   // The SaveInfo struct will delete everything that it's got allocated
01202   // So we don't need to worry about that
01203   delete [] games;
01204   games = 0;
01205 }
01206 
01207 // Save Info Methods
01208 
01209 // Constructor
01210 Newfile_gump::SaveInfo::SaveInfo() : num(0), filename(0), savename(0), readable(true),
01211       details(0), party(0), screenshot(0)
01212 {
01213 
01214 }
01215 
01216 // Destructor
01217 Newfile_gump::SaveInfo::~SaveInfo()
01218 {
01219   delete [] filename;
01220   delete [] savename;
01221   delete details;
01222   delete [] party;
01223   delete screenshot;
01224 }
01225 
01226 // Set Sequence Number
01227 void Newfile_gump::SaveInfo::SetSeqNumber()
01228 {
01229   int i;
01230 
01231   for (i = strlen(filename) - 1; !isdigit(filename[i]); i--)
01232     ;
01233   for (; isdigit(filename[i]); i--)
01234     ;
01235 
01236   num = atoi(filename+i+1);
01237 }
01238 
01239 // Compare This
01240 int Newfile_gump::SaveInfo::CompareThis(const SaveInfo *other) const
01241 {
01242   // Check by time first, if possible
01243   if (details && other->details)
01244   {
01245     if (details->real_year < other->details->real_year)
01246       return 1;
01247     if (details->real_year > other->details->real_year)
01248       return -1;
01249 
01250     if (details->real_month < other->details->real_month)
01251       return 1;
01252     if (details->real_month > other->details->real_month)
01253       return -1;
01254 
01255     if (details->real_day < other->details->real_day)
01256       return 1;
01257     if (details->real_day > other->details->real_day)
01258       return -1;
01259 
01260     if (details->real_hour < other->details->real_hour)
01261       return 1;
01262     if (details->real_hour > other->details->real_hour)
01263       return -1;
01264 
01265     if (details->real_minute < other->details->real_minute)
01266       return 1;
01267     if (details->real_minute > other->details->real_minute)
01268       return -1;
01269 
01270     if (details->real_second < other->details->real_second)
01271       return 1;
01272     if (details->real_second > other->details->real_second)
01273       return -1;
01274   }
01275   else if (details) // If the other doesn't have time we are first
01276   {
01277     return -1;
01278   }
01279   else if (other->details)  // If we don't have time we are last
01280   {
01281     return 1;
01282   }
01283   
01284   return num - other->num;
01285 }
01286 
01287 static int _U7SaveSeqNr(const char *a)
01288 {
01289   int i;
01290 
01291 
01292   for (i = strlen((char*)a) - 1; !isdigit(((char*)a)[i]); i--)
01293     ;
01294   for (; isdigit(((char*)a)[i]); i--)
01295     ;
01296 
01297 
01298   return atoi(&a[i+1]);
01299 }
01300 
01301 // Compare Games Static
01302 int Newfile_gump::SaveInfo::CompareGames(const void *a, const void *b)
01303 {
01304   return ((Newfile_gump::SaveInfo*)a)->CompareThis((Newfile_gump::SaveInfo*)b);
01305 }

Generated on Mon Jul 9 14:42:46 2007 for ExultEngine by  doxygen 1.5.1