Apply Marc Coll's Grand Prix editor, thanks! There will be refinements to come for sure, but this is a very nice start
BIN
data/gui/down.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
54
data/gui/edit_track.stkgui
Normal 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>
|
||||
21
data/gui/enter_gp_name_dialog.stkgui
Normal 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
|
After Width: | Height: | Size: 6.1 KiB |
BIN
data/gui/gp_copy.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
data/gui/gp_edit.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
data/gui/gp_edit_track.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
data/gui/gp_new.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
data/gui/gp_remove.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
data/gui/gp_remove_track.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
data/gui/gp_rename.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
data/gui/gp_save.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
35
data/gui/gpedit.stkgui
Normal 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
@@ -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>
|
||||
@@ -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
|
After Width: | Height: | Size: 4.0 KiB |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" );
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
137
src/states_screens/dialogs/enter_gp_name_dialog.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
70
src/states_screens/dialogs/enter_gp_name_dialog.hpp
Normal 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
|
||||
334
src/states_screens/edit_gp_screen.cpp
Normal 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));
|
||||
}
|
||||
85
src/states_screens/edit_gp_screen.hpp
Normal 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
|
||||
240
src/states_screens/edit_track_screen.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
78
src/states_screens/edit_track_screen.hpp
Normal 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
|
||||
286
src/states_screens/grand_prix_editor_screen.cpp
Normal 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();
|
||||
}
|
||||
68
src/states_screens/grand_prix_editor_screen.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||