/* client/savegamemenu.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include #include "core/application.h" #include "core/gameinterface.h" #include "core/gameserver.h" #include "model/material.h" #include "client/savegamemenu.h" #include "filesystem/filesystem.h" #include "render/screenshot.h" #include "render/textures.h" #include "ui/ui.h" #include "sys/sys.h" namespace client { SaveGameMenu::SaveGameMenu(ui::Widget *parent, const char *label, const Mode mode) : ui::Window(parent) { set_label(label); set_border(true); set_background(true); set_font(ui::root()->font_small()); savegamemenu_mode = mode; // window title savegamemenu_titlelabel = new ui::Label(this); savegamemenu_titlelabel->set_label("title"); savegamemenu_titlelabel->set_background(false); savegamemenu_titlelabel->set_border(false); savegamemenu_titlelabel->set_font(ui::root()->font_large()); savegamemenu_titlelabel->set_alignment(ui::AlignCenter); if (savegamemenu_mode == Save) { savegamemenu_titlelabel->set_text("SAVE GAME"); } else { savegamemenu_titlelabel->set_text("LOAD GAME"); } // close button savegamemenu_closebutton = new ui::IconButton(savegamemenu_titlelabel, "bitmaps/icons/window_close"); savegamemenu_closebutton->set_label("close"); // file listview savegamemenu_filelistview = new ui::ListView(this); savegamemenu_filelistview->set_label("files"); // savegame screenshot savegamemenu_screenshot = new ui::Bitmap(this); savegamemenu_screenshot->set_background(true); savegamemenu_screenshot->set_border(true); // savegame description title savegamemenu_descrtitle = new ui::Label(this); savegamemenu_descrtitle->set_label("subtitle"); savegamemenu_descrtitle->set_background(false); savegamemenu_descrtitle->set_border(false); savegamemenu_descrtitle->set_font(ui::root()->font_large()); savegamemenu_descrtitle->set_alignment(ui::AlignCenter); // savegame description label savegamemenu_descrlabel = new ui::Label(this); savegamemenu_descrlabel->set_label("info"); savegamemenu_descrlabel->set_background(true); savegamemenu_descrlabel->set_border(true); // save button savegamemenu_savebutton = new ui::Button(this, "Save"); savegamemenu_savebutton->set_label("save"); // delete button savegamemenu_deletebutton = new ui::IconButton(this, "bitmaps/icons/button_delete"); savegamemenu_deletebutton->set_label("delete"); // cancel save/load/overwrite button savegamemenu_cancelbutton = new ui::Button(this, "Cancel"); savegamemenu_cancelbutton->set_label("cancel"); // confirm save/load/overwrite button savegamemenu_confirmsavebutton = new ui::Button(this, "Confirm"); savegamemenu_confirmsavebutton->set_label("confirmsave"); // confirm save/load/overwrite button savegamemenu_confirmdeletebutton = new ui::Button(this, "Delete"); savegamemenu_confirmdeletebutton->set_label("confirmdelete"); // description input box savegamemenu_descrinput = new ui::InputBox(this); savegamemenu_descrinput->set_label("description"); savegamemenu_descrinput->hide(); } SaveGameMenu::~SaveGameMenu() { } void SaveGameMenu::resize() { const float padding = ui::root()->font_large()->height(); const float icon_size = 24.0f; // small icons // resize title label savegamemenu_titlelabel->set_size(width() - padding * 2.0f, savegamemenu_titlelabel->font()->height()); savegamemenu_titlelabel->set_location(padding, padding); // resize close button savegamemenu_closebutton->set_size(savegamemenu_titlelabel->font()->height(), savegamemenu_titlelabel->font()->height()); savegamemenu_closebutton->set_location(savegamemenu_titlelabel->width() - savegamemenu_closebutton->width(), 0); // resize files listview savegamemenu_filelistview->set_size( ui::UI::elementsize.width(), height() - icon_size - padding * 3.0f - savegamemenu_titlelabel->bottom() ); savegamemenu_filelistview->set_location( padding, savegamemenu_titlelabel->bottom() + padding ); // resize delete button savegamemenu_deletebutton->set_size(icon_size, icon_size); savegamemenu_deletebutton->set_location( savegamemenu_filelistview->left(), savegamemenu_filelistview->bottom() + padding ); // resize description title savegamemenu_descrtitle->set_size( width() - savegamemenu_filelistview->right() - 2.0f * padding, savegamemenu_descrtitle->font()->height() ); savegamemenu_descrtitle->set_location( savegamemenu_filelistview->right() + padding, savegamemenu_filelistview->top() + ui::UI::elementsize.width() + padding ); // resize description label savegamemenu_descrlabel->set_size( savegamemenu_descrtitle->width(), savegamemenu_filelistview->bottom() - savegamemenu_descrtitle->bottom() - padding ); savegamemenu_descrlabel->set_location( savegamemenu_filelistview->right() + padding, savegamemenu_descrtitle->bottom() + padding ); // resize screenshot model::Material *screenshotmaterial = model::Material::find("ui/screenshot"); if (screenshotmaterial) { float sx = savegamemenu_descrtitle->width() / screenshotmaterial->size().width(); float sy = ui::UI::elementsize.width() / screenshotmaterial->size().height(); float s = math::min (sx, sy); savegamemenu_screenshot->set_size(screenshotmaterial->size().width() * s, screenshotmaterial->size().height() * s); } else { savegamemenu_screenshot->set_size( ui::UI::elementsize.width(), ui::UI::elementsize.width() ); } savegamemenu_screenshot->set_location( savegamemenu_descrtitle->left() + (savegamemenu_descrtitle->width() - savegamemenu_screenshot->width()) * 0.5f, savegamemenu_filelistview->top() + (ui::UI::elementsize.width() - savegamemenu_screenshot->height()) * 0.5f ); // resize save button savegamemenu_savebutton->set_size(ui::UI::elementsize); savegamemenu_savebutton->set_location( savegamemenu_descrlabel->left() + (savegamemenu_descrlabel->width() - ui::UI::elementsize.width()) * 0.5f, savegamemenu_descrlabel->bottom() + padding); // resize save/load/overwrite confirm button savegamemenu_confirmsavebutton->set_size(ui::UI::elementsize); savegamemenu_confirmsavebutton->set_location( savegamemenu_descrlabel->left() + (savegamemenu_descrlabel->width() - 2.0f * ui::UI::elementsize.width() - padding) * 0.5f, savegamemenu_descrlabel->bottom() + padding ); // resize cancel button savegamemenu_cancelbutton->set_size(ui::UI::elementsize); savegamemenu_cancelbutton->set_location( savegamemenu_confirmsavebutton->right() + padding, savegamemenu_descrlabel->bottom() + padding ); // resize confirm delete button savegamemenu_confirmdeletebutton->set_size(ui::UI::elementsize); savegamemenu_confirmdeletebutton->set_location(savegamemenu_confirmsavebutton->location()); } void SaveGameMenu::refresh() { ui::ListItem *listitem = 0; std::string savegamepath(filesystem::writedir()); savegamepath.append("savegames"); filesystem::Directory directory(savegamepath); savegamemenu_filelistview->clear(); savegamemenu_deletebutton->disable(); savegamemenu_savebutton->hide(); savegamemenu_savebutton->enable(); savegamemenu_confirmsavebutton->hide(); savegamemenu_confirmdeletebutton->hide(); savegamemenu_cancelbutton->hide(); if (savegamemenu_mode == Save) { listitem = new ui::ListItem(savegamemenu_filelistview, "NEW SAVEGAME"); listitem->set_label("new"); listitem->set_sortkey("[new]"); listitem->set_font(ui::root()->font_tiny()); listitem->set_height(listitem->font()->height() * 2.0f); savegamemenu_savebutton->set_text("Save"); if (!core::localcontrol()) { savegamemenu_savebutton->disable(); } } else { savegamemenu_savebutton->set_text("Load"); } for (filesystem::Directory::FileNames::const_iterator it = directory.filenames().begin(); it != directory.filenames().end(); ++it) { const std::string filename((*it).c_str()); if (filename.size() < 5) { continue; } std::string ext = filename.substr(filename.size() - 4); aux::lowercase(ext); if (ext.compare(".ini")) { continue; } std::string savename = filename.substr(0, filename.size() - 4); std::string descr; std::string timestamp; std::string info; get_savegame_info(savename, descr, info, timestamp); if (descr.size()) { std::ostringstream descrstr; descrstr << descr << '\n'; descrstr << timestamp; listitem = new ui::ListItem(savegamemenu_filelistview, descrstr.str().c_str()); listitem->set_value(savename); listitem->set_sortkey(timestamp); listitem->set_font(ui::root()->font_tiny()); listitem->set_height(listitem->font()->height() * 3.0f); } } savegamemenu_filelistview->sort_reverse(); savegamemenu_filelistview->event_resize(); show_file_info(); } void SaveGameMenu::hide() { Window::hide(); model::Material *screenshotmaterial = model::Material::find("ui/screenshot"); if (screenshotmaterial && screenshotmaterial->texture().size()) { render::Textures::unload(screenshotmaterial->texture()); screenshotmaterial->set_texture(""); } } void SaveGameMenu::show() { Window::show(); refresh(); } void SaveGameMenu::show_file_info() { savegamemenu_descrtitle->clear(); savegamemenu_descrlabel->clear(); savegamemenu_deletebutton->disable(); savegamemenu_confirmsavebutton->hide(); savegamemenu_confirmdeletebutton->hide(); savegamemenu_cancelbutton->hide(); savegamemenu_descrlabel->hide(); savegamemenu_screenshot->hide(); if (savegamemenu_filelistview->selected()) { savegamemenu_savebutton->show(); savegamemenu_descrlabel->show(); std::string savename(savegamemenu_filelistview->selected()->value().c_str()); if (savename.size()) { std::string descr; std::string timestamp; std::string info; get_savegame_info(savename, descr, info, timestamp); std::ostringstream descrstr; //descrstr << descr << '\n'; descrstr << info; // info has a trailing newline descrstr << timestamp << '\n'; savegamemenu_deletebutton->enable(); savegamemenu_descrtitle->set_text(savename); savegamemenu_descrlabel->set_text(descrstr.str().c_str()); model::Material *screenshotmaterial = model::Material::find("ui/screenshot"); if (!screenshotmaterial) { screenshotmaterial = new model::Material("ui/screenshot"); model::Material::add(screenshotmaterial); screenshotmaterial ->set_flags(model::Material::Texture); screenshotmaterial ->set_flags(model::Material::Bright); } else if (screenshotmaterial->texture().size()) { render::Textures::unload(screenshotmaterial->texture()); } std::string screenshotfilename("savegames/" + savename); screenshotmaterial->set_texture(screenshotfilename); savegamemenu_screenshot->set_texture("ui/screenshot"); savegamemenu_screenshot->show(); resize(); } else { // new savegame, immediatly show the save game dialog show_save_dialog(); return; } } } void SaveGameMenu::show_save_dialog() { if (!savegamemenu_filelistview->selected()) { return; } std::string savename(savegamemenu_filelistview->selected()->value().c_str()); if (!savename.size()) { savegamemenu_descrtitle->set_text("New savegame"); savegamemenu_confirmsavebutton->set_text("Save"); } else { savegamemenu_descrtitle->set_text("Overwrite game " + savename + "?"); savegamemenu_confirmsavebutton->set_text("Overwrite"); } savegamemenu_savebutton->hide(); savegamemenu_confirmsavebutton->show(); savegamemenu_cancelbutton->show(); } void SaveGameMenu::show_load_dialog() { if (!savegamemenu_filelistview->selected()) { return; } std::string savename(savegamemenu_filelistview->selected()->value().c_str()); if (!savename.size()) { return; } savegamemenu_descrtitle->set_text("Load game " + savename + "?"); savegamemenu_confirmsavebutton->set_text("Load"); savegamemenu_savebutton->hide(); savegamemenu_confirmsavebutton->show(); savegamemenu_cancelbutton->show(); } void SaveGameMenu::show_delete_dialog() { if (!savegamemenu_filelistview->selected()) { return; } std::string savename(savegamemenu_filelistview->selected()->value().c_str()); if (!savename.size()) { return; } savegamemenu_descrtitle->set_text(savename); savegamemenu_descrtitle->set_text("Delete game " + savename + "?"); savegamemenu_savebutton->hide(); savegamemenu_confirmdeletebutton->show(); savegamemenu_cancelbutton->show(); } bool SaveGameMenu::on_keypress(const int key, const unsigned int modifier) { if (key == SDLK_ESCAPE) { if (visible()) { if (savegamemenu_cancelbutton->visible()) { show_file_info(); return true; } else { // hide the parent mainwindow parent()->hide(); return true; } } } return false; } bool SaveGameMenu::on_emit(ui::Widget *sender, const ui::Widget::Event event, void *data) { if (sender == savegamemenu_closebutton) { if (event == ui::Widget::EventButtonClicked) { // hide the parent mainwindow parent()->hide(); return true; } } else if (sender == savegamemenu_cancelbutton) { if (event == ui::Widget::EventButtonClicked) { show_file_info(); } } else if (sender == savegamemenu_confirmsavebutton) { if (event == ui::Widget::EventButtonClicked) { if (savegamemenu_mode == Save) { if (savegamemenu_filelistview->selected()) { std::string descr; savegame(savegamemenu_filelistview->selected()->value(), descr); parent()->hide(); } } else { if (savegamemenu_filelistview->selected()) { loadgame(savegamemenu_filelistview->selected()->value()); parent()->hide(); } } return true; } } else if (sender == savegamemenu_savebutton) { if (event == ui::Widget::EventButtonClicked) { if (savegamemenu_mode == Save) { show_save_dialog(); } else { show_load_dialog(); } return true; } } else if (sender == savegamemenu_confirmdeletebutton) { if (event == ui::Widget::EventButtonClicked) { if (savegamemenu_filelistview->selected()) { deletegame(savegamemenu_filelistview->selected()->value()); refresh(); } return true; } } else if (sender == savegamemenu_deletebutton) { if (event == ui::Widget::EventButtonClicked) { if (savegamemenu_filelistview->selected()) { show_delete_dialog(); } return true; } } else if (sender == savegamemenu_filelistview) { if (event == ui::Widget::EventListViewChanged) { show_file_info(); return true; } } return Window::on_emit(sender, event, data); } void SaveGameMenu::get_savegame_info(const std::string & savename, std::string &game_descr, std::string &game_info, std::string &game_timestamp) { filesystem::IniFile inifile("savegames/" + savename); std::string infostr; game_descr.clear(); game_info.clear(); game_timestamp.clear(); if (!inifile.is_open()) { return; } while(inifile.getline()) { if (inifile.got_key()) { if (inifile.in_section("savegame")) { if (inifile.got_key_string("description", game_descr)) { continue; } else if (inifile.got_key_string("timestamp", game_timestamp)) { continue; } else if (inifile.got_key_string("info", infostr)) { game_info.append(infostr); game_info += '\n'; continue; } } } } inifile.close(); } void SaveGameMenu::deletegame(std::string savename) { std::string filename(filesystem::writedir() + "savegames/" + savename + ".ini"); con_debug << "Deleteing " << filename << std::endl; ::remove(filename.c_str()); } void SaveGameMenu::savescreenshot(std::string savename) { std::string filename(filesystem::writedir() + "savegames/" + savename + ".jpg"); render::Screenshot::savegameshot(filename); } void SaveGameMenu::savegame(std::string savename, const std::string & description) { if (!core::server() || !core::server()->module()) return; if (!core::localcontrol()) { return; } // find an approriate savegame name if none was specified if (!savename.size()) { std::string filename; unsigned long n = 0; do { n++; std::ostringstream numberstr; numberstr << std::setfill('0') << std::setw(4) << n; savename.assign("save"); savename.append(numberstr.str()); filename.assign(filesystem::writedir() + "savegames/" + savename + ".ini"); } while (sys::file_exists(filename)); } // create savegame diretory if required std::string savegamepath(filesystem::writedir()); savegamepath.append("savegames"); if (!sys::directory_exists(savegamepath)) { sys::mkdir(savegamepath); } filesystem::OFileStream ofs("savegames/" + savename + ".ini"); core::application()->notify_message(core::Message::Info, "Saving " + savename); con_debug << "Saving " << ofs.filename() << std::endl; ofs << "; Project::OSiRiON" << std::endl; ofs << "; savegame data" << std::endl; ofs << std::endl; ofs << "[savegame]" << std::endl; ofs << "description="; if (description.size()) { ofs << description << std::endl; } else { if (core::localcontrol()->state() == core::Entity::Docked) { // FIXME implement entity->dock() ofs << core::localplayer()->view()->name() << std::endl; } else { ofs << core::localplayer()->zone()->name() << std::endl; } } ofs << "info="; if (core::localcontrol()->state() == core::Entity::Docked) { // FIXME implement entity->dock() ofs << core::localplayer()->view()->name() << ", "; } ofs << core::localplayer()->zone()->name() << std::endl; if (core::localcontrol()->info()) { ofs << "info=" << core::localcontrol()->info()->name() << std::endl; } ofs << "info=" << core::localplayer()->credits() << " credits" << std::endl; int year = 0; int month = 0; int day = 0; int hours = 0; int minutes = 0; int seconds = 0; int msec = 0; sys::get_localtime(year, month, day, hours, minutes, seconds, msec); std::ostringstream timestampstr; timestampstr << std::setfill('0') << std::setw(4) << year << "-" << std::setw(2) << month << "-" << std::setw(2) << day << " "; timestampstr << std::setw(2) << hours << ":" << std::setw(2) << minutes << ":" << std::setw(2) << seconds; ofs << "timestamp=" << timestampstr.str() << std::endl; ofs << std::endl; core::server()->module()->game_save(core::localplayer(), ofs); ofs.close(); // save screenshot savescreenshot(savename); } void SaveGameMenu::loadgame(std::string savename) { filesystem::IniFile inifile("savegames/" + savename); if (!inifile.is_open()) { con_warn << "Could not open savegames/" + savename << ".ini" << std::endl; return; } if (core::application()->connected()) core::application()->disconnect(); core::application()->connect(""); con_debug << "Loading " << inifile.filename() << std::endl; core::application()->notify_message(core::Message::Info, "Loading " + savename); if (core::application()->connected() && core::server() && core::server()->module()) { core::server()->module()->game_load(core::localplayer(), inifile); } inifile.close(); } } // namespace client