Apply Marc Coll's Grand Prix editor, thanks! There will be refinements to come for sure, but this is a very nice start

This commit is contained in:
Marianne Gagnon
2014-03-20 21:13:05 -04:00
parent 68935decaa
commit 2f2547420d
39 changed files with 2018 additions and 1235 deletions

BIN
data/gui/down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,54 @@
<stkgui>
<div x="2%" y="1%" width="96%" height="98%" layout="vertical-row" padding="10">
<header id="selected_track" width="80%" height="30"
I18N="No neeed to translate this, it will be overwritten by the track name"
text="" align="center" text_align="center" />
<spacer height="20" />
<!-- Track selection -->
<box width="100%" height="60%" layout="vertical-row">
<ribbon_grid id="tracks" proportion="1" width="100%" height="100%" square_items="true"
label_location="each" align="center" child_width="240" child_height="160" />
</box>
<!-- Populated dynamically at runtime -->
<tabs width="100%" height="30" id="trackgroups"> </tabs>
<spacer height="50" />
<!-- Laps and reverse -->
<div width="100%" height="100" layout="horizontal-row" align="center">
<spacer proportion="1" />
<label id="laps_label" text_align="left"
I18N="In the edit track screen" text="Number of laps:" />
<spacer width="20" />
<spinner id="laps" proportion="1" width="100" min_value="1"
max_value="99" wrap_around="false" />
<spacer proportion="1" />
<label id="reverse_label" text_align="left"
I18N="In the edit track screen" text="Reverse:" />
<spacer width="20" />
<checkbox id="reverse" />
<spacer proportion="1" />
</div>
<!-- Dialog buttons -->
<div width="100%" height="60" layout="horizontal-row">
<spacer proportion="2" />
<button id="ok" text="OK" proportion="1" />
<spacer proportion="1" />
<button id="cancel" text="Cancel" proportion="1" />
<spacer proportion="2" />
</div>
</div>
</stkgui>

View File

@@ -0,0 +1,21 @@
<stkgui>
<div x="2%" y="10%" width="96%" height="80%" layout="vertical-row" >
<label id="title" width="100%" text_align="center" word_wrap="true"
I18N="In the 'add new grand prix' dialog"
text="Please enter the name of the grand prix" proportion="1" />
<spacer height="25" width="10" />
<textbox id="textfield" width="75%" I18N="In the 'add new grand prix' dialog" align="center"/>
<spacer height="20" width="20" />
<button id="cancel" I18N="When configuring input" text="Press ESC to cancel" align="center"/>
<spacer height="15" width="20" />
</div>
</stkgui>

BIN
data/gui/gp_add_track.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
data/gui/gp_copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
data/gui/gp_edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
data/gui/gp_edit_track.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
data/gui/gp_new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
data/gui/gp_remove.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
data/gui/gp_rename.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
data/gui/gp_save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

35
data/gui/gpedit.stkgui Normal file
View File

@@ -0,0 +1,35 @@
<stkgui>
<icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png" />
<div x="2%" y="1%" width="96%" height="98%" layout="vertical-row">
<header id="title" width="80%" I18N="Title in edit grand prix screen"
text="Edit Grand Prix" align="center" text_align="center" />
<spacer width="100%" height="2%" />
<box proportion="5" width="98%" align="center" layout="vertical-row" padding="6">
<list id="tracks" x="0" y="0" width="100%" height="100%" keep_selection="true" />
</box>
<spacer width="100%" height="2%" />
<buttonbar proportion="1" id="menu" height="135" width="100%" align="center">
<icon-button id="up" width="128" height="128" icon="gui/up.png"
I18N="Menu item" text="Move up" />
<icon-button id="down" width="128" height="128" icon="gui/down.png"
I18N="Menu item" text="Move down" />
<icon-button id="add" width="128" height="128" icon="gui/gp_add_track.png"
I18N="Menu item" text="Add" />
<icon-button id="edit" width="128" height="128" icon="gui/gp_edit_track.png"
I18N="Menu item" text="Edit" />
<icon-button id="remove" width="128" height="128" icon="gui/gp_remove_track.png"
I18N="Menu item" text="Remove" />
<icon-button id="save" width="128" height="128" icon="gui/gp_save.png"
I18N="Menu item" text="Save" />
</buttonbar>
</div>
</stkgui>

45
data/gui/gpeditor.stkgui Normal file
View File

@@ -0,0 +1,45 @@
<stkgui>
<icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
<div x="2%" y="1%" width="96%" height="98%" layout="vertical-row" >
<header width="80%" I18N="Title in grand prix editor screen" text="Grand Prix editor"
align="center" text_align="center" />
<spacer height="20" />
<box proportion="4" width="100%" layout="vertical-row">
<ribbon_grid id="gplist" proportion="1" width="100%" square_items="true"
label_location="each" align="left" max_rows="2" child_width="160"
child_height="120" keep_selection="true" />
</box>
<spacer height="10" />
<box proportion="2" width="100%" layout="vertical-row">
<label id="gpname" text_align="center" width="100%"
I18N="In the grand prix editor screen" text="" />
<ribbon_grid id="tracks" proportion="1" width="100%" square_items="true"
label_location="each" align="left" max_rows="1"
child_width="160" child_height="120" />
</box>
<spacer height="20" />
<buttonbar proportion="1" id="menu" height="135" width="100%" align="center">
<icon-button id="new" width="128" height="128" icon="gui/gp_new.png"
I18N="Menu item" text="New" />
<icon-button id="copy" width="128" height="128" icon="gui/gp_copy.png"
I18N="Menu item" text="Copy" />
<icon-button id="edit" width="128" height="128" icon="gui/gp_edit.png"
I18N="Menu item" text="Edit" />
<icon-button id="remove" width="128" height="128" icon="gui/gp_remove.png"
I18N="Menu item" text="Remove" />
<icon-button id="rename" width="128" height="128" icon="gui/gp_rename.png"
I18N="Menu item" text="Rename" />
</buttonbar>
</div>
</stkgui>

View File

@@ -1,9 +1,9 @@
<stkgui>
<div x="0" y="0" width="100%" height="100%" layout="vertical-row" >
<icon id="logo" align="center" proportion="5" width="100%" icon="gui/logo.png"/>
<buttonbar id="menu_toprow" proportion="3" width="90%" align="center">
<icon-button id="story" width="128" height="128"
icon="gui/menu_story.png" focus_icon="gui/menu_story_focus.png"
@@ -21,35 +21,37 @@
icon="gui/menu_addons.png" focus_icon="gui/menu_addons_focus.png"
I18N="Main menu button" text="Addons"/>
</buttonbar>
<spacer width="10" height="7%"/>
<bottombar width="100%" height="10%" layout="horizontal-row">
<spacer width="10" height="10" />
<label proportion="3" height="100%" id="info_addons"
I18N="In the main screen"
text=""
align="center" text_align="left" />
<spacer width="10" height="10" />
<buttonbar id="menu_bottomrow" x="0" y="0" width="30%" height="100%" align="center">
<buttonbar id="menu_bottomrow" x="0" y="0" width="38%" height="100%" align="center">
<icon-button id="options" width="64" height="64" icon="gui/main_options.png" extend_label="50"
I18N="Main menu button" text="Options" label_location="hover"/>
<icon-button id="help" width="64" height="64" icon="gui/main_help.png" extend_label="50"
I18N="Main menu button" text="Help" label_location="hover"/>
<icon-button id="startTutorial" width="64" height="64" icon="gui/tutorial.png" extend_label="150"
I18N="Main menu button" text="Tutorial" label_location="hover"/>
<icon-button id="gpEditor" width="64" height="64" icon="gui/gpeditor.png" extend_label="150"
I18N="Main menu button" text="Grand Prix Editor" label_location="hover"/>
<icon-button id="about" width="64" height="64" icon="gui/main_about.png" extend_label="50"
I18N="Main menu button" text="About" label_location="hover"/>
<icon-button id="quit" width="64" height="64" icon="gui/main_quit.png" extend_label="50"
I18N="Main menu button" text="Quit" label_location="hover"/>
</buttonbar>
</bottombar>
</div>
</stkgui>

BIN
data/gui/up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -227,6 +227,7 @@ src/states_screens/dialogs/addons_loading.cpp
src/states_screens/dialogs/change_password_dialog.cpp
src/states_screens/dialogs/confirm_resolution_dialog.cpp
src/states_screens/dialogs/custom_video_settings.cpp
src/states_screens/dialogs/enter_gp_name_dialog.cpp
src/states_screens/dialogs/enter_player_name_dialog.cpp
src/states_screens/dialogs/gp_info_dialog.cpp
src/states_screens/dialogs/message_dialog.cpp
@@ -243,7 +244,10 @@ src/states_screens/dialogs/tutorial_message_dialog.cpp
src/states_screens/dialogs/user_info_dialog.cpp
src/states_screens/dialogs/vote_dialog.cpp
src/states_screens/easter_egg_screen.cpp
src/states_screens/edit_gp_screen.cpp
src/states_screens/edit_track_screen.cpp
src/states_screens/feature_unlocked.cpp
src/states_screens/grand_prix_editor_screen.cpp
src/states_screens/grand_prix_lose.cpp
src/states_screens/grand_prix_win.cpp
src/states_screens/guest_login_screen.cpp
@@ -563,6 +567,7 @@ src/states_screens/dialogs/addons_loading.hpp
src/states_screens/dialogs/change_password_dialog.hpp
src/states_screens/dialogs/confirm_resolution_dialog.hpp
src/states_screens/dialogs/custom_video_settings.hpp
src/states_screens/dialogs/enter_gp_name_dialog.hpp
src/states_screens/dialogs/enter_player_name_dialog.hpp
src/states_screens/dialogs/gp_info_dialog.hpp
src/states_screens/dialogs/message_dialog.hpp
@@ -579,7 +584,10 @@ src/states_screens/dialogs/tutorial_message_dialog.hpp
src/states_screens/dialogs/user_info_dialog.hpp
src/states_screens/dialogs/vote_dialog.hpp
src/states_screens/easter_egg_screen.hpp
src/states_screens/edit_gp_screen.hpp
src/states_screens/edit_track_screen.hpp
src/states_screens/feature_unlocked.hpp
src/states_screens/grand_prix_editor_screen.hpp
src/states_screens/grand_prix_lose.hpp
src/states_screens/grand_prix_win.hpp
src/states_screens/guest_login_screen.hpp

View File

@@ -26,6 +26,7 @@ STKModifiedSpriteBank::STKModifiedSpriteBank(IGUIEnvironment* env) :
#endif
m_scale = 1.0f;
m_height = 0;
if (Environment)
{
@@ -62,8 +63,8 @@ core::array< core::rect<s32> >& STKModifiedSpriteBank::getPositions()
for (int n=0; n<(int)Rectangles.size(); n++)
{
const int h = (int)(Rectangles[n].getHeight()*m_scale);
const int w = (int)(Rectangles[n].getWidth() *m_scale);
const int h = getScaledHeight(Rectangles[n].getHeight());
const int w = getScaledWidth(Rectangles[n].getWidth());
copy.push_back( core::rect<s32>(Rectangles[n].UpperLeftCorner,
core::dimension2d<s32>(w,h) )
);
@@ -203,8 +204,8 @@ void STKModifiedSpriteBank::draw2DSprite(u32 index,
const core::dimension2d<s32>& dim = r.getSize();
core::rect<s32> dest( pos,
core::dimension2d<s32>((int)(dim.Width*m_scale),
(int)(dim.Height*m_scale)) );
core::dimension2d<s32>(getScaledWidth(dim.Width),
getScaledHeight(dim.Height)));
if (center)
{
dest -= dest.getSize() / 2;
@@ -295,6 +296,30 @@ void STKModifiedSpriteBank::draw2DSpriteBatch(const core::array<u32>& indices,
}
} // draw2DSpriteBatch
// ----------------------------------------------------------------------------
void STKModifiedSpriteBank::scaleToHeight(int height)
{
m_height = height;
}
// ----------------------------------------------------------------------------
s32 STKModifiedSpriteBank::getScaledWidth(s32 width) const
{
if (m_height == 0)
return (s32)((float)width * m_scale);
else
return m_height;
}
// ----------------------------------------------------------------------------
s32 STKModifiedSpriteBank::getScaledHeight(s32 height) const
{
if (m_height == 0)
return (s32)((float)height * m_scale);
else
return m_height;
}
// ----------------------------------------------------------------------------
} // namespace gui
} // namespace irr

View File

@@ -67,12 +67,15 @@ public:
m_scale = scale;
}
void scaleToHeight(int height);
protected:
// this object was getting access after being freed, I wanna see when/why
unsigned int m_magic_number;
float m_scale;
int m_height;
struct SDrawBatch
{
@@ -91,6 +94,8 @@ protected:
IGUIEnvironment* Environment;
video::IVideoDriver* Driver;
s32 getScaledWidth(s32 width) const;
s32 getScaledHeight(s32 height) const;
};
} // end namespace gui

View File

@@ -486,6 +486,14 @@ namespace GUIEngine
Used on divs, indicate by how many pixels to pad contents
\n
\subsection prop20 PROP_KEEP_SELECTION
<em> Name in XML files: </em> \c "keep_selection"
Used on lists, indicates that the list should keep showing the selected item
even when it doesn't have the focus
\n
<HR>
\section code Using the engine in code

View File

@@ -221,6 +221,7 @@ if(prop_name != NULL) widget.m_properties[prop_flag] = core::stringc(prop_name).
READ_PROPERTY(max_rows, PROP_MAX_ROWS);
READ_PROPERTY(wrap_around, PROP_WRAP_AROUND);
READ_PROPERTY(padding, PROP_DIV_PADDING);
READ_PROPERTY(keep_selection, PROP_KEEP_SELECTION);
#undef READ_PROPERTY
const wchar_t* text = xml->getAttributeValue( L"text" );

View File

@@ -103,7 +103,8 @@ namespace GUIEngine
PROP_LABELS_LOCATION,
PROP_MAX_ROWS,
PROP_WRAP_AROUND,
PROP_DIV_PADDING
PROP_DIV_PADDING,
PROP_KEEP_SELECTION,
};
bool isWithinATextBox();

View File

@@ -299,7 +299,8 @@ void ListWidget::unfocused(const int playerID, Widget* new_focus)
CGUISTKListBox* list = getIrrlichtElement<CGUISTKListBox>();
// remove selection when leaving list
if (list != NULL) list->setSelected(-1);
if (list != NULL && m_properties[PROP_KEEP_SELECTION] != "true")
list->setSelected(-1);
}
// -----------------------------------------------------------------------------

View File

@@ -188,10 +188,11 @@ FileManager::FileManager()
addRootDirs(root_dir+"../../stk-assets");
if ( getenv ( "SUPERTUXKART_ROOT_PATH" ) != NULL )
addRootDirs(getenv("SUPERTUXKART_ROOT_PATH"));
checkAndCreateConfigDir();
checkAndCreateAddonsDir();
checkAndCreateScreenshotDir();
checkAndCreateGPDir();
#ifdef WIN32
redirectOutput();
@@ -203,12 +204,14 @@ FileManager::FileManager()
for(unsigned int i=0; i<m_root_dirs.size(); i++)
Log::info("[FileManager]", "Data files will be fetched from: '%s'",
m_root_dirs[i].c_str());
Log::info("[FileManager]", "User directory is '%s'.",
Log::info("[FileManager]", "User directory is '%s'.",
m_user_config_dir.c_str());
Log::info("[FileManager]", "Addons files will be stored in '%s'.",
m_addons_dir.c_str());
Log::info("[FileManager]", "Screenshots will be stored in '%s'.",
m_screenshot_dir.c_str());
Log::info("[FileManager]", "User-defined grand prix will be stored in '%s'.",
m_gp_dir.c_str());
/** Now search for the path to all needed subdirectories. */
// ==========================================================
@@ -270,7 +273,7 @@ void FileManager::reInit()
// Note that we can't push the texture search path in the constructor
// since this also adds a file archive to te file system - and
// m_file_system is deleted (in irr_driver) after
// m_file_system is deleted (in irr_driver) after
pushTextureSearchPath(m_subdir_name[TEXTURE]);
if(fileExists(m_subdir_name[TEXTURE]+"deprecated/"))
pushTextureSearchPath(m_subdir_name[TEXTURE]+"deprecated/");
@@ -543,7 +546,7 @@ std::string FileManager::getAssetChecked(FileManager::AssetType type,
} // getAssetChecked
//-----------------------------------------------------------------------------
/** Returns the full path of a file of the given asset class. It is not
/** Returns the full path of a file of the given asset class. It is not
* checked if the file actually exists (use getAssetChecked() instead if
* checking is needed).
* \param type Type of the asset class.
@@ -577,7 +580,15 @@ std::string FileManager::getScreenshotDir() const
} // getScreenshotDir
//-----------------------------------------------------------------------------
/** Returns the full path of a texture file name by searching in all
/** Returns the directory in which user-defined grand prix should be stored.
*/
std::string FileManager::getGPDir() const
{
return m_gp_dir;
} // getGPDir
//-----------------------------------------------------------------------------
/** Returns the full path of a texture file name by searching in all
* directories currently in the texture search path. The difference to
* a call getAsset(TEXTURE,...) is that the latter will only return
* textures from .../textures, while the searchTexture will also
@@ -666,7 +677,7 @@ bool FileManager::checkAndCreateDirectoryP(const std::string &path)
//-----------------------------------------------------------------------------
/** Checks if the config directory exists, and it not, tries to create it.
* It will set m_user_config_dir to the path to which user-specific config
* It will set m_user_config_dir to the path to which user-specific config
* files are stored.
*/
void FileManager::checkAndCreateConfigDir()
@@ -725,7 +736,7 @@ void FileManager::checkAndCreateConfigDir()
}
else if (!getenv("HOME"))
{
Log::error("[FileManager]",
Log::error("[FileManager]",
"No home directory, this should NOT happen "
"- trying '.' for config files!");
m_user_config_dir = ".";
@@ -737,7 +748,7 @@ void FileManager::checkAndCreateConfigDir()
if(!checkAndCreateDirectory(m_user_config_dir))
{
// If $HOME/.config can not be created:
Log::error("[FileManager]",
Log::error("[FileManager]",
"Cannot create directory '%s', falling back to use '%s'",
m_user_config_dir.c_str(), getenv("HOME"));
m_user_config_dir = getenv("HOME");
@@ -824,6 +835,32 @@ void FileManager::checkAndCreateScreenshotDir()
} // checkAndCreateScreenshotDir
// ----------------------------------------------------------------------------
/** Creates the directories for user-defined grand prix. This will set m_gp_dir
* with the appropriate path.
*/
void FileManager::checkAndCreateGPDir()
{
#if defined(WIN32) || defined(__CYGWIN__)
m_gp_dir = m_user_config_dir + "grandprix/";
#elif defined(__APPLE__)
m_gp_dir = getenv("HOME");
m_gp_dir += "/Library/Application Support/SuperTuxKart/grandprix/";
#else
m_gp_dir = checkAndCreateLinuxDir("XDG_DATA_HOME", "supertuxkart",
".local/share", ".supertuxkart");
m_gp_dir += "grandprix/";
#endif
if(!checkAndCreateDirectory(m_gp_dir))
{
Log::error("FileManager", "Can not create user-defined grand prix directory '%s', "
"falling back to '.'.", m_gp_dir.c_str());
m_gp_dir = ".";
}
} // checkAndCreateGPDir
// ----------------------------------------------------------------------------
#if !defined(WIN32) && !defined(__CYGWIN__) && !defined(__APPLE__)

File diff suppressed because it is too large Load Diff

View File

@@ -46,10 +46,10 @@ class FileManager : public NoCopy
public:
/** The various asset types (and directories) STK might request.
* The last entry ASSET_COUNT specifies the number of entries. */
enum AssetType {ASSET_MIN,
CHALLENGE=ASSET_MIN,
enum AssetType {ASSET_MIN,
CHALLENGE=ASSET_MIN,
FONT, GFX, GRANDPRIX, GUI, MODEL, MUSIC,
SFX, SHADER, SKIN, TEXTURE, TRANSLATION,
SFX, SHADER, SKIN, TEXTURE, TRANSLATION,
ASSET_MAX = TRANSLATION,
ASSET_COUNT};
private:
@@ -72,6 +72,9 @@ private:
/** Directory to store screenshots in. */
std::string m_screenshot_dir;
/** Directory where user-defined grand prix are stored. */
std::string m_gp_dir;
std::vector<std::string>
m_texture_search_path,
m_model_search_path,
@@ -88,6 +91,7 @@ private:
bool isDirectory(const std::string &path) const;
void checkAndCreateAddonsDir();
void checkAndCreateScreenshotDir();
void checkAndCreateGPDir();
#if !defined(WIN32) && !defined(__CYGWIN__) && !defined(__APPLE__)
std::string checkAndCreateLinuxDir(const char *env_name,
const char *dir_name,
@@ -106,6 +110,7 @@ public:
XMLNode *createXMLTreeFromString(const std::string & content);
std::string getScreenshotDir() const;
std::string getGPDir() const;
bool checkAndCreateDirectoryP(const std::string &path);
const std::string &getAddonsDir() const;
std::string getAddonsFile(const std::string &name);

View File

@@ -23,37 +23,62 @@
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "io/file_manager.hpp"
#include "io/utf_writer.hpp"
#include "tracks/track_manager.hpp"
#include "tracks/track.hpp"
#include "utils/string_utils.hpp"
#include "utils/translation.hpp"
#include <iostream>
#include <memory>
#include <algorithm>
#include <stdexcept>
GrandPrixData::GrandPrixData(const std::string filename)
{
load_from_file(file_manager->getAsset(FileManager::GRANDPRIX, filename),
filename);
}
// ----------------------------------------------------------------------------
GrandPrixData::GrandPrixData(const std::string dir, const std::string filename)
{
assert(dir[dir.size() - 1] == '/');
load_from_file(dir + filename, filename);
}
// ----------------------------------------------------------------------------
void GrandPrixData::load_from_file(const std::string fullpath,
const std::string filename)
GrandPrixData::GrandPrixData(const std::string& filename) throw(std::logic_error)
{
m_filename = filename;
m_id = StringUtils::getBasename(StringUtils::removeExtension(filename));
m_editable = (filename.find(file_manager->getGPDir(), 0) == 0);
reload();
}
XMLNode * root = file_manager->createXMLTree(fullpath);
if (!root)
// ----------------------------------------------------------------------------
void GrandPrixData::setId(const std::string& id)
{
m_id = id;
}
// ----------------------------------------------------------------------------
void GrandPrixData::setName(const irr::core::stringw& name)
{
m_name = name;
}
// ----------------------------------------------------------------------------
void GrandPrixData::setFilename(const std::string& filename)
{
m_filename = filename;
}
// ----------------------------------------------------------------------------
void GrandPrixData::setEditable(const bool editable)
{
m_editable = editable;
}
// ----------------------------------------------------------------------------
void GrandPrixData::reload()
{
m_tracks.clear();
m_laps.clear();
m_reversed.clear();
std::auto_ptr<XMLNode> root(file_manager->createXMLTree(m_filename));
if (root.get() == NULL)
{
Log::error("GrandPrixData","Error while trying to read grandprix file "
"'%s'", fullpath.c_str());
Log::error("GrandPrixData","Error while trying to read grandprix file '%s'",
m_filename.c_str());
throw std::logic_error("File not found");
}
@@ -61,24 +86,18 @@ void GrandPrixData::load_from_file(const std::string fullpath,
if (root->getName() == "supertuxkart_grand_prix")
{
std::string temp_name;
if (root->get("name", &temp_name) == 0)
if (root->get("name", &m_name) == 0)
{
Log::error("GrandPrixData", "Error while trying to read grandprix "
"file '%s' : missing 'name' attribute\n",
fullpath.c_str());
delete root;
Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : "
"missing 'name' attribute\n", m_filename.c_str());
throw std::logic_error("File contents are incomplete or corrupt");
}
m_name = temp_name.c_str();
foundName = true;
}
else
{
Log::error("GrandPrixData", "Error while trying to read grandprix file "
"'%s' : Root node has an unexpected name\n",
fullpath.c_str());
delete root;
Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : "
"Root node has an unexpected name\n", m_filename.c_str());
throw std::logic_error("File contents are incomplete or corrupt");
}
@@ -95,20 +114,17 @@ void GrandPrixData::load_from_file(const std::string fullpath,
int numLaps;
bool reversed = false;
const int idFound = node->get("id", &trackID);
const int lapFound = node->get("laps", &numLaps);
const int idFound = node->get("id", &trackID );
const int lapFound = node->get("laps", &numLaps );
// Will stay false if not found
node->get("reverse", &reversed );
if (!idFound || !lapFound)
{
Log::error("GrandPrixData", "Error while trying to read "
"grandprix file '%s' : <track> tag does not have "
"idi and laps reverse attributes. \n",
fullpath.c_str());
delete root;
throw std::logic_error("File contents are incomplete or "
"corrupt");
Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : "
"<track> tag does not have idi and laps reverse attributes. \n",
m_filename.c_str());
throw std::logic_error("File contents are incomplete or corrupt");
}
// Make sure the track really is reversible
@@ -127,27 +143,58 @@ void GrandPrixData::load_from_file(const std::string fullpath,
}
else
{
Log::error("Unknown node in Grand Prix XML file: %s/n",
node->getName().c_str());
delete root;
std::cerr << "Unknown node in Grand Prix XML file : " << node->getName().c_str() << std::endl;
throw std::runtime_error("Unknown node in sfx XML file");
}
}// end for
delete root;
}// nend for
// sanity checks
if (!foundName)
{
Log::error("GrandPrixData", "Error while trying to read grandprix file "
"'%s' : missing 'name' attribute\n", fullpath.c_str());
Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : "
"missing 'name' attribute\n", m_filename.c_str());
throw std::logic_error("File contents are incomplete or corrupt");
}
}
// ----------------------------------------------------------------------------
bool GrandPrixData::writeToFile()
{
try
{
UTFWriter file(m_filename.c_str());
if (file.is_open())
{
file << L"\n<supertuxkart_grand_prix name=\"" << m_name << L"\">\n\n";
for (unsigned int i = 0; i < getNumberOfTracks(); i++)
{
file <<
L"\t<track id=\"" << m_tracks[i] <<
L"\" laps=\"" << m_laps[i] <<
L"\" reverse=\"" << (m_reversed[i] ? L"true" : L"false") <<
L"\" />\n";
}
file << L"\n</supertuxkart_grand_prix>\n";
file.close();
return true;
}
return false;
}
catch (std::runtime_error& e)
{
Log::error("GrandPrixData", "Failed to write '%s'; cause: %s\n",
m_filename.c_str(), e.what());
return false;
}
}
// ----------------------------------------------------------------------------
bool GrandPrixData::checkConsistency(bool chatty) const
{
for(unsigned int i=0; i<m_tracks.size(); i++)
for(unsigned int i=0; i<getNumberOfTracks(); i++)
{
Track* t = track_manager->getTrack(m_tracks[i]);
@@ -166,6 +213,7 @@ bool GrandPrixData::checkConsistency(bool chatty) const
return true;
} // checkConsistency
// ----------------------------------------------------------------------------
/** Returns true if the track is available. This is used to test if Fort Magma
* is available (this way FortMagma is not used in the last Grand Prix in
@@ -182,7 +230,7 @@ bool GrandPrixData::isTrackAvailable(const std::string &id) const
void GrandPrixData::getLaps(std::vector<int> *laps) const
{
laps->clear();
for(unsigned int i=0; i< m_tracks.size(); i++)
for(unsigned int i=0; i< getNumberOfTracks(); i++)
if(isTrackAvailable(m_tracks[i]))
laps->push_back(m_laps[i]);
} // getLaps
@@ -191,16 +239,129 @@ void GrandPrixData::getLaps(std::vector<int> *laps) const
void GrandPrixData::getReverse(std::vector<bool> *reverse) const
{
reverse->clear();
for(unsigned int i=0; i< m_tracks.size(); i++)
for(unsigned int i=0; i< getNumberOfTracks(); i++)
if(isTrackAvailable(m_tracks[i]))
reverse->push_back(m_reversed[i]);
} // getReverse
// ----------------------------------------------------------------------------
bool GrandPrixData::isEditable() const
{
return m_editable;
} // isEditable
// ----------------------------------------------------------------------------
unsigned int GrandPrixData::getNumberOfTracks() const
{
return m_tracks.size();
}
// ----------------------------------------------------------------------------
irr::core::stringw GrandPrixData::getTrackName(const unsigned int track) const
{
assert(track < getNumberOfTracks());
Track* t = track_manager->getTrack(m_tracks[track]);
assert(t != NULL);
return t->getName();
}
// ----------------------------------------------------------------------------
const std::string& GrandPrixData::getTrackId(const unsigned int track) const
{
assert(track < getNumberOfTracks());
return m_tracks[track];
}
// ----------------------------------------------------------------------------
unsigned int GrandPrixData::getLaps(const unsigned int track) const
{
assert(track < getNumberOfTracks());
return m_laps[track];
}
// ----------------------------------------------------------------------------
bool GrandPrixData::getReverse(const unsigned int track) const
{
assert(track < getNumberOfTracks());
return m_reversed[track];
}
// ----------------------------------------------------------------------------
void GrandPrixData::moveUp(const unsigned int track)
{
assert (track > 0 && track < getNumberOfTracks());
std::swap(m_tracks[track], m_tracks[track - 1]);
std::swap(m_laps[track], m_laps[track - 1]);
m_reversed.swap(m_reversed[track], m_reversed[track - 1]);
}
// ----------------------------------------------------------------------------
void GrandPrixData::moveDown(const unsigned int track)
{
assert (track < (getNumberOfTracks() - 1));
std::swap(m_tracks[track], m_tracks[track + 1]);
std::swap(m_laps[track], m_laps[track + 1]);
m_reversed.swap(m_reversed[track], m_reversed[track + 1]);
}
// ----------------------------------------------------------------------------
void GrandPrixData::addTrack(Track* track, unsigned int laps, bool reverse,
int position)
{
int n;
n = getNumberOfTracks();
assert (track != NULL);
assert (laps > 0);
assert (position >= -1 && position < n);
if (position < 0 || position == (n - 1) || m_tracks.empty())
{
//Append new track to the end of the list
m_tracks.push_back(track->getIdent());
m_laps.push_back(laps);
m_reversed.push_back(reverse);
}
else
{
//Insert new track right after the specified position. Caution:
//std::vector inserts elements _before_ the specified position
m_tracks.insert(m_tracks.begin() + position + 1, track->getIdent());
m_laps.insert(m_laps.begin() + position + 1, laps);
m_reversed.insert(m_reversed.begin() + position + 1, reverse);
}
}
// ----------------------------------------------------------------------------
void GrandPrixData::editTrack(unsigned int t, Track* track,
unsigned int laps, bool reverse)
{
assert (t < getNumberOfTracks());
assert (track != NULL);
assert (laps > 0);
m_tracks[t] = track->getIdent();
m_laps[t] = laps;
m_reversed[t] = reverse;
}
// ----------------------------------------------------------------------------
void GrandPrixData::remove(const unsigned int track)
{
assert (track < getNumberOfTracks());
m_tracks.erase(m_tracks.begin() + track);
m_laps.erase(m_laps.begin() + track);
m_reversed.erase(m_reversed.begin() + track);
}
// ----------------------------------------------------------------------------
const std::vector<std::string>& GrandPrixData::getTrackNames() const
{
m_really_available_tracks.clear();
for(unsigned int i=0; i< m_tracks.size(); i++)
for(unsigned int i=0; i< getNumberOfTracks(); i++)
{
if(isTrackAvailable(m_tracks[i]))
m_really_available_tracks.push_back(m_tracks[i]);

View File

@@ -27,7 +27,8 @@
#include <stdexcept>
#include "utils/translation.hpp"
#include "io/file_manager.hpp"
class Track;
/** Simple class that hold the data relevant to a 'grand_prix', aka. a number
* of races that has to be completed one after the other
@@ -65,28 +66,49 @@ private:
/** Whether the track in question should be done in reverse mode */
std::vector<bool> m_reversed;
void load_from_file(const std::string fullpath, const std::string filename);
/** Wether the user can edit this grand prix or not */
bool m_editable;
bool isTrackAvailable(const std::string &id) const;
public:
/** Load the GrandPrixData from the given filename */
#if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__)
#pragma warning(disable:4290)
#endif
GrandPrixData () {}; // empty for initialising
GrandPrixData(const std::string filename);
GrandPrixData (const std::string dir, const std::string filename);
GrandPrixData (const std::string& filename) throw(std::logic_error);
GrandPrixData () {}; // empty for initialising
void setId(const std::string& id);
void setName(const irr::core::stringw& name);
void setFilename(const std::string& filename);
void setEditable(const bool editable);
void reload();
bool writeToFile();
bool checkConsistency(bool chatty=true) const;
bool checkConsistency(bool chatty=true) const;
const std::vector<std::string>& getTrackNames() const;
void getLaps(std::vector<int> *laps) const;
void getReverse(std::vector<bool> *reverse) const;
void getLaps(std::vector<int> *laps) const;
void getReverse(std::vector<bool> *reverse) const;
bool isEditable() const;
unsigned int getNumberOfTracks() const;
const std::string& getTrackId(const unsigned int track) const;
irr::core::stringw getTrackName(const unsigned int track) const;
unsigned int getLaps(const unsigned int track) const;
bool getReverse(const unsigned int track) const;
void moveUp(const unsigned int track);
void moveDown(const unsigned int track);
void addTrack(Track* track, unsigned int laps,
bool reverse, int position=-1);
void editTrack(unsigned int t, Track* track,
unsigned int laps, bool reverse);
void remove(const unsigned int track);
// ------------------------------------------------------------------------
/** @return the (potentially translated) user-visible name of the Grand
* Prix (apply fribidi as needed) */
const irr::core::stringw getName() const { return _LTR(m_name.c_str()); }
irr::core::stringw getName() const { return _LTR(m_name.c_str()); }
// ------------------------------------------------------------------------
/** @return the internal name identifier of the Grand Prix (not translated) */

View File

@@ -18,66 +18,126 @@
#include "race/grand_prix_manager.hpp"
#include <set>
#include "config/user_config.hpp"
#include "grand_prix_data.hpp"
#include "io/file_manager.hpp"
#include "utils/string_utils.hpp"
#include "config/user_config.hpp"
#include <algorithm>
#include <set>
#include <sstream>
GrandPrixManager *grand_prix_manager = NULL;
const char* GrandPrixManager::SUFFIX = ".grandprix";
// ----------------------------------------------------------------------------
void GrandPrixManager::loadFiles()
{
std::set<std::string> dirs;
//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);
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);
}
}
// ----------------------------------------------------------------------------
void GrandPrixManager::loadDir(const std::string& dir)
{
Log::info("GrandPrixManager", "Loading Grand Prix files from %s", dir.c_str());
assert(!dir.empty() && dir[dir.size() - 1] == '/');
// Findout which grand prixs 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++)
{
if (StringUtils::hasSuffix(*i, SUFFIX))
load(dir + *i);
} // for i
} // loadDir
// ----------------------------------------------------------------------------
void GrandPrixManager::load(const std::string& filename)
{
GrandPrixData* gp;
try
{
gp = new GrandPrixData(filename);
m_gp_data.push_back(gp);
Log::debug("GrandPrixManager", "Grand Prix '%s' loaded from %s",
gp->getId().c_str(), filename.c_str());
}
catch (std::logic_error& er)
{
Log::error("GrandPrixManager", "Ignoring GP %s (%s)\n",
filename.c_str(), er.what());
}
} // load
// ----------------------------------------------------------------------------
void GrandPrixManager::reload()
{
for(unsigned int i=0; i<m_gp_data.size(); i++)
delete m_gp_data[i];
m_gp_data.clear();
loadFiles();
}
// ----------------------------------------------------------------------------
std::string GrandPrixManager::generateId()
{
std::stringstream s;
do
{
s.clear();
s << "usr_gp_" << ((rand() % 90000000) + 10000000);
} while (existsId(s.str()));
return s.str();
}
// ----------------------------------------------------------------------------
bool GrandPrixManager::existsId(const std::string& id) const
{
bool exists;
exists = false;
for (unsigned int i = 0; !exists && i < m_gp_data.size(); i++)
exists = (m_gp_data[i]->getId() == id);
return exists;
}
// ----------------------------------------------------------------------------
bool GrandPrixManager::existsName(const irr::core::stringw& name) const
{
bool exists;
exists = false;
for (unsigned int i = 0; !exists && i < m_gp_data.size(); i++)
exists = (m_gp_data[i]->getName() == name);
return exists;
}
// ----------------------------------------------------------------------------
GrandPrixManager::GrandPrixManager()
{
// Findout which grand prixs are available and load them
// Grand Prix in the standart directory
std::set<std::string> result;
std::string gp_dir = file_manager->getAsset(FileManager::GRANDPRIX, "");
file_manager->listFiles(result, gp_dir);
for(std::set<std::string>::iterator i = result.begin();
i != result.end() ; i++)
{
if (StringUtils::hasSuffix(*i, ".grandprix"))
{
try
{
m_gp_data.push_back(new GrandPrixData(*i));
Log::debug("GrandPrixManager", "Grand Prix %s loaded.",
i->c_str());
}
catch (std::logic_error& e)
{
Log::error("GrandPrixManager", "Ignoring GP %s ( %s ) \n",
i->c_str(), e.what());
}
}
}
// Load additional Grand Prix
const std::string dir = UserConfigParams::m_additional_gp_directory;
if(dir != "") {
Log::info("GrandPrixManager", "Loading additional Grand Prix from "
"%s ...", dir.c_str());
file_manager->listFiles(result, dir);
for(std::set<std::string>::iterator i = result.begin();
i != result.end() ; i++)
{
if (StringUtils::hasSuffix(*i, ".grandprix"))
{
try
{
m_gp_data.push_back(new GrandPrixData(dir, *i));
Log::debug("GrandPrixManager", "Grand Prix %s loaded from "
"%s", i->c_str(),
dir.c_str());
}
catch (std::logic_error& e)
{
Log::error("GrandPrixManager", "Ignoring GP %s ( %s ) \n",
i->c_str(), e.what());
}
}
} // end for
} // end if
loadFiles();
} // GrandPrixManager
// ----------------------------------------------------------------------------
GrandPrixManager::~GrandPrixManager()
{
@@ -87,15 +147,21 @@ GrandPrixManager::~GrandPrixManager()
} // for i
} // ~GrandPrixManager
// ----------------------------------------------------------------------------
const GrandPrixData* GrandPrixManager::getGrandPrix(const std::string& s) const
{
for(unsigned int i=0; i<m_gp_data.size(); i++)
if(m_gp_data[i]->getId() == s)
return m_gp_data[i];
return NULL;
return editGrandPrix(s);
} // getGrandPrix
// ----------------------------------------------------------------------------
GrandPrixData* GrandPrixManager::editGrandPrix(const std::string& s) const
{
for(unsigned int i=0; i<m_gp_data.size(); i++)
if(m_gp_data[i]->getId()==s) return m_gp_data[i];
return NULL;
}
// ----------------------------------------------------------------------------
void GrandPrixManager::checkConsistency()
{
@@ -104,9 +170,64 @@ void GrandPrixManager::checkConsistency()
if(!m_gp_data[i]->checkConsistency())
{
// delete this GP, since a track is missing
m_gp_data.erase(m_gp_data.begin()+i);
delete *(m_gp_data.erase(m_gp_data.begin()+i));
i--;
}
}
} // checkConsistency
// ----------------------------------------------------------------------------
GrandPrixData* GrandPrixManager::createNew(const irr::core::stringw& newName)
{
if (existsName(newName))
return NULL;
std::string newID = generateId();
GrandPrixData* gp = new GrandPrixData;
gp->setId(newID);
gp->setName(newName);
gp->setFilename(file_manager->getGPDir() + newID + SUFFIX);
gp->setEditable(true);
gp->writeToFile();
m_gp_data.push_back(gp);
return gp;
}
// ----------------------------------------------------------------------------
GrandPrixData* GrandPrixManager::copy(const std::string& id,
const irr::core::stringw& newName)
{
if (existsName(newName))
return NULL;
std::string newID = generateId();
GrandPrixData* gp = new GrandPrixData(*getGrandPrix(id));
gp->setId(newID);
gp->setName(newName);
gp->setFilename(file_manager->getGPDir() + newID + SUFFIX);
gp->setEditable(true);
gp->writeToFile();
m_gp_data.push_back(gp);
return gp;
}
// ----------------------------------------------------------------------------
void GrandPrixManager::remove(const std::string& id)
{
const GrandPrixData* gp = getGrandPrix(id);
assert(gp != NULL);
if (gp->isEditable())
{
file_manager->removeFile(gp->getFilename());
reload();
}
else
{
Log::warn("GrandPrixManager", "Grand Prix '%s' cannot be removed\n", gp->getId().c_str());
}
}

View File

@@ -30,16 +30,31 @@
class GrandPrixManager
{
private:
static const char* SUFFIX;
void loadFiles();
void loadDir(const std::string& dir);
void load(const std::string &filename);
std::string generateId();
bool existsId(const std::string& id) const;
bool existsName(const irr::core::stringw& name) const;
std::vector<GrandPrixData*> m_gp_data;
public:
GrandPrixManager();
~GrandPrixManager();
void load(const std::string &filename);
const GrandPrixData* getGrandPrix(int i) const { return m_gp_data[i]; }
void reload();
const GrandPrixData* getGrandPrix(const int i) const { return m_gp_data[i]; }
const GrandPrixData* getGrandPrix(const std::string& s) const;
GrandPrixData* editGrandPrix(const std::string& s) const;
unsigned int getNumberOfGrandPrix() const { return m_gp_data.size(); }
void checkConsistency();
GrandPrixData* createNew(const irr::core::stringw& newName);
GrandPrixData* copy(const std::string& id, const irr::core::stringw& newName);
void remove(const std::string& id);
}; // GrandPrixManager
extern GrandPrixManager *grand_prix_manager;

View File

@@ -0,0 +1,137 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "states_screens/dialogs/enter_gp_name_dialog.hpp"
#include "audio/sfx_manager.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/widgets/button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "guiengine/widgets/text_box_widget.hpp"
#include "race/grand_prix_manager.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/translation.hpp"
#include <IGUIEnvironment.h>
using namespace GUIEngine;
using namespace irr::core;
// -----------------------------------------------------------------------------
EnterGPNameDialog::EnterGPNameDialog(INewGPListener* listener,
const float w, const float h)
: ModalDialog(w, h), m_listener(listener), m_self_destroy(false)
{
assert(listener != NULL);
loadFromFile("enter_gp_name_dialog.stkgui");
TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
assert(textCtrl != NULL);
textCtrl->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
}
// -----------------------------------------------------------------------------
EnterGPNameDialog::~EnterGPNameDialog()
{
// FIXME: what is this code for?
TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
textCtrl->getIrrlichtElement()->remove();
textCtrl->clearListeners();
}
// -----------------------------------------------------------------------------
GUIEngine::EventPropagation EnterGPNameDialog::processEvent(const std::string& eventSource)
{
if (eventSource == "cancel")
{
dismiss();
return GUIEngine::EVENT_BLOCK;
}
return GUIEngine::EVENT_LET;
}
// -----------------------------------------------------------------------------
void EnterGPNameDialog::onEnterPressedInternal()
{
//Cancel button pressed
ButtonWidget* cancelButton = getWidget<ButtonWidget>("cancel");
if (GUIEngine::isFocusedForPlayer(cancelButton, PLAYER_ID_GAME_MASTER))
{
std::string fakeEvent = "cancel";
processEvent(fakeEvent);
return;
}
//Otherwise, see if we can accept the new name
TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
assert(textCtrl != NULL);
stringw name = textCtrl->getText().trim();
if (name.size() > 0)
{
// check for duplicate names
for (int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++)
{
const GrandPrixData* gp = grand_prix_manager->getGrandPrix(i);
if (gp->getName() == name)
{
LabelWidget* label = getWidget<LabelWidget>("title");
assert(label != NULL);
label->setText(_("Another grand prix with this name already exists."), false);
sfx_manager->quickSound("anvil");
return;
}
}
// It's unsafe to delete from inside the event handler so we do it
// in onUpdate (which checks for m_self_destroy)
m_self_destroy = true;
}
else
{
LabelWidget* label = getWidget<LabelWidget>("title");
assert(label != NULL);
label->setText(_("Cannot add a grand prix with this name"), false);
sfx_manager->quickSound("anvil");
}
}
// -----------------------------------------------------------------------------
void EnterGPNameDialog::onUpdate(float dt)
{
// It's unsafe to delete from inside the event handler so we do it here
if (m_self_destroy)
{
TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
stringw name = textCtrl->getText().trim();
// irrLicht is too stupid to remove focus from deleted widgets
// so do it by hand
GUIEngine::getGUIEnv()->removeFocus( textCtrl->getIrrlichtElement() );
GUIEngine::getGUIEnv()->removeFocus( m_irrlicht_window );
// we will destroy the dialog before notifying the listener to be safer.
// but in order not to crash we must make a local copy of the listern
// otherwise we will crash
INewGPListener* listener = m_listener;
ModalDialog::dismiss();
if (listener != NULL)
listener->onNewGPWithName(name);
}
}

View File

@@ -0,0 +1,70 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_ENTER_GP_NAME_DIALOG_HPP
#define HEADER_ENTER_GP_NAME_DIALOG_HPP
#include "guiengine/modaldialog.hpp"
#include <irrString.h>
namespace GUIEngine
{
class TextBoxWidget;
class ButtonWidget;
class LabelWidget;
}
/**
* \brief Dialog that allows the player to enter the name for a new grand prix
* \ingroup states_screens
*/
class EnterGPNameDialog : public GUIEngine::ModalDialog
{
public:
class INewGPListener
{
public:
virtual void onNewGPWithName(const irr::core::stringw& newName) = 0;
virtual ~INewGPListener(){}
};
private:
INewGPListener* m_listener;
bool m_self_destroy;
public:
/**
* Creates a modal dialog with given percentage of screen width and height
*/
EnterGPNameDialog(INewGPListener* listener, const float percentWidth,
const float percentHeight);
~EnterGPNameDialog();
void onEnterPressedInternal();
GUIEngine::EventPropagation processEvent(const std::string& eventSource);
virtual void onUpdate(float dt);
};
#endif

View File

@@ -0,0 +1,334 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "states_screens/edit_gp_screen.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/CGUISpriteBank.h"
#include "guiengine/widgets/dynamic_ribbon_widget.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "guiengine/widgets/list_widget.hpp"
#include "race/grand_prix_data.hpp"
#include "states_screens/edit_track_screen.hpp"
#include "states_screens/state_manager.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( EditGPScreen );
// -----------------------------------------------------------------------------
EditGPScreen::EditGPScreen()
: Screen("gpedit.stkgui"), m_gp(NULL), m_list(NULL), m_icon_bank(NULL),
m_selected(-1), m_modified(false)
{
}
// -----------------------------------------------------------------------------
EditGPScreen::~EditGPScreen()
{
delete m_icon_bank;
}
// -----------------------------------------------------------------------------
void EditGPScreen::setSelectedGP(GrandPrixData* gp)
{
assert(gp != NULL);
m_gp = gp;
}
// -----------------------------------------------------------------------------
void EditGPScreen::loadedFromFile()
{
if (m_icon_bank == NULL)
m_icon_bank = new irr::gui::STKModifiedSpriteBank(GUIEngine::getGUIEnv());
m_list = getWidget<ListWidget>("tracks");
assert(m_list != NULL);
m_list->addColumn(_("Track"), 3);
m_list->addColumn(_("Laps"), 1);
m_list->addColumn(_("Reversed"), 1);
}
// -----------------------------------------------------------------------------
void EditGPScreen::eventCallback(GUIEngine::Widget* widget, const std::string& name,
const int playerID)
{
setSelected(m_list->getSelectionID());
if (name == "tracks")
{
m_action = "edit";
edit();
}
else if (name == "menu")
{
RibbonWidget* menu = getWidget<RibbonWidget>("menu");
assert(menu != NULL);
m_action = menu->getSelectionIDString(PLAYER_ID_GAME_MASTER);
if (m_action == "up")
{
if (canMoveUp())
{
m_gp->moveUp(m_selected--);
loadList(m_selected);
setModified(true);
}
}
else if (m_action == "down")
{
if (canMoveDown())
{
m_gp->moveDown(m_selected++);
loadList(m_selected);
setModified(true);
}
}
else if (m_action == "add" || m_action == "edit")
{
if (m_action == "edit")
{
edit();
}
else
{
EditTrackScreen* edit = EditTrackScreen::getInstance();
assert(edit != NULL);
//By default, 3 laps and no reversing
edit->setSelection(NULL, 3, false);
StateManager::get()->pushScreen(edit);
}
}
else if (m_action == "remove")
{
if (m_selected >= 0 && m_selected < m_list->getItemCount())
{
new MessageDialog(
_("Are you sure you want to remove '%s'?",
m_gp->getTrackName(m_selected).c_str()),
MessageDialog::MESSAGE_DIALOG_CONFIRM,
this, false);
}
}
else if (m_action == "save")
{
save();
}
}
else if (name == "back")
{
if (m_modified)
{
m_action = "back";
new MessageDialog(
_("Do you want to save your changes?"),
MessageDialog::MESSAGE_DIALOG_CONFIRM,
this, false);
}
else
{
back();
}
}
}
// -----------------------------------------------------------------------------
void EditGPScreen::init()
{
if (m_action.empty())
{
LabelWidget* header = getWidget<LabelWidget>("title");
assert(header != NULL);
header->setText(m_gp->getName(), true);
IconButtonWidget* button = getWidget<IconButtonWidget>("save");
assert(button != NULL);
button->setDeactivated();
loadList(0);
setModified(false);
}
else
{
EditTrackScreen* edit = EditTrackScreen::getInstance();
assert(edit != NULL);
if (edit->getResult())
{
if (m_action == "add")
{
m_gp->addTrack(edit->getTrack(), edit->getLaps(), edit->getReverse(),
m_selected);
setSelected(m_selected + 1);
}
else if (m_action == "edit")
{
m_gp->editTrack(m_selected, edit->getTrack(), edit->getLaps(),
edit->getReverse());
}
setModified(true);
}
loadList(m_selected);
m_action.clear();
}
}
// -----------------------------------------------------------------------------
void EditGPScreen::onConfirm()
{
ModalDialog::dismiss();
if (m_action == "remove")
{
m_gp->remove(m_selected);
setSelected(m_selected >= m_gp->getNumberOfTracks() ?
m_gp->getNumberOfTracks() - 1 : m_selected);
loadList(m_selected);
setModified(true);
}
else if (m_action == "back")
{
save();
back();
}
}
// -----------------------------------------------------------------------------
void EditGPScreen::onCancel()
{
ModalDialog::dismiss();
if (m_action == "back")
back();
}
// -----------------------------------------------------------------------------
void EditGPScreen::loadList(const int selected)
{
m_list->clear();
m_icons.clear();
m_icon_bank->clear();
m_icon_bank->scaleToHeight (64);
m_list->setIcons(m_icon_bank, 64);
for (unsigned int i = 0; i < m_gp->getNumberOfTracks(); i++)
{
std::vector<GUIEngine::ListWidget::ListCell> row;
Track* t = track_manager->getTrack(m_gp->getTrackId(i));
assert(t != NULL);
video::ITexture* screenShot = irr_driver->getTexture(t->getScreenshotFile());
assert(screenShot != NULL);
m_icons.push_back(m_icon_bank->addTextureAsSprite(screenShot));
row.push_back(GUIEngine::ListWidget::ListCell(
_LTR(m_gp->getTrackName(i).c_str()), m_icons[i], 3, false));
row.push_back(GUIEngine::ListWidget::ListCell(
StringUtils::toWString<unsigned int>(m_gp->getLaps(i)), -1, 1, true));
row.push_back(GUIEngine::ListWidget::ListCell(
m_gp->getReverse(i) ? _("Yes") : _("No"), -1, 1, true));
m_list->addItem(m_gp->getId(), row);
}
m_list->setIcons(m_icon_bank);
if (selected < m_list->getItemCount())
{
m_list->setSelectionID(selected);
setSelected(selected);
}
}
// -----------------------------------------------------------------------------
void EditGPScreen::setModified(const bool modified)
{
m_modified = modified;
IconButtonWidget* save_button = getWidget<IconButtonWidget>("save");
assert(save_button != NULL);
if (modified)
save_button->setActivated();
else
save_button->setDeactivated();
}
// -----------------------------------------------------------------------------
void EditGPScreen::setSelected(const int selected)
{
IconButtonWidget* button_up = getWidget<IconButtonWidget>("up");
assert(button_up != NULL);
IconButtonWidget* button_down = getWidget<IconButtonWidget>("down");
assert(button_down != NULL);
m_selected = selected;
}
// -----------------------------------------------------------------------------
void EditGPScreen::edit()
{
EditTrackScreen* edit_screen = EditTrackScreen::getInstance();
assert(edit_screen != NULL);
if (m_selected >= 0 && m_selected < m_list->getItemCount())
{
edit_screen->setSelection(track_manager->getTrack(
m_gp->getTrackId(m_selected)),
m_gp->getLaps(m_selected),
m_gp->getReverse(m_selected));
StateManager::get()->pushScreen(edit_screen);
}
}
// -----------------------------------------------------------------------------
bool EditGPScreen::save()
{
if (m_gp->writeToFile())
{
setModified(false);
return true;
}
else
{
new MessageDialog(
_("An error occurred while trying to save your grand prix"),
MessageDialog::MESSAGE_DIALOG_OK, NULL, false);
return false;
}
}
// -----------------------------------------------------------------------------
void EditGPScreen::back ()
{
m_action.clear();
m_modified = false;
StateManager::get()->popMenu();
}
// -----------------------------------------------------------------------------
bool EditGPScreen::canMoveUp() const
{
return (m_selected > 0 && m_selected < m_list->getItemCount());
}
// -----------------------------------------------------------------------------
bool EditGPScreen::canMoveDown() const
{
return (m_selected >= 0 && m_selected < (m_list->getItemCount() - 1));
}

View File

@@ -0,0 +1,85 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_EDIT_GP_SCREEN_HPP
#define HEADER_EDIT_GP_SCREEN_HPP
#include "guiengine/screen.hpp"
#include "guiengine/widgets/list_widget.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
#include <vector>
namespace GUIEngine { class Widget; }
namespace irr { namespace gui { class STKModifiedSpriteBank; } }
class GrandPrixData;
/**
* \brief screen where the user can edit a grand prix
* \ingroup states_screens
*/
class EditGPScreen :
public GUIEngine::Screen,
public GUIEngine::ScreenSingleton<EditGPScreen>,
public MessageDialog::IConfirmDialogListener
{
friend class GUIEngine::ScreenSingleton<EditGPScreen>;
EditGPScreen();
void onConfirm();
void onCancel();
void loadList(const int selected);
void setModified(const bool modified);
void setSelected(const int selected);
void edit();
bool save();
void back();
bool canMoveUp() const;
bool canMoveDown() const;
GrandPrixData* m_gp;
GUIEngine::ListWidget* m_list;
irr::gui::STKModifiedSpriteBank* m_icon_bank;
std::vector<int> m_icons;
int m_selected;
bool m_modified;
std::string m_action;
public:
~EditGPScreen();
void setSelectedGP(GrandPrixData* gp);
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void loadedFromFile() OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name,
const int playerID) OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void init() OVERRIDE;
};
#endif

View File

@@ -0,0 +1,240 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "states_screens/edit_track_screen.hpp"
#include "guiengine/widgets/button_widget.hpp"
#include "guiengine/widgets/check_box_widget.hpp"
#include "guiengine/widgets/dynamic_ribbon_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "guiengine/widgets/ribbon_widget.hpp"
#include "guiengine/widgets/spinner_widget.hpp"
#include "states_screens/state_manager.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
using namespace GUIEngine;
using namespace irr::core;
const char* EditTrackScreen::ALL_TRACKS_GROUP_ID = "all";
DEFINE_SCREEN_SINGLETON( EditTrackScreen );
// -----------------------------------------------------------------------------
EditTrackScreen::EditTrackScreen()
: Screen("edit_track.stkgui"), m_track_group(ALL_TRACKS_GROUP_ID),
m_track(NULL), m_laps(0), m_reverse(false), m_result(false)
{
}
// -----------------------------------------------------------------------------
EditTrackScreen::~EditTrackScreen()
{
}
// -----------------------------------------------------------------------------
void EditTrackScreen::setSelection(Track* track, unsigned int laps, bool reverse)
{
assert(laps > 0);
m_track = track;
m_laps = laps;
m_reverse = reverse;
}
// -----------------------------------------------------------------------------
Track* EditTrackScreen::getTrack() const
{
return m_track;
}
// -----------------------------------------------------------------------------
unsigned int EditTrackScreen::getLaps() const
{
return m_laps;
}
// -----------------------------------------------------------------------------
bool EditTrackScreen::getReverse() const
{
return m_reverse;
}
// -----------------------------------------------------------------------------
bool EditTrackScreen::getResult() const
{
return m_result;
}
// -----------------------------------------------------------------------------
void EditTrackScreen::loadedFromFile()
{
}
// -----------------------------------------------------------------------------
void EditTrackScreen::eventCallback(GUIEngine::Widget* widget, const std::string& name,
const int playerID)
{
if (name == "ok")
{
m_result = true;
StateManager::get()->popMenu();
}
else if (name == "cancel")
{
m_result = false;
StateManager::get()->popMenu();
}
else if (name == "tracks")
{
DynamicRibbonWidget* tracks = getWidget<DynamicRibbonWidget>("tracks");
assert(tracks != NULL);
selectTrack(tracks->getSelectionIDString(PLAYER_ID_GAME_MASTER));
}
else if (name == "trackgroups")
{
RibbonWidget* tabs = getWidget<RibbonWidget>("trackgroups");
assert(tabs != NULL);
m_track_group = tabs->getSelectionIDString(PLAYER_ID_GAME_MASTER);
loadTrackList();
}
else if (name == "laps")
{
SpinnerWidget* laps = getWidget<SpinnerWidget>("laps");
assert(laps != NULL);
m_laps = laps->getValue();
}
else if (name == "reverse")
{
CheckBoxWidget* reverse = getWidget<CheckBoxWidget>("reverse");
assert(reverse != NULL);
m_reverse = reverse->getState();
}
}
// -----------------------------------------------------------------------------
void EditTrackScreen::beforeAddingWidget()
{
RibbonWidget* tabs = getWidget<RibbonWidget>("trackgroups");
assert (tabs != NULL);
tabs->clearAllChildren();
const std::vector<std::string>& groups = track_manager->getAllTrackGroups();
if (groups.size() > 1)
{
tabs->addTextChild(_("All"), ALL_TRACKS_GROUP_ID);
for (unsigned int i = 0; i < groups.size(); i++)
tabs->addTextChild(_(groups[i].c_str()), groups[i]);
}
}
// -----------------------------------------------------------------------------
void EditTrackScreen::init()
{
RibbonWidget* tabs = getWidget<RibbonWidget>("trackgroups");
assert (tabs != NULL);
SpinnerWidget* laps = getWidget<SpinnerWidget>("laps");
assert(laps != NULL);
CheckBoxWidget* reverse = getWidget<CheckBoxWidget>("reverse");
assert(reverse != NULL);
if (m_track_group.empty())
tabs->select (ALL_TRACKS_GROUP_ID, PLAYER_ID_GAME_MASTER);
else
tabs->select (m_track_group, PLAYER_ID_GAME_MASTER);
laps->setValue(m_laps);
reverse->setState(m_reverse);
loadTrackList();
if (m_track == NULL)
selectTrack("");
else
selectTrack(m_track->getIdent());
}
// -----------------------------------------------------------------------------
void EditTrackScreen::loadTrackList()
{
bool belongsToGroup;
DynamicRibbonWidget* tracks_widget = getWidget<DynamicRibbonWidget>("tracks");
assert(tracks_widget != NULL);
tracks_widget->clearItems();
for (unsigned int i = 0; i < track_manager->getNumberOfTracks(); i++)
{
Track* t = track_manager->getTrack(i);
const std::vector<std::string>& groups = t->getGroups();
belongsToGroup = (m_track_group.empty() || m_track_group == ALL_TRACKS_GROUP_ID ||
std::find(groups.begin(), groups.end(), m_track_group) != groups.end());
if (!t->isArena() && !t->isSoccer() && !t->isInternal() && belongsToGroup)
{
tracks_widget->addItem(translations->fribidize(t->getName()), t->getIdent(),
t->getScreenshotFile(), 0, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE );
}
}
tracks_widget->updateItemDisplay();
}
// -----------------------------------------------------------------------------
void EditTrackScreen::selectTrack(const std::string& id)
{
DynamicRibbonWidget* tracks = getWidget<DynamicRibbonWidget>("tracks");
assert(tracks != NULL);
LabelWidget* selected_track = getWidget<LabelWidget>("selected_track");
assert(selected_track != NULL);
SpinnerWidget* laps = getWidget<SpinnerWidget>("laps");
assert(laps != NULL);
LabelWidget* label_reverse = getWidget<LabelWidget>("reverse_label");
assert(label_reverse != NULL);
CheckBoxWidget* reverse = getWidget<CheckBoxWidget>("reverse");
assert(reverse != NULL);
ButtonWidget* ok_button = getWidget<ButtonWidget>("ok");
assert(ok_button != NULL);
m_track = track_manager->getTrack(id);
if (m_track != NULL)
{
tracks->setSelection(m_track->getIdent(), PLAYER_ID_GAME_MASTER, true);
selected_track->setText(m_track->getName(), true);
laps->setValue(m_laps);
reverse->setVisible(m_track->reverseAvailable());
label_reverse->setVisible(m_track->reverseAvailable());
ok_button->setActivated();
}
else
{
tracks->setSelection("", PLAYER_ID_GAME_MASTER, true);
selected_track->setText(_("Select a track"), true);
laps->setValue(3);
reverse->setVisible(true);
reverse->setState(false);
ok_button->setDeactivated();
}
}

View File

@@ -0,0 +1,78 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_EDIT_TRACK_SCREEN_HPP
#define HEADER_EDIT_TRACK_SCREEN_HPP
#include "guiengine/screen.hpp"
namespace GUIEngine { class Widget; }
namespace irr { namespace gui { class STKModifiedSpriteBank; } }
class Track;
/**
* \brief screen where the user can edit the details of a track inside a grand prix
* \ingroup states_screens
*/
class EditTrackScreen :
public GUIEngine::Screen,
public GUIEngine::ScreenSingleton<EditTrackScreen>
{
friend class GUIEngine::ScreenSingleton<EditTrackScreen>;
static const char* ALL_TRACKS_GROUP_ID;
EditTrackScreen();
void loadTrackList();
void selectTrack(const std::string& id);
std::string m_track_group;
Track* m_track;
unsigned int m_laps;
bool m_reverse;
bool m_result;
public:
~EditTrackScreen();
void setSelection(Track* track, unsigned int laps, bool reverse);
Track* getTrack() const;
unsigned int getLaps() const;
bool getReverse() const;
bool getResult() const;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void beforeAddingWidget() OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void loadedFromFile() OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name,
const int playerID) OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void init() OVERRIDE;
};
#endif

View File

@@ -0,0 +1,286 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "states_screens/grand_prix_editor_screen.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#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"
#include "states_screens/dialogs/enter_gp_name_dialog.hpp"
#include "states_screens/dialogs/gp_info_dialog.hpp"
#include "states_screens/dialogs/track_info_dialog.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/translation.hpp"
using namespace GUIEngine;
using namespace irr::core;
DEFINE_SCREEN_SINGLETON( GrandPrixEditorScreen );
// -----------------------------------------------------------------------------
GrandPrixEditorScreen::GrandPrixEditorScreen()
: Screen("gpeditor.stkgui"), m_selection(NULL)
{
}
// -----------------------------------------------------------------------------
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")
{
RibbonWidget* menu = getWidget<RibbonWidget>("menu");
assert(menu != NULL);
m_action = menu->getSelectionIDString(PLAYER_ID_GAME_MASTER);
if (m_action == "new" || m_action == "copy")
{
new EnterGPNameDialog(this, 0.5f, 0.4f);
}
else if (m_action == "edit")
{
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);
}
}
else if (m_action == "remove")
{
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);
}
}
else if (m_action == "rename")
{
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);
}
}
}
else if (name == "back")
{
StateManager::get()->escapePressed();
}
}
// -----------------------------------------------------------------------------
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();
}
}
// -----------------------------------------------------------------------------
void GrandPrixEditorScreen::setSelection (const GrandPrixData* gpdata)
{
LabelWidget* gpname_widget = getWidget<LabelWidget>("gpname");
assert(gpname_widget != NULL);
DynamicRibbonWidget* gplist_widget = getWidget<DynamicRibbonWidget>("gplist");
assert (gplist_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());
}
// -----------------------------------------------------------------------------
void GrandPrixEditorScreen::loadTrackList (const std::string& gpname)
{
if (gpname.empty())
return;
DynamicRibbonWidget* tracks_widget = getWidget<DynamicRibbonWidget>("tracks");
assert(tracks_widget != NULL);
const GrandPrixData* gp = grand_prix_manager->getGrandPrix(gpname);
const std::vector<std::string>& tracks = gp->getTrackNames();
tracks_widget->clearItems();
tracks_widget->setItemCountHint(tracks.size());
for (unsigned int t = 0; t < tracks.size(); t++)
{
Track* curr = track_manager->getTrack(tracks[t]);
if (curr == NULL)
{
Log::warn("GrandPrixEditor",
"Grand Prix '%s' refers to track '%s', which does not exist\n",
gp->getId().c_str(), tracks[t].c_str());
}
else
{
tracks_widget->addItem(
StringUtils::toWString(t + 1) + ". " + translations->fribidize(curr->getName()),
curr->getIdent(), curr->getScreenshotFile(), 0,
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE );
}
}
tracks_widget->updateItemDisplay();
}
// -----------------------------------------------------------------------------
void GrandPrixEditorScreen::loadGPList()
{
DynamicRibbonWidget* gplist_widget = getWidget<DynamicRibbonWidget>("gplist");
assert(gplist_widget != NULL);
// Reset GP list everytime (accounts for locking changes, etc.)
gplist_widget->clearItems();
// Build GP list
for (unsigned int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++)
{
const GrandPrixData* gp = grand_prix_manager->getGrandPrix(i);
const std::vector<std::string>& tracks = gp->getTrackNames();
std::vector<std::string> sshot_files;
for (unsigned int t=0; t<tracks.size(); t++)
{
Track* track = track_manager->getTrack(tracks[t]);
if (track == NULL)
{
Log::warn("GrandPrixEditor",
"Grand Prix '%s' refers to track '%s', which does not exist\n",
gp->getId().c_str(), tracks[t].c_str());
}
else
{
sshot_files.push_back(track->getScreenshotFile());
}
}
if (sshot_files.size() == 0)
{
Log::warn("GrandPrixEditor",
"Grand Prix '%s' does not contain any valid track\n",
gp->getId().c_str());
sshot_files.push_back("gui/main_help.png");
}
gplist_widget->addAnimatedItem(translations->fribidize(gp->getName()), gp->getId(),
sshot_files, 2.0f, 0, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE );
}
gplist_widget->updateItemDisplay();
}
// -----------------------------------------------------------------------------
void GrandPrixEditorScreen::showEditScreen(GrandPrixData* gp)
{
assert(gp != NULL);
EditGPScreen* edit = EditGPScreen::getInstance();
edit->setSelectedGP(gp);
StateManager::get()->pushScreen(edit);
}
// -----------------------------------------------------------------------------
void GrandPrixEditorScreen::onNewGPWithName(const stringw& newName)
{
if (m_action == "copy")
{
setSelection(grand_prix_manager->copy(m_selection->getId(), newName));
}
else if (m_action == "rename")
{
m_selection->setName(newName);
m_selection->writeToFile();
}
else if (m_action == "new")
{
setSelection(grand_prix_manager->createNew(newName));
}
loadGPList();
if (m_action != "rename")
showEditScreen(m_selection);
}
// -----------------------------------------------------------------------------
void GrandPrixEditorScreen::onConfirm()
{
if (m_action == "remove")
{
grand_prix_manager->remove(m_selection->getId());
loadGPList();
if (grand_prix_manager->getNumberOfGrandPrix() > 0)
setSelection (grand_prix_manager->getGrandPrix(0));
}
ModalDialog::dismiss();
}

View File

@@ -0,0 +1,68 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014 Marc Coll
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_GRAND_PRIX_EDITOR_SCREEN_HPP
#define HEADER_GRAND_PRIX_EDITOR_SCREEN_HPP
#include "dialogs/enter_gp_name_dialog.hpp"
#include "guiengine/screen.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
namespace GUIEngine { class Widget; }
class GrandPrixData;
/**
* \brief screen where the user can edit his own grand prix
* \ingroup states_screens
*/
class GrandPrixEditorScreen :
public GUIEngine::Screen,
public GUIEngine::ScreenSingleton<GrandPrixEditorScreen>,
public EnterGPNameDialog::INewGPListener,
public MessageDialog::IConfirmDialogListener
{
friend class GUIEngine::ScreenSingleton<GrandPrixEditorScreen>;
GrandPrixEditorScreen();
void setSelection(const GrandPrixData* gpdata);
void loadGPList();
void loadTrackList(const std::string& gpname);
void showEditScreen(GrandPrixData* gp);
void onNewGPWithName(const irr::core::stringw& newName);
void onConfirm();
GrandPrixData* m_selection;
std::string m_action;
public:
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void loadedFromFile() OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name,
const int playerID) OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void init() OVERRIDE;
};
#endif

View File

@@ -39,6 +39,7 @@
#include "online/request_manager.hpp"
#include "states_screens/addons_screen.hpp"
#include "states_screens/credits.hpp"
#include "states_screens/grand_prix_editor_screen.hpp"
#include "states_screens/help_screen_1.hpp"
#include "states_screens/login_screen.hpp"
#include "states_screens/offline_kart_selection.hpp"
@@ -158,7 +159,7 @@ void MainMenuScreen::onUpdate(float delta)
}
else // now must be either logging in or logging out
m_online->setDeactivated();
m_online->setLabel(CurrentUser::get()->getID() ? _("Online")
: _("Login" ) );
IconButtonWidget* addons_icon = getWidget<IconButtonWidget>("addons");
@@ -413,7 +414,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
{
// Don't go to addons if there is no internet, unless some addons are
// already installed (so that you can delete addons without being online).
if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED &&
if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED &&
!addons_manager->anyAddonsInstalled())
{
new MessageDialog(_("You can not download addons without internet access. "
@@ -424,6 +425,10 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
}
StateManager::get()->pushScreen(AddonsScreen::getInstance());
}
else if (selection == "gpEditor")
{
StateManager::get()->pushScreen(GrandPrixEditorScreen::getInstance());
}
} // eventCallback
// ----------------------------------------------------------------------------