First version of demo mode. Use --demo-mode T to start

a demo after T seconds of idle time, --demo-tracks a,b,c
to specify a list of tracks to use for demo mode, --demo-karts
to specify the number of karts.
This is WIP, esp. it crashes if ESC is pressed during a race
(other keys will successfully return to main menu).


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11189 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-05-07 00:10:33 +00:00
parent 48bac59013
commit c15528f903
13 changed files with 348 additions and 14 deletions

View File

@ -692,6 +692,10 @@
<Filter
Name="modes"
>
<File
RelativePath="..\..\modes\demo_world.cpp"
>
</File>
<File
RelativePath="..\..\modes\follow_the_leader.cpp"
>
@ -1850,6 +1854,10 @@
<Filter
Name="modes"
>
<File
RelativePath="..\..\modes\demo_world.hpp"
>
</File>
<File
RelativePath="..\..\modes\follow_the_leader.hpp"
>

View File

@ -33,11 +33,13 @@
#include "input/input.hpp"
#include "karts/controller/controller.hpp"
#include "karts/abstract_kart.hpp"
#include "modes/demo_world.hpp"
#include "modes/profile_world.hpp"
#include "modes/world.hpp"
#include "race/history.hpp"
#include "replay/replay_recorder.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/main_menu_screen.hpp"
#include "states_screens/options_screen_input2.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/string_utils.hpp"
@ -441,6 +443,14 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID,
return;
}
// Abort demo mode if a key is pressed during the race in demo mode
if(dynamic_cast<DemoWorld*>(World::getWorld()))
{
race_manager->exitRace();
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
return;
}
StateManager::ActivePlayer* player = NULL;
PlayerAction action;
bool action_found = m_device_manager->translateInput(type, deviceID,

View File

@ -28,7 +28,6 @@ KartWithStats::KartWithStats(const std::string& ident,
: Kart(ident, world_kart_id, position,
init_transform)
{
reset();
} // KartWithStats
// ----------------------------------------------------------------------------
@ -47,6 +46,7 @@ void KartWithStats::reset()
m_small_nitro_count = 0;
m_large_nitro_count = 0;
m_bubblegum_count = 0;
Kart::reset();
} // reset
// ----------------------------------------------------------------------------

View File

@ -166,6 +166,7 @@
#include "items/projectile_manager.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/demo_world.hpp"
#include "modes/profile_world.hpp"
#include "network/network_manager.hpp"
#include "race/grand_prix_manager.hpp"
@ -392,6 +393,12 @@ void cmdLineHelp (char* invocation)
" --profile-time=n Enable automatic driven profile mode for n "
"seconds.\n"
" --no-graphics Do not display the actual race.\n"
" --demo-mode t Enables demo mode after t seconds idle time in "
"main menu.\n"
" --demo-tracks t1,t2 List of tracks to be used in demo mode. No\n"
" spaces are allowed in the track names.\n"
" --demo-laps n Number of laps in a demo.\n"
" --demo-karts n Number of karts to use in a demo.\n"
" --ghost Replay ghost data together with one player kart.\n"
// " --history Replay history file 'history.dat'.\n"
// " --history=n Replay history file 'history.dat' using:\n"
@ -937,6 +944,35 @@ int handleCmdLine(int argc, char **argv)
UserConfigParams::m_no_start_screen = true;
}
else if( !strcmp(argv[i], "--demo-mode") && i+1<argc)
{
float t;
StringUtils::fromString(argv[i+1], t);
DemoWorld::enableDemoMode(t);
i++;
}
else if( !strcmp(argv[i], "--demo-laps") && i+1<argc)
{
// Note that we use a separate setting for demo mode to avoid the
// problem that someone plays a game, and in further demos then
// the wrong (i.e. last selected) number of laps would be used
DemoWorld::setNumLaps(atoi(argv[i+1]));
i++;
}
else if( !strcmp(argv[i], "--demo-karts") && i+1<argc)
{
// Note that we use a separate setting for demo mode to avoid the
// problem that someone plays a game, and in further demos then
// the wrong (i.e. last selected) number of karts would be used
DemoWorld::setNumKarts(atoi(argv[i+1]));
i++;
}
else if( !strcmp(argv[i], "--demo-tracks") && i+1<argc)
{
DemoWorld::setTracks(StringUtils::split(std::string(argv[i+1]),
','));
i++;
}
else if( !strcmp(argv[i], "--item") && i+1<argc )
{
item_manager->setUserFilename(argv[i+1]);

144
src/modes/demo_world.cpp Normal file
View File

@ -0,0 +1,144 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2012 Joerg Henrichs
//
// 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 "modes/demo_world.hpp"
#include "input/device_manager.hpp"
#include "input/input_manager.hpp"
#include "network/network_manager.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
std::vector<std::string> DemoWorld::m_demo_tracks;
int DemoWorld::m_num_karts = 2;
float DemoWorld::m_max_idle_time = 99999.0f;
float DemoWorld::m_current_idle_time = 0;
bool DemoWorld::m_do_demo = false;
//-----------------------------------------------------------------------------
/** The constructor sets the number of (local) players to 0, since only AI
* karts are used.
*/
DemoWorld::DemoWorld()
{
m_abort = false;
ProfileWorld::setProfileModeLaps(m_num_laps);
race_manager->setReverseTrack(false);
race_manager->setMinorMode (RaceManager::MINOR_MODE_NORMAL_RACE);
race_manager->setDifficulty(RaceManager::RD_HARD);
race_manager->setNumKarts(m_num_karts);
race_manager->setNumLocalPlayers(1);
race_manager->setLocalKartInfo(0, UserConfigParams::m_default_kart);
} // DemoWorld
//-----------------------------------------------------------------------------
/** Destructor. Resets the flag that the next race is a demo mode.
*/
DemoWorld::~DemoWorld()
{
m_current_idle_time = 0;
m_do_demo = false;
} // ~DemoWorld
//-----------------------------------------------------------------------------
/** Sets the list of tracks that is to be used.
* \param tracks List of track identifiers.
*/
void DemoWorld::setTracks(const std::vector<std::string> &tracks)
{
m_demo_tracks = tracks;
} // setTracks
//-----------------------------------------------------------------------------
/** The race is over if either the requested number of laps have been done
* or the requested time is over.
*/
bool DemoWorld::isRaceOver()
{
if(m_abort) return true;
// Now it must be laps based profiling:
return race_manager->getFinishedKarts()==getNumKarts();
} // isRaceOver
//-----------------------------------------------------------------------------
/** This function is called when the race is finished, but end-of-race
* animations have still to be played. In the case of profiling,
* we can just abort here without waiting for the animations.
*/
void DemoWorld::enterRaceOverState()
{
// Do not call profile enterRaceOverState, since it will abort
StandardRace::enterRaceOverState();
} // enterRaceOverState
//-----------------------------------------------------------------------------
/** Updates the current idle time by dt. If the accumulated idle time is
* large enough, a demo race is started.
* \param dt Time step size, which is added to the idle time.
* \return true if a demo is started.
*/
bool DemoWorld::updateIdleTimeAndStartDemo(float dt)
{
m_current_idle_time += dt;
if(m_current_idle_time <= m_max_idle_time)
return false;
if(m_demo_tracks.size()==0)
m_demo_tracks = track_manager->getAllTrackIdentifiers();
Track *track = track_manager->getTrack(m_demo_tracks[0]);
// Remove arena tracks (outside the if statement above in case that
// a user requests a arena track ;) )
while((!track || track->isArena()) && m_demo_tracks.size()>1)
{
if(!track)
printf("Invalid demo track identifier '%s'.\n",
m_demo_tracks[0].c_str());
m_demo_tracks.erase(m_demo_tracks.begin());
track = track_manager->getTrack(m_demo_tracks[0]);
}
if(m_demo_tracks.size()==0)
{
printf("No valid tracks found, no demo started.\n");
return false;
}
StateManager::get()->enterGameState();
race_manager->setNumLocalPlayers(1);
InputDevice *device;
// Use keyboard 0 by default in --no-start-screen
device = input_manager->getDeviceList()->getKeyboard(0);
StateManager::get()->createActivePlayer(
UserConfigParams::m_all_players.get(0), device );
// ASSIGN should make sure that only input from assigned devices
// is read.
input_manager->getDeviceList()->setAssignMode(ASSIGN);
m_do_demo = true;
race_manager->setNumKarts(m_num_karts);
race_manager->setLocalKartInfo(0, "tux");
network_manager->setupPlayerKartInfo();
race_manager->startSingleRace(m_demo_tracks[0], m_num_laps, false);
m_demo_tracks.push_back(m_demo_tracks[0]);
m_demo_tracks.erase(m_demo_tracks.begin());
return true;
} // updateIdleTime

87
src/modes/demo_world.hpp Normal file
View File

@ -0,0 +1,87 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2012 Joerg Henrichs
//
// 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_DEMO_WORLD_HPP
#define HEADER_DEMO_WORLD_HPP
#include "modes/profile_world.hpp"
class Kart;
/** \brief This class is used to show a demo of STK (e.g. after being
* idle for a certain amount of time, or on certain command line options.
* This allows STK to be used as a demo in a shop.
* \ingroup modes
*/
class DemoWorld : public ProfileWorld
{
private:
/** True if demo mode should be aborted, e.g. because a key was
* pressed. */
bool m_abort;
/** The list of tracks to use for demo mode. They will be picked randomly,
* but each track will be picked once before one track is repeated. */
static std::vector<std::string> m_demo_tracks;
/** Number of karts to use in demo mode. */
static int m_num_karts;
/** Idle time after which demo mode should be started. */
static float m_max_idle_time;
/** Used by the menu system to measure idle time. */
static float m_current_idle_time;
/** True if the next race is to be a demo race. */
static bool m_do_demo;
public:
DemoWorld();
virtual ~DemoWorld();
/** Returns identifier for this world. */
virtual std::string getInternalCode() const {return "DEMO"; }
virtual void update(float dt) {ProfileWorld::update(dt);};
virtual bool isRaceOver();
virtual void enterRaceOverState();
static bool updateIdleTimeAndStartDemo(float dt) ;
// ------------------------------------------------------------------------
/** Sets the number of laps to use in demo mode. m_num_laps is from
* ProfileWorld. */
static void setNumLaps(unsigned int num_laps) { m_num_laps = num_laps; }
// ------------------------------------------------------------------------
/** Sets the number of karts to use in demo mode. */
static void setNumKarts(unsigned int num_karts) { m_num_karts = num_karts;}
// ------------------------------------------------------------------------
static void setTracks(const std::vector<std::string> &tracks);
// ------------------------------------------------------------------------
/** Enables demo mode after the specified amount of time (default 1
* second).
* \param time The idle time after which demo mode should be started. */
static void enableDemoMode(float time=1.0f) { m_max_idle_time = time; }
// ------------------------------------------------------------------------
/** Returns true if the menu detected enough idle time to run a demo. */
static bool isDemoMode() { return m_do_demo; }
// ------------------------------------------------------------------------
/** Resets the idle time to 0. */
static void resetIdleTime() { m_current_idle_time = 0; }
// ------------------------------------------------------------------------
/** Signals that the demo should be aborted. */
void abortDemo() {m_abort = true; }
}; // DemoWorld
#endif

View File

@ -40,9 +40,9 @@ ProfileWorld::ProfileWorld()
race_manager->setNumLocalPlayers(0);
// Set number of laps so that the end of the race can be detected by
// quering the number of finished karts from the race manager (in laps
// based profiling) - otherwise just a high number.
race_manager->setNumLaps(m_profile_mode==PROFILE_LAPS ? m_num_laps
: 99999);
// based profiling) - in case of time based profiling, the number of
// laps is set to 99999.
race_manager->setNumLaps(m_num_laps);
m_phase = RACE_PHASE;
m_frame_count = 0;
m_start_time = irr_driver->getRealTime();
@ -55,12 +55,15 @@ ProfileWorld::ProfileWorld()
} // ProfileWorld
//-----------------------------------------------------------------------------
/** Enables profiling for a certain amount of time.
/** Enables profiling for a certain amount of time. It also sets the
* number of laps to a high number (so that the lap count will not finish
* a race before the time is over).
* \param time Time to profile a race for.
*/
void ProfileWorld::setProfileModeTime(float time)
{
m_profile_mode = PROFILE_TIME;
m_num_laps = 99999;
m_time = time;
} // setProfileModeTime
@ -75,6 +78,17 @@ void ProfileWorld::setProfileModeLaps(int laps)
m_num_laps = laps;
} // setProfileModeLaps
//-----------------------------------------------------------------------------
/** Activates Demo mode. In this mode all karts are controlled by an AI, but
* at the end no stats are printed, and the game isn't exited.
* \param laps The number of laps.
*/
void ProfileWorld::setProfileModeDemo(int laps)
{
m_profile_mode = PROFILE_DEMO;
m_num_laps = laps;
} // setProfileModeDemo
//-----------------------------------------------------------------------------
/** Creates a kart, having a certain position, starting location, and local
* and global player id (if applicable).
@ -118,8 +132,15 @@ bool ProfileWorld::isRaceOver()
if(m_profile_mode==PROFILE_TIME)
return getTime()>m_time;
// Now it must be laps based profiling:
return race_manager->getFinishedKarts()==getNumKarts();
if(m_profile_mode == PROFILE_LAPS ||
m_profile_mode == PROFILE_DEMO)
{
// Now it must be laps based profiling:
return race_manager->getFinishedKarts()==getNumKarts();
}
// Unknown profile mode
assert(false);
return false; // keep compiler happy
} // isRaceOver
//-----------------------------------------------------------------------------
@ -128,6 +149,8 @@ bool ProfileWorld::isRaceOver()
void ProfileWorld::update(float dt)
{
StandardRace::update(dt);
if(m_profile_mode==PROFILE_DEMO) return;
m_frame_count++;
video::IVideoDriver *driver = irr_driver->getVideoDriver();
io::IAttributes *attr = irr_driver->getSceneManager()->getParameters();
@ -172,6 +195,9 @@ void ProfileWorld::enterRaceOverState()
continue;
m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i]));
}
// Do nothing more in demo mode - esp. don't abort ;)
if(m_profile_mode == PROFILE_DEMO) return;
// Print framerate statistics
float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f;

View File

@ -31,7 +31,8 @@ class ProfileWorld : public StandardRace
{
private:
/** Profiling modes. */
enum ProfileType {PROFILE_NONE, PROFILE_TIME, PROFILE_LAPS};
enum ProfileType {PROFILE_NONE, PROFILE_TIME, PROFILE_LAPS,
PROFILE_DEMO};
/** If profiling is done, and if so, which mode. */
static ProfileType m_profile_mode;
@ -40,9 +41,6 @@ private:
* of AI changes etc. */
static bool m_no_graphics;
/** In laps based profiling: number of laps to run. */
static int m_num_laps;
/** In time based profiling only: time to run. */
static float m_time;
@ -71,6 +69,10 @@ private:
long long m_num_calls;
protected:
/** In laps based profiling: number of laps to run. Also
* used by DemoWorld. */
static int m_num_laps;
virtual AbstractKart *createKart(const std::string &kart_ident, int index,
int local_player_id, int global_player_id,
RaceManager::KartType type);
@ -86,6 +88,7 @@ public:
static void setProfileModeTime(float time);
static void setProfileModeLaps(int laps);
static void setProfileModeDemo(int laps);
// ------------------------------------------------------------------------
/** Returns true if profile mode was selected. */
static bool isProfileMode() {return m_profile_mode!=PROFILE_NONE; }

View File

@ -167,7 +167,7 @@ void World::init()
powerup_manager->updateWeightsForRace(num_karts);
// erase messages left over
RaceGUIBase* rg = World::getWorld()->getRaceGUI();
RaceGUIBase* rg = getRaceGUI();
rg->init();
if (rg) rg->clearAllMessages();
} // init

View File

@ -29,6 +29,7 @@
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/demo_world.hpp"
#include "modes/follow_the_leader.hpp"
#include "modes/overworld.hpp"
#include "modes/profile_world.hpp"
@ -367,7 +368,9 @@ void RaceManager::startNextRace()
// variable world. Admittedly a bit ugly, but simplifies
// handling of objects which get created in the constructor
// and need world to be defined.
if (ProfileWorld::isProfileMode())
if(DemoWorld::isDemoMode())
World::setWorld(new DemoWorld());
else if(ProfileWorld::isProfileMode())
World::setWorld(new ProfileWorld());
else if(m_minor_mode==MINOR_MODE_FOLLOW_LEADER)
World::setWorld(new FollowTheLeaderRace());

View File

@ -35,6 +35,7 @@
#include "karts/kart_properties_manager.hpp"
#include "main_loop.hpp"
#include "modes/overworld.hpp"
#include "modes/demo_world.hpp"
#include "network/network_manager.hpp"
#include "states_screens/addons_screen.hpp"
#include "states_screens/credits.hpp"
@ -129,11 +130,19 @@ void MainMenuScreen::init()
r = getWidget<RibbonWidget>("menu_toprow");
r->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
DemoWorld::resetIdleTime();
} // init
// ----------------------------------------------------------------------------
void MainMenuScreen::onUpdate(float delta, irr::video::IVideoDriver* driver)
{
// If a demo mode is started, do nothing more
if(DemoWorld::updateIdleTimeAndStartDemo(delta))
{
//StateManager::get()->popMenu();
return;
}
IconButtonWidget* addons_icon = getWidget<IconButtonWidget>("addons");
if (addons_icon != NULL)
{
@ -177,6 +186,7 @@ void MainMenuScreen::onUpdate(float delta, irr::video::IVideoDriver* driver)
void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
const int playerID)
{
DemoWorld::resetIdleTime();
// most interesting stuff is in the ribbons, so start there
RibbonWidget* ribbon = dynamic_cast<RibbonWidget*>(widget);

View File

@ -28,11 +28,11 @@ namespace GUIEngine { class Widget; class ListWidget; }
*/
class MainMenuScreen : public GUIEngine::Screen, public GUIEngine::ScreenSingleton<MainMenuScreen>
{
private:
friend class GUIEngine::ScreenSingleton<MainMenuScreen>;
MainMenuScreen();
public:
virtual void onUpdate(float delta, irr::video::IVideoDriver* driver);

View File

@ -30,6 +30,7 @@
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/kart_properties.hpp"
#include "modes/demo_world.hpp"
#include "modes/overworld.hpp"
#include "modes/world_with_rank.hpp"
#include "states_screens/dialogs/race_over_dialog.hpp"
@ -539,6 +540,12 @@ void RaceResultGUI::renderGlobal(float dt)
case RR_WAIT_TILL_END:
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
displayGPProgress();
if(m_timer - m_time_rotation > 1.0f &&
dynamic_cast<DemoWorld*>(World::getWorld()) )
{
race_manager->exitRace();
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
}
break;
} // switch