Some improvements to the GP editor:

- GP are now classified into groups, just like tracks: standard, user defined, and add-ons
    - allow empty GPs to be loaded by the GP manager, so that they can appear
      on the GP editor (but not on the track selection screen)
    - enable/disable buttons depending on the available options
    - provide some feedback after pushing the "save" button
    - some minor bug corrections and improvements
This commit is contained in:
Marc Coll Carrillo 2014-09-13 10:40:27 +02:00
parent d79a5aaf01
commit 5cf3ad220f
10 changed files with 276 additions and 115 deletions

View File

@ -15,7 +15,10 @@
child_height="120" keep_selection="true" />
</box>
<spacer height="10" />
<!-- Populated dynamically at runtime -->
<tabs width="100%" height="30" id="gpgroups"> </tabs>
<spacer height="20" />
<box proportion="2" width="100%" layout="vertical-row">
<label id="gpname" text_align="center" width="100%"

View File

@ -20,6 +20,7 @@
#include "race/grand_prix_data.hpp"
#include "config/player_profile.hpp"
#include "config/user_config.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "io/file_manager.hpp"
@ -39,12 +40,13 @@
/** Loads a grand prix definition from a file.
* \param filename Name of the file to load.
*/
GrandPrixData::GrandPrixData(const std::string& filename)
GrandPrixData::GrandPrixData(const std::string& filename, enum GPGroupType group)
{
m_filename = filename;
m_id = StringUtils::getBasename(
StringUtils::removeExtension(filename));
setFilename(filename);
m_id = StringUtils::getBasename(StringUtils::removeExtension(filename));
m_editable = (filename.find(file_manager->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

View File

@ -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

View File

@ -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 <algorithm>
#include <set>
#include <utility>
#include <sstream>
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<std::string> 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<std::string>::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<std::string> result;
file_manager->listFiles(result, dir);
for(std::set<std::string>::iterator i = result.begin();
i != result.end(); i++)
for(std::set<std::string>::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);

View File

@ -19,6 +19,8 @@
#ifndef HEADER_GRAND_PRIX_MANAGER_HPP
#define HEADER_GRAND_PRIX_MANAGER_HPP
#include "race/grand_prix_data.hpp"
#include <vector>
#include <string>
@ -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();

View File

@ -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<LabelWidget>("title");
assert(header != NULL);
header->setText(m_gp->getName() + (modified ? L" (+)" : L""), true);
enableButtons();
}
// -----------------------------------------------------------------------------
void EditGPScreen::setSelected(const int selected)
{
assert(getWidget<IconButtonWidget>("up") != NULL);
assert(getWidget<IconButtonWidget>("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<IconButtonWidget>("up");
IconButtonWidget* down_button = getWidget<IconButtonWidget>("down");
IconButtonWidget* edit_button = getWidget<IconButtonWidget>("edit");
IconButtonWidget* remove_button = getWidget<IconButtonWidget>("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();
}
}

View File

@ -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;

View File

@ -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<RibbonWidget>("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<DynamicRibbonWidget>("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<DynamicRibbonWidget>("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<RibbonWidget>("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<RibbonWidget>("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<RibbonWidget>("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<DynamicRibbonWidget>("gplist");
assert (gplist_widget != NULL);
DynamicRibbonWidget* tracks_widget = getWidget<DynamicRibbonWidget>("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<IconButtonWidget>("copy");
IconButtonWidget* edit_button = getWidget<IconButtonWidget>("edit");
IconButtonWidget* remove_button = getWidget<IconButtonWidget>("remove");
IconButtonWidget* rename_button = getWidget<IconButtonWidget>("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"???";
}
}

View File

@ -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;

View File

@ -185,6 +185,10 @@ void TracksScreen::init()
const GrandPrixData* gp = grand_prix_manager->getGrandPrix(n);
const std::vector<std::string> tracks = gp->getTrackNames(true);
//Skip epmpty GPs
if (gp->getNumberOfTracks()==0)
continue;
std::vector<std::string> screenshots;
for (unsigned int t=0; t<tracks.size(); t++)
{