From c15528f903ebaf4c7b43c7eba78c9aa0b41d1935 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Mon, 7 May 2012 00:10:33 +0000 Subject: [PATCH] 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 --- src/ide/vc9/supertuxkart.vcproj | 8 ++ src/input/input_manager.cpp | 10 ++ src/karts/kart_with_stats.cpp | 2 +- src/main.cpp | 36 ++++++ src/modes/demo_world.cpp | 144 ++++++++++++++++++++++++ src/modes/demo_world.hpp | 87 ++++++++++++++ src/modes/profile_world.cpp | 38 ++++++- src/modes/profile_world.hpp | 11 +- src/modes/world.cpp | 2 +- src/race/race_manager.cpp | 5 +- src/states_screens/main_menu_screen.cpp | 10 ++ src/states_screens/main_menu_screen.hpp | 2 +- src/states_screens/race_result_gui.cpp | 7 ++ 13 files changed, 348 insertions(+), 14 deletions(-) create mode 100644 src/modes/demo_world.cpp create mode 100644 src/modes/demo_world.hpp diff --git a/src/ide/vc9/supertuxkart.vcproj b/src/ide/vc9/supertuxkart.vcproj index 79e43d0c7..d370fd4ab 100644 --- a/src/ide/vc9/supertuxkart.vcproj +++ b/src/ide/vc9/supertuxkart.vcproj @@ -692,6 +692,10 @@ + + @@ -1850,6 +1854,10 @@ + + diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index 710880e2b..57a694e56 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -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(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, diff --git a/src/karts/kart_with_stats.cpp b/src/karts/kart_with_stats.cpp index 857bfc848..7f683ce0b 100644 --- a/src/karts/kart_with_stats.cpp +++ b/src/karts/kart_with_stats.cpp @@ -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 // ---------------------------------------------------------------------------- diff --git a/src/main.cpp b/src/main.cpp index de9d39841..a2214f0c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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+1setUserFilename(argv[i+1]); diff --git a/src/modes/demo_world.cpp b/src/modes/demo_world.cpp new file mode 100644 index 000000000..ad4ce2f6b --- /dev/null +++ b/src/modes/demo_world.cpp @@ -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 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 &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 \ No newline at end of file diff --git a/src/modes/demo_world.hpp b/src/modes/demo_world.hpp new file mode 100644 index 000000000..376c62ba1 --- /dev/null +++ b/src/modes/demo_world.hpp @@ -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 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 &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 diff --git a/src/modes/profile_world.cpp b/src/modes/profile_world.cpp index c8c6b469e..4c60a1a12 100644 --- a/src/modes/profile_world.cpp +++ b/src/modes/profile_world.cpp @@ -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; diff --git a/src/modes/profile_world.hpp b/src/modes/profile_world.hpp index b69cf0a16..56df00bce 100644 --- a/src/modes/profile_world.hpp +++ b/src/modes/profile_world.hpp @@ -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; } diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 2824b78fd..196ee15c1 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -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 diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 349ef1ebc..023925032 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -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()); diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index f362df729..04b1b9657 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -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("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("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(widget); diff --git a/src/states_screens/main_menu_screen.hpp b/src/states_screens/main_menu_screen.hpp index 347b2995b..c713c6bf0 100644 --- a/src/states_screens/main_menu_screen.hpp +++ b/src/states_screens/main_menu_screen.hpp @@ -28,11 +28,11 @@ namespace GUIEngine { class Widget; class ListWidget; } */ class MainMenuScreen : public GUIEngine::Screen, public GUIEngine::ScreenSingleton { +private: friend class GUIEngine::ScreenSingleton; MainMenuScreen(); - public: virtual void onUpdate(float delta, irr::video::IVideoDriver* driver); diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index cf616038b..bde90ec87 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -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(World::getWorld()) ) + { + race_manager->exitRace(); + StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance()); + } break; } // switch