diff --git a/data/gui/gpeditor.stkgui b/data/gui/gpeditor.stkgui index 06d78e572..27ba65e59 100644 --- a/data/gui/gpeditor.stkgui +++ b/data/gui/gpeditor.stkgui @@ -15,7 +15,10 @@ child_height="120" keep_selection="true" /> - + + + + getGPDir(), 0) == 0); + m_group = group; + reload(); } // GrandPrixData @@ -56,7 +58,7 @@ GrandPrixData::GrandPrixData(const std::string& filename) * \param new_tracks If true, new tracks are selected, otherwise existing * tracks will not be changed (used to e.g. increase the number of * tracks in an already existing random grand prix). - * + * */ void GrandPrixData::createRandomGP(const unsigned int number_of_tracks, const std::string &track_group, @@ -67,6 +69,7 @@ void GrandPrixData::createRandomGP(const unsigned int number_of_tracks, m_id = "random"; m_name = "Random Grand Prix"; m_editable = false; + m_group = GP_NONE; if(new_tracks) { @@ -202,6 +205,15 @@ void GrandPrixData::setEditable(const bool editable) m_editable = editable; } // setEditable +// ---------------------------------------------------------------------------- +/** Sets the group of this grand prix. + * \param editable New value. + */ +void GrandPrixData::setGroup(const enum GPGroupType group) +{ + m_group = group; +} // setGroup + // ---------------------------------------------------------------------------- /** Reloads grand prix from file. */ @@ -241,10 +253,9 @@ void GrandPrixData::reload() const int amount = root->getNumNodes(); if (amount == 0) { - Log::error("GrandPrixData", - "Error while trying to read grandprix file '%s': " - "There is no track defined", m_filename.c_str()); - throw std::runtime_error("No track defined"); + Log::warn("GrandPrixData", + "Grandprix file '%s': There is no track defined", + m_filename.c_str()); } // Every iteration means parsing one track entry diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp index 36530b84c..47ceb268e 100644 --- a/src/race/grand_prix_data.hpp +++ b/src/race/grand_prix_data.hpp @@ -36,6 +36,17 @@ class Track; */ class GrandPrixData { +public: + /** Used to classify GPs into groups */ + enum GPGroupType + { + GP_NONE = 0, ///< No group + GP_STANDARD, ///< Standard GP, included with the game + GP_USER_DEFINED, ///< Created by the user + GP_ADDONS, ///< Add-on GP + GP_GROUP_COUNT ///< Number of groups + }; // GPGroupType + private: /** The name of the grand prix. */ irr::core::stringw m_name; @@ -60,6 +71,9 @@ private: /** Wether the user can edit this grand prix or not */ bool m_editable; + /** Group to which this GP belongs. */ + enum GPGroupType m_group; + /** In the last GP Fort Magma can not be used untill the final challenge. * In order to provide still 5 tracks/GP, the last GP is only using 4 * tracks in story mode, but once nolok is unlocked Fort Magma becomes @@ -84,7 +98,7 @@ public: # pragma warning(disable:4290) #endif /** Load the GrandPrixData from the given filename */ - GrandPrixData(const std::string& filename); + GrandPrixData(const std::string& filename, enum GPGroupType group); /** Needed for simple creation of an instance of GrandPrixData */ GrandPrixData() {}; @@ -103,6 +117,7 @@ public: void setName(const irr::core::stringw& name); void setFilename(const std::string& filename); void setEditable(const bool editable); + void setGroup(const enum GPGroupType group); /** Load the grand prix from the file set by the constructor or the grand * prix editor */ void reload(); @@ -141,6 +156,10 @@ public: // ------------------------------------------------------------------------ /** Returns the filename of the grand prix xml file. */ const std::string& getFilename() const { return m_filename; } + + // ------------------------------------------------------------------------ + /** Returns the group. */ + enum GPGroupType getGroup() const { return m_group; } }; // GrandPrixData #endif diff --git a/src/race/grand_prix_manager.cpp b/src/race/grand_prix_manager.cpp index ce9f00b6e..73eb589be 100644 --- a/src/race/grand_prix_manager.cpp +++ b/src/race/grand_prix_manager.cpp @@ -20,11 +20,11 @@ #include "config/user_config.hpp" #include "io/file_manager.hpp" -#include "race/grand_prix_data.hpp" #include "utils/string_utils.hpp" #include #include +#include #include GrandPrixManager *grand_prix_manager = NULL; @@ -47,44 +47,47 @@ GrandPrixManager::~GrandPrixManager() // ---------------------------------------------------------------------------- void GrandPrixManager::loadFiles() { + // Add the directories to a set to avoid duplicates std::set dirs; + std::string dir; - // Add all the directories to a set to avoid duplicates - dirs.insert(file_manager->getAsset(FileManager::GRANDPRIX, "")); - dirs.insert(file_manager->getGPDir()); - dirs.insert(UserConfigParams::m_additional_gp_directory); + //Standard GPs + loadDir(file_manager->getAsset(FileManager::GRANDPRIX, ""), GrandPrixData::GP_STANDARD); - for (std::set::const_iterator it = dirs.begin(); - it != dirs.end (); ++it) - { - std::string dir = *it; - if (!dir.empty() && dir[dir.size() - 1] == '/') - loadDir(dir); - } + //User defined GPs + dir = file_manager->getGPDir(); + if (!dir.empty() && dir[dir.size() - 1] == '/' && dirs.count(dir) == 0) + loadDir(dir, GrandPrixData::GP_USER_DEFINED); + + //Add-on GPs + dir = UserConfigParams::m_additional_gp_directory; + if (!dir.empty() && dir[dir.size() - 1] == '/' && dirs.count(dir) == 0) + loadDir(dir, GrandPrixData::GP_ADDONS); } // loadFiles // ---------------------------------------------------------------------------- -void GrandPrixManager::loadDir(const std::string& dir) +void GrandPrixManager::loadDir(const std::string& dir, enum GrandPrixData::GPGroupType group) { Log::info("GrandPrixManager", "Loading Grand Prix files from %s", dir.c_str()); assert(!dir.empty() && dir[dir.size() - 1] == '/'); - // Findout which grand prix are available and load them + // Find out which grand prix are available and load them std::set result; file_manager->listFiles(result, dir); - for(std::set::iterator i = result.begin(); - i != result.end(); i++) + for(std::set::iterator i = result.begin(); i != result.end(); i++) + { if (StringUtils::hasSuffix(*i, SUFFIX)) - load(dir + *i); + load(dir + *i, group); + } } // loadDir // ---------------------------------------------------------------------------- -void GrandPrixManager::load(const std::string& filename) +void GrandPrixManager::load(const std::string& filename, enum GrandPrixData::GPGroupType group) { try { - GrandPrixData* gp = new GrandPrixData(filename); + GrandPrixData* gp = new GrandPrixData(filename, group); m_gp_data.push_back(gp); Log::debug("GrandPrixManager", "Grand Prix '%s' loaded from %s", @@ -188,6 +191,7 @@ GrandPrixData* GrandPrixManager::createNewGP(const irr::core::stringw& newName) gp->setName(newName); gp->setFilename(file_manager->getGPDir() + newID + SUFFIX); gp->setEditable(true); + gp->setGroup(GrandPrixData::GP_USER_DEFINED); gp->writeToFile(); m_gp_data.push_back(gp); diff --git a/src/race/grand_prix_manager.hpp b/src/race/grand_prix_manager.hpp index 5eabbb719..698167688 100644 --- a/src/race/grand_prix_manager.hpp +++ b/src/race/grand_prix_manager.hpp @@ -19,6 +19,8 @@ #ifndef HEADER_GRAND_PRIX_MANAGER_HPP #define HEADER_GRAND_PRIX_MANAGER_HPP +#include "race/grand_prix_data.hpp" + #include #include @@ -38,9 +40,9 @@ private: /** Load all the grands prix from the 3 directories known */ void loadFiles(); /** Load all the grands prix in one directory */ - void loadDir(const std::string& dir); + void loadDir(const std::string& dir, enum GrandPrixData::GPGroupType group); /** Load a grand prix and add it to m_gp_data*/ - void load(const std::string &filename); + void load(const std::string &filename, enum GrandPrixData::GPGroupType group); /** Generates a new unique indentifier for a user defined grand prix */ std::string generateId(); diff --git a/src/states_screens/edit_gp_screen.cpp b/src/states_screens/edit_gp_screen.cpp index a9a3b66d4..2247f81ae 100644 --- a/src/states_screens/edit_gp_screen.cpp +++ b/src/states_screens/edit_gp_screen.cpp @@ -187,6 +187,7 @@ void EditGPScreen::init() loadList(m_selected); m_action.clear(); } + enableButtons(); } // ----------------------------------------------------------------------------- @@ -213,7 +214,10 @@ void EditGPScreen::onCancel() { ModalDialog::dismiss(); if (m_action == "back") + { + m_gp->reload(); // Discard changes back(); + } } // ----------------------------------------------------------------------------- @@ -231,8 +235,14 @@ void EditGPScreen::loadList(const int selected) Track* t = track_manager->getTrack(m_gp->getTrackId(i)); assert(t != NULL); + video::ITexture* screenShot = irr_driver->getTexture(t->getScreenshotFile()); - assert(screenShot != NULL); + if (screenShot == NULL) + { + screenShot = irr_driver->getTexture( + file_manager->getAsset(FileManager::GUI, "main_help.png")); + } + assert (screenShot != NULL); m_icons.push_back(m_icon_bank->addTextureAsSprite(screenShot)); row.push_back(GUIEngine::ListWidget::ListCell( @@ -264,15 +274,19 @@ void EditGPScreen::setModified(const bool modified) save_button->setActivated(); else save_button->setDeactivated(); + + LabelWidget* header = getWidget("title"); + assert(header != NULL); + header->setText(m_gp->getName() + (modified ? L" (+)" : L""), true); + + enableButtons(); } // ----------------------------------------------------------------------------- void EditGPScreen::setSelected(const int selected) { - assert(getWidget("up") != NULL); - assert(getWidget("down") != NULL); - m_selected = selected; + enableButtons(); } // ----------------------------------------------------------------------------- @@ -327,3 +341,38 @@ bool EditGPScreen::canMoveDown() const { return (0 <= m_selected && m_selected < m_list->getItemCount() - 1); } + +// ----------------------------------------------------------------------------- +void EditGPScreen::enableButtons() +{ + IconButtonWidget* up_button = getWidget("up"); + IconButtonWidget* down_button = getWidget("down"); + IconButtonWidget* edit_button = getWidget("edit"); + IconButtonWidget* remove_button = getWidget("remove"); + assert(up_button != NULL); + assert(down_button != NULL); + assert(edit_button != NULL); + assert(remove_button != NULL); + + if (m_selected >= 0 && m_list->getItemCount() > 1) + { + up_button->setActivated(); + down_button->setActivated(); + } + else + { + up_button->setDeactivated(); + down_button->setDeactivated(); + } + + if (m_selected >= 0) + { + edit_button->setActivated(); + remove_button->setActivated(); + } + else + { + edit_button->setDeactivated(); + remove_button->setDeactivated(); + } +} diff --git a/src/states_screens/edit_gp_screen.hpp b/src/states_screens/edit_gp_screen.hpp index 62b260b1e..35adeb591 100644 --- a/src/states_screens/edit_gp_screen.hpp +++ b/src/states_screens/edit_gp_screen.hpp @@ -56,6 +56,8 @@ class EditGPScreen : bool canMoveUp() const; bool canMoveDown() const; + void enableButtons(); + GrandPrixData* m_gp; GUIEngine::ListWidget* m_list; irr::gui::STKModifiedSpriteBank* m_icon_bank; diff --git a/src/states_screens/grand_prix_editor_screen.cpp b/src/states_screens/grand_prix_editor_screen.cpp index 1552835c1..0e2b5c536 100644 --- a/src/states_screens/grand_prix_editor_screen.cpp +++ b/src/states_screens/grand_prix_editor_screen.cpp @@ -23,7 +23,6 @@ #include "guiengine/widgets/dynamic_ribbon_widget.hpp" #include "guiengine/widgets/icon_button_widget.hpp" #include "io/file_manager.hpp" -#include "race/grand_prix_data.hpp" #include "race/grand_prix_manager.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/edit_gp_screen.hpp" @@ -39,12 +38,27 @@ using namespace irr::core; DEFINE_SCREEN_SINGLETON( GrandPrixEditorScreen ); + // ----------------------------------------------------------------------------- GrandPrixEditorScreen::GrandPrixEditorScreen() - : Screen("gpeditor.stkgui"), m_selection(NULL) + : Screen("gpeditor.stkgui"), m_selection(NULL), m_gpgroup(GrandPrixData::GP_NONE) { } +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::beforeAddingWidget() +{ + RibbonWidget* tabs = getWidget("gpgroups"); + assert (tabs != NULL); + + tabs->clearAllChildren(); + for (int i = 0; i < GrandPrixData::GP_GROUP_COUNT; i++) + { + tabs->addTextChild(getGroupName((enum GrandPrixData::GPGroupType)i), + StringUtils::toString(i)); + } +} + // ----------------------------------------------------------------------------- void GrandPrixEditorScreen::loadedFromFile() { @@ -54,13 +68,20 @@ void GrandPrixEditorScreen::loadedFromFile() // ----------------------------------------------------------------------------- void GrandPrixEditorScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) { - DynamicRibbonWidget* gplist_widget = getWidget("gplist"); - assert (gplist_widget != NULL); - std::string selected = gplist_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); - if (!selected.empty()) - setSelection (grand_prix_manager->getGrandPrix(selected)); - - if (name == "menu") + if (name == "gplist") + { + DynamicRibbonWidget* gplist_widget = getWidget("gplist"); + assert (gplist_widget != NULL); + std::string selected = gplist_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (!selected.empty()) + { + if (m_selection != NULL && selected == m_selection->getId() && m_selection->isEditable()) + showEditScreen(m_selection); + else + setSelection (grand_prix_manager->getGrandPrix(selected)); + } + } + else if (name == "menu") { RibbonWidget* menu = getWidget("menu"); assert(menu != NULL); @@ -70,52 +91,40 @@ void GrandPrixEditorScreen::eventCallback(Widget* widget, const std::string& nam { new EnterGPNameDialog(this, 0.5f, 0.4f); } - else if (m_action == "edit") + else if (m_action == "edit" && m_selection != NULL) { - if (m_selection->isEditable()) - { - showEditScreen(m_selection); - } - else - { - new MessageDialog( - _("You can't edit the '%s' grand prix.\nYou might want to copy it first", - m_selection->getName().c_str()), - MessageDialog::MESSAGE_DIALOG_OK, NULL, false); - } + showEditScreen(m_selection); } - else if (m_action == "remove") + else if (m_action == "remove" && m_selection != NULL) { - if (m_selection->isEditable()) - { - new MessageDialog( - _("Are you sure you want to remove '%s'?", m_selection->getName().c_str()), - MessageDialog::MESSAGE_DIALOG_CONFIRM, - this, false); - } - else - { - new MessageDialog( - _("You can't remove '%s'.", m_selection->getName().c_str()), - MessageDialog::MESSAGE_DIALOG_OK, NULL, false); - } + new MessageDialog( + _("Are you sure you want to remove '%s'?", m_selection->getName().c_str()), + MessageDialog::MESSAGE_DIALOG_CONFIRM, + this, false); } - else if (m_action == "rename") + else if (m_action == "rename" && m_selection != NULL) { - if (m_selection->isEditable()) - { - new EnterGPNameDialog(this, 0.5f, 0.4f); - } - else - { - new MessageDialog( - _("You can't rename '%s'.", m_selection->getName().c_str()), - MessageDialog::MESSAGE_DIALOG_OK, NULL, false); - } + new EnterGPNameDialog(this, 0.5f, 0.4f); + } + } + else if (name == "gpgroups") + { + RibbonWidget* tabs = getWidget("gpgroups"); + assert(tabs != NULL); + + enum GrandPrixData::GPGroupType group = (enum GrandPrixData::GPGroupType)atoi( + tabs->getSelectionIDString(PLAYER_ID_GAME_MASTER).c_str()); + if (m_gpgroup != group) + { + m_gpgroup = group; + loadGPList(); + setSelection(NULL); } } else if (name == "back") { + m_gpgroup = GrandPrixData::GP_NONE; + setSelection(NULL); StateManager::get()->escapePressed(); } } @@ -123,27 +132,12 @@ void GrandPrixEditorScreen::eventCallback(Widget* widget, const std::string& nam // ----------------------------------------------------------------------------- void GrandPrixEditorScreen::init() { - if (grand_prix_manager->getNumberOfGrandPrix() > 0) - { - if (m_selection == NULL) - { - loadGPList(); - setSelection (grand_prix_manager->getGrandPrix(0)); - } - else - { - std::string id = m_selection->getId(); - grand_prix_manager->reload(); - loadGPList(); - m_selection = grand_prix_manager->editGrandPrix(id); - m_selection->reload(); - setSelection (m_selection); - } - } - else - { - loadGPList(); - } + RibbonWidget* tabs = getWidget("gpgroups"); + assert (tabs != NULL); + + tabs->select (StringUtils::toString(m_gpgroup), PLAYER_ID_GAME_MASTER); + loadGPList(); + setSelection(m_selection); } // ----------------------------------------------------------------------------- @@ -153,11 +147,25 @@ void GrandPrixEditorScreen::setSelection (const GrandPrixData* gpdata) assert(gpname_widget != NULL); DynamicRibbonWidget* gplist_widget = getWidget("gplist"); assert (gplist_widget != NULL); + DynamicRibbonWidget* tracks_widget = getWidget("tracks"); + assert(tracks_widget != NULL); - m_selection = grand_prix_manager->editGrandPrix(gpdata->getId()); - gpname_widget->setText (gpdata->getName(), true); - gplist_widget->setSelection(m_selection->getId(), PLAYER_ID_GAME_MASTER, true); - loadTrackList (gpdata->getId()); + if (gpdata == NULL) + { + m_selection = NULL; + gpname_widget->setText (L"Please select a Grand Prix", true); + tracks_widget->clearItems(); + tracks_widget->updateItemDisplay(); + } + else + { + m_selection = grand_prix_manager->editGrandPrix(gpdata->getId()); + gpname_widget->setText (gpdata->getName(), true); + gplist_widget->setSelection(m_selection->getId(), PLAYER_ID_GAME_MASTER, true); + loadTrackList (gpdata->getId()); + } + + enableButtons(); } // ----------------------------------------------------------------------------- @@ -219,9 +227,16 @@ void GrandPrixEditorScreen::loadGPList() Track* track = track_manager->getTrack(tracks[t]); sshot_files.push_back(track->getScreenshotFile()); } + if (sshot_files.empty()) + sshot_files.push_back(file_manager->getAsset(FileManager::GUI,"main_help.png")); - gplist_widget->addAnimatedItem(translations->fribidize(gp->getName()), gp->getId(), - sshot_files, 2.0f, 0, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE ); + if (m_gpgroup == GrandPrixData::GP_NONE || m_gpgroup == gp->getGroup()) + { + gplist_widget->addAnimatedItem( + translations->fribidize(gp->getName()), + gp->getId(), sshot_files, 2.0f, 0, + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE ); + } } gplist_widget->updateItemDisplay(); @@ -236,14 +251,45 @@ void GrandPrixEditorScreen::showEditScreen(GrandPrixData* gp) StateManager::get()->pushScreen(edit); } +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::enableButtons() +{ + IconButtonWidget* copy_button = getWidget("copy"); + IconButtonWidget* edit_button = getWidget("edit"); + IconButtonWidget* remove_button = getWidget("remove"); + IconButtonWidget* rename_button = getWidget("rename"); + assert(copy_button != NULL); + assert(edit_button != NULL); + assert(remove_button != NULL); + assert(rename_button != NULL); + + if (m_selection != NULL && m_selection->getNumberOfTracks() > 0) + copy_button->setActivated(); + else + copy_button->setDeactivated(); + + if (m_selection != NULL && m_selection->isEditable()) + { + edit_button->setActivated(); + remove_button->setActivated(); + rename_button->setActivated(); + } + else + { + edit_button->setDeactivated(); + remove_button->setDeactivated(); + rename_button->setDeactivated(); + } +} + // ----------------------------------------------------------------------------- void GrandPrixEditorScreen::onNewGPWithName(const stringw& newName) { - if (m_action == "copy") + if (m_action == "copy" && m_selection != NULL) { setSelection(grand_prix_manager->copy(m_selection->getId(), newName)); } - else if (m_action == "rename") + else if (m_action == "rename" && m_selection != NULL) { m_selection->setName(newName); m_selection->writeToFile(); @@ -254,14 +300,14 @@ void GrandPrixEditorScreen::onNewGPWithName(const stringw& newName) } loadGPList(); - if (m_action != "rename") + if (m_action != "rename" && m_selection != NULL) showEditScreen(m_selection); } // ----------------------------------------------------------------------------- void GrandPrixEditorScreen::onConfirm() { - if (m_action == "remove") + if (m_action == "remove" && m_selection != NULL) { grand_prix_manager->remove(m_selection->getId()); loadGPList(); @@ -270,3 +316,16 @@ void GrandPrixEditorScreen::onConfirm() } ModalDialog::dismiss(); } + +// ---------------------------------------------------------------------------- +const wchar_t* GrandPrixEditorScreen::getGroupName(enum GrandPrixData::GPGroupType group) +{ + switch (group) + { + case GrandPrixData::GP_NONE: return _("All"); + case GrandPrixData::GP_STANDARD: return _("Standard"); + case GrandPrixData::GP_USER_DEFINED: return _("User defined"); + case GrandPrixData::GP_ADDONS: return _("Add-Ons"); + default: return L"???"; + } +} diff --git a/src/states_screens/grand_prix_editor_screen.hpp b/src/states_screens/grand_prix_editor_screen.hpp index 318acf0c0..1c329dfd9 100644 --- a/src/states_screens/grand_prix_editor_screen.hpp +++ b/src/states_screens/grand_prix_editor_screen.hpp @@ -19,6 +19,7 @@ #define HEADER_GRAND_PRIX_EDITOR_SCREEN_HPP #include "guiengine/screen.hpp" +#include "race/grand_prix_data.hpp" #include "states_screens/dialogs/enter_gp_name_dialog.hpp" #include "states_screens/dialogs/message_dialog.hpp" @@ -45,15 +46,22 @@ class GrandPrixEditorScreen : void loadGPList(); void loadTrackList(const std::string& gpname); void showEditScreen(GrandPrixData* gp); + void enableButtons(); void onNewGPWithName(const irr::core::stringw& newName); void onConfirm(); - GrandPrixData* m_selection; - std::string m_action; + static const wchar_t* getGroupName(enum GrandPrixData::GPGroupType group); + + GrandPrixData* m_selection; + std::string m_action; + enum GrandPrixData::GPGroupType m_gpgroup; public: + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + /** \brief implement callback from parent class GUIEngine::Screen */ virtual void loadedFromFile() OVERRIDE; diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 62e94f6df..26f34f77b 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -185,6 +185,10 @@ void TracksScreen::init() const GrandPrixData* gp = grand_prix_manager->getGrandPrix(n); const std::vector tracks = gp->getTrackNames(true); + //Skip epmpty GPs + if (gp->getNumberOfTracks()==0) + continue; + std::vector screenshots; for (unsigned int t=0; t