diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index df82a5fa7..ab13a8b29 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -313,7 +313,7 @@ void IrrDriver::doApplyResSettings() kart_properties_manager -> unloadAllKarts(); powerup_manager -> removeTextures(); GUIEngine::clear(); - GUIEngine::cleanUp(); + GUIEngine::cleanUp(); //FIXME: cleanUp is not called when exiting normally, only when changing resolution! //std::cout << "^^^^^^^^ Closing m_device ^^^^^^^^\n"; m_device->closeDevice(); diff --git a/src/guiengine/abstract_state_manager.cpp b/src/guiengine/abstract_state_manager.cpp index 0001d4405..aa72e816b 100644 --- a/src/guiengine/abstract_state_manager.cpp +++ b/src/guiengine/abstract_state_manager.cpp @@ -21,7 +21,6 @@ #include #include "main_loop.hpp" -#include "audio/music_manager.hpp" #include "guiengine/engine.hpp" #include "guiengine/screen.hpp" #include "input/device_manager.hpp" @@ -41,27 +40,22 @@ AbstractStateManager::AbstractStateManager() #pragma mark Other #endif -/* -void initGUI() -{ - IrrlichtDevice* device = irr_driver->getDevice(); - video::IVideoDriver* driver = device->getVideoDriver(); - GUIEngine::init(device, driver, &eventCallback); -} - */ - // ----------------------------------------------------------------------------- + void AbstractStateManager::enterGameState() { if (getCurrentScreen() != NULL) getCurrentScreen()->tearDown(); m_menu_stack.clear(); m_menu_stack.push_back("race"); setGameState(GAME); - cleanForGame(); + GUIEngine::cleanForGame(); + + //FIXME: this probably doesn't belong in the *abstract* state manager input_manager->setMode(InputManager::INGAME); } // ----------------------------------------------------------------------------- + GameState AbstractStateManager::getGameState() { return m_game_mode; @@ -87,6 +81,8 @@ void AbstractStateManager::pushMenu(std::string name) if (m_game_mode == GAME) { setGameState(INGAME_MENU); + + //FIXME: this doesn't go in the *abstract* state manager if (World::getWorld() != NULL) World::getWorld()->pause(); } else @@ -113,7 +109,9 @@ void AbstractStateManager::replaceTopMostScreen(Screen* screen) // Send tear-down event to previous menu if (m_menu_stack.size() > 0) getCurrentScreen()->tearDown(); + //FIXME: this doesn't go in the *abstract* state manager input_manager->setMode(InputManager::MENU); + m_menu_stack[m_menu_stack.size()-1] = name; switchToScreen(name.c_str()); @@ -150,6 +148,7 @@ void AbstractStateManager::popMenu() if (m_menu_stack.size() == 0) { + //FIXME: this doesn't go in the *abstract* state manager main_loop->abort(); return; } @@ -161,10 +160,13 @@ void AbstractStateManager::popMenu() m_menu_stack.push_back("race"); if (m_game_mode == INGAME_MENU) { + //FIXME: this doesn't go in the *abstract* state manager if (World::getWorld() != NULL) World::getWorld()->unpause(); } setGameState(GAME); - cleanForGame(); + GUIEngine::cleanForGame(); + + //FIXME: this doesn't go in the *abstract* state manager input_manager->setMode(InputManager::INGAME); } else @@ -180,23 +182,10 @@ void AbstractStateManager::setGameState(GameState state) { if (m_game_mode == state) return; // no change + GameState previous = m_game_mode; m_game_mode = state; - if (m_game_mode == GAME) - { - irr_driver->hidePointer(); - } - else - { - // menu - irr_driver->showPointer(); - - if (m_game_mode == MENU) - { - //FIXME: not up to the *abstract* state manager to do this - music_manager->startMusic(stk_config->m_title_music); - } - } + onGameStateChange(previous, state); } // ----------------------------------------------------------------------------- @@ -205,15 +194,21 @@ void AbstractStateManager::resetAndGoToScreen(Screen* screen) { std::string name = screen->getName(); + //FIXME: this doesn't go in the *abstract* state manager assert(World::getWorld()==NULL); if (m_game_mode != GAME) getCurrentScreen()->tearDown(); m_menu_stack.clear(); + //FIXME: this doesn't go in the *abstract* state manager input_manager->setMode(InputManager::MENU); + m_menu_stack.push_back(name); setGameState(MENU); + + //FIXME: this doesn't go in the *abstract* state manager sfx_manager->positionListener( Vec3(0,0,0), Vec3(0,1,0) ); + switchToScreen(name.c_str()); getCurrentScreen()->init(); } @@ -225,6 +220,7 @@ void AbstractStateManager::resetAndSetStack(Screen* screens[]) assert(screens != NULL); assert(screens[0] != NULL); + //FIXME: this doesn't go in the *abstract* state manager input_manager->setMode(InputManager::MENU); if (m_game_mode != GAME) getCurrentScreen()->tearDown(); @@ -237,7 +233,9 @@ void AbstractStateManager::resetAndSetStack(Screen* screens[]) setGameState(MENU); + //FIXME: this doesn't go in the *abstract* state manager sfx_manager->positionListener( Vec3(0,0,0), Vec3(0,1,0) ); + switchToScreen(m_menu_stack[m_menu_stack.size()-1].c_str()); getCurrentScreen()->init(); } diff --git a/src/guiengine/abstract_state_manager.hpp b/src/guiengine/abstract_state_manager.hpp index bff750887..afd6e60f0 100644 --- a/src/guiengine/abstract_state_manager.hpp +++ b/src/guiengine/abstract_state_manager.hpp @@ -44,27 +44,55 @@ namespace GUIEngine void setGameState(GameState state); public: + + /** inits an AbstractStateManager is MENU state */ AbstractStateManager(); + virtual ~AbstractStateManager() { } + /** \brief adds a menu at the top of the screens stack */ void pushScreen(Screen* screen); + /** \brief replaces the menu at the top of the screens stack + * (i.e. pops the topmost screen and adds this one instead, but without + * displaying the second-topmost menu of the stack in-between) + */ void replaceTopMostScreen(Screen* screen); + + /** + * \brief removes the menu at the top of the screens stack + * If the stack becomes empty after performing the pop (i.e. if it contained + * only one item prior to the call), the game is aborted. + * In other cases, the second-topmost screen is displayed. + */ void popMenu(); + + /** + * \brief clears the menu stack and starts afresh with a new stack containing only + * the given screen + */ void resetAndGoToScreen(Screen* screen); /** - * Sets the whole menu stack. Only the topmost screen will be inited/shown, but others remain + * \brief Sets the whole menu stack. + * Only the topmost screen will be inited/shown, but others remain * under for cases where the user wants to go back. - * @param screens an array containing the menus that should go into stack. The first item will be + * \param screens an array containing the menus that should go into stack. The first item will be * the bottom item in the stack, the last item will be the stack top. Array must be * NULL-terminated. */ void resetAndSetStack(Screen* screens[]); + + /** + * \brief call to make the state manager enter game mode. + * Causes the menu stack to be cleared; all widgets shown on screen are removed + */ void enterGameState(); + /** \return the current state of the game */ GameState getGameState(); + /** \brief to be called after e.g. a resolution switch */ void reshowTopMostMenu(); /* *********************************** @@ -72,10 +100,15 @@ namespace GUIEngine *********************************** */ /** - * callback called whenever escape was pressed (or any similar cancel operation) - */ + * \brief callback invoked whenever escape was pressed (or any similar cancel operation) + */ virtual void escapePressed() = 0; + /** + * \brief callback invoked when game mode changes (e.g. goes from "menu" to "in-game") + */ + virtual void onGameStateChange(GameState previousState, GameState newState) = 0; + }; } diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index 417033001..2e92fb00e 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -410,6 +410,8 @@ namespace GUIEngine using namespace Private; ptr_vector needsUpdate; + + //FIXME: the contents of this vector are never ever freed ptr_vector g_loaded_screens; float dt = 0; @@ -469,12 +471,16 @@ void clear() if (g_current_screen != NULL) g_current_screen->elementsWereDeleted(); g_current_screen = NULL; } -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + void cleanForGame() { clear(); + + //FIXME: I'm not very sure why this isn't called in the regular clear() method?? needsUpdate.clearWithoutDeleting(); } + // ----------------------------------------------------------------------------- void switchToScreen(const char* screen_name) { @@ -512,18 +518,21 @@ void switchToScreen(const char* screen_name) g_current_screen->addWidgets(); } // ----------------------------------------------------------------------------- + void addScreenToList(Screen* cutscene) { g_loaded_screens.push_back(cutscene); } + // ----------------------------------------------------------------------------- -/** to be called after e.g. a resolution switch */ + void reshowCurrentScreen() { needsUpdate.clearWithoutDeleting(); g_state_manager->reshowTopMostMenu(); //g_current_screen->addWidgets(); } + // ----------------------------------------------------------------------------- void cleanUp() { @@ -614,15 +623,9 @@ void init(IrrlichtDevice* device_a, IVideoDriver* driver_a, AbstractStateManager // set event receiver g_device->setEventReceiver(EventHandler::get()); } + // ----------------------------------------------------------------------------- -/** transmit event to user event callback (out of encapsulated GUI module) */ -void transmitEvent(Widget* widget, std::string& name, const int playerID) -{ - assert(g_state_manager != NULL); - getCurrentScreen()->eventCallback(widget, name, playerID); -} -// ----------------------------------------------------------------------------- void render(float elapsed_time) { GUIEngine::dt = elapsed_time; diff --git a/src/guiengine/engine.hpp b/src/guiengine/engine.hpp index fb880e168..c8b38ccad 100644 --- a/src/guiengine/engine.hpp +++ b/src/guiengine/engine.hpp @@ -33,7 +33,7 @@ /** * \ingroup guiengine - * \brief Contains all GUI engine related classes + * \brief Contains all GUI engine related classes and functions */ namespace GUIEngine { @@ -41,26 +41,31 @@ namespace GUIEngine class CutScene; class Widget; - /** Returns the widget currently focused by given player, or NULL if none. - Do NOT use irrLicht's GUI focus facilities; it's too limited for our - needs, so we use ours. (i.e. always call these functions are never those - in IGUIEnvironment) */ + /** \brief Returns the widget currently focused by given player, or NULL if none. + * \note Do NOT use irrLicht's GUI focus facilities; it's too limited for our + * needs, so we use ours. (i.e. always call these functions are never those + * in IGUIEnvironment) + */ Widget* getFocusForPlayer(const int playerID); - /** Focuses nothing for given player (removes any selection for this player). - Do NOT use irrLicht's GUI focus facilities; it's too limited for our - needs, so we use ours. (i.e. always call these functions are never those - in IGUIEnvironment) */ + /** \brief Focuses nothing for given player (removes any selection for this player). + * \note Do NOT use irrLicht's GUI focus facilities; it's too limited for our + * needs, so we use ours. (i.e. always call these functions are never those + * in IGUIEnvironment) + */ void focusNothingForPlayer(const int playerID); - /** Returns whether given the widget is currently focused by given player. - Do NOT use irrLicht's GUI focus facilities; it's too limited for our - needs, so we use ours. (i.e. always call these functions are never those - in IGUIEnvironment) */ + /** \brief Returns whether given the widget is currently focused by given player. + * \note Do NOT use irrLicht's GUI focus facilities; it's too limited for our + * needs, so we use ours. (i.e. always call these functions are never those + * in IGUIEnvironment) + */ bool isFocusedForPlayer(const Widget*w, const int playerID); - // In an attempt to make getters as fast as possible by possibly allowing inlining - // These fields should never be accessed outside of the GUI engine. + /** + * In an attempt to make getters as fast as possible, by possibly still allowing inlining + * These fields should never be accessed outside of the GUI engine. + */ namespace Private { extern irr::gui::IGUIEnvironment* g_env; @@ -76,48 +81,127 @@ namespace GUIEngine extern Widget* g_focus_for_player[MAX_PLAYER_COUNT]; } - inline IrrlichtDevice* getDevice() { return Private::g_device; } - inline irr::gui::IGUIEnvironment* getGUIEnv() { return Private::g_env; } - inline irr::video::IVideoDriver* getDriver() { return Private::g_driver; } - inline irr::gui::IGUIFont* getSmallFont() { return Private::g_small_font; } - inline irr::gui::IGUIFont* getFont() { return Private::g_font; } - inline irr::gui::IGUIFont* getTitleFont() { return Private::g_title_font; } - inline Screen* getCurrentScreen() { return Private::g_current_screen; } - inline AbstractStateManager* getStateManager() { return Private::g_state_manager; } - inline Skin* getSkin() { return Private::g_skin; } - - /** Returns the height of the font in pixels */ - int getFontHeight(); - - int getSmallFontHeight(); - - float getLatestDt(); - - /** show a warning message to explain to the player that only the game master cn act at this point */ - void showMasterOnlyString(); - - /** Add a cutscene to the list of screens known by the gui engine */ - void addScreenToList(Screen* screen); - - // Widgets that need to be notified at every frame can add themselves there + /** Widgets that need to be notified at every frame can add themselves there (FIXME: unclean) */ extern ptr_vector needsUpdate; + /** + * \brief Call this method to init the GUI engine. + * \precondition A irrlicht device and its corresponding video drivers must have been created + * \param device An initialized irrlicht device object + * \param driver An initialized irrlicht driver object + * \param state_manager An instance of a class derived from abstract base AbstractStateManager + */ void init(irr::IrrlichtDevice* device, irr::video::IVideoDriver* driver, AbstractStateManager* state_manager); + + /** + * \brief frees all resources allocated by GUIEngine::init and subsequent uses of the GUI engine. + */ void cleanUp(); - /** Low-level mean to change current screen. Use a state manager instead to get higher-level functionnality. */ + + /** + * \return the irrlicht device object + */ + inline IrrlichtDevice* getDevice() { return Private::g_device; } + + /** + * \return the irrlicht GUI environment object + */ + inline irr::gui::IGUIEnvironment* getGUIEnv() { return Private::g_env; } + + /** + * \return the irrlicht video driver object + */ + inline irr::video::IVideoDriver* getDriver() { return Private::g_driver; } + + /** + * \return the smaller font (useful for less important messages) + */ + inline irr::gui::IGUIFont* getSmallFont() { return Private::g_small_font; } + + /** + * \return the "normal" font (useful for text) + */ + inline irr::gui::IGUIFont* getFont() { return Private::g_font; } + + /** + * \return the "title" font (it's bigger and orange, useful for headers/captions) + */ + inline irr::gui::IGUIFont* getTitleFont() { return Private::g_title_font; } + + /** + * \return the currently shown screen, or NULL if none + */ + inline Screen* getCurrentScreen() { return Private::g_current_screen; } + + /** + * \return the state manager being used, as passed to GUIEngine::init + */ + inline AbstractStateManager* getStateManager() { return Private::g_state_manager; } + + /** + * \precondition GUIEngine::init must have been called first + * \return the skin object used to render widgets + */ + inline Skin* getSkin() { return Private::g_skin; } + + /** \return the height of the font in pixels */ + int getFontHeight(); + + /** \return the height of the small font in pixels */ + int getSmallFontHeight(); + + /** + * \precondition the value returned by this function is only valid when invoked from GUIEngine::render + * \return the time delta between the last two frames + */ + float getLatestDt(); + + /** \brief show a warning message to explain to the player that only the game master + * can act at this point + */ + void showMasterOnlyString(); + + /** \brief Add a screen to the list of screens known by the gui engine */ + void addScreenToList(Screen* screen); + + /** \brief Low-level mean to change current screen. + * \note Do not use directly. Use a state manager instead to get higher-level functionnality. + */ void switchToScreen(const char* ); + + /** \brief erases the currently displayed screen, removing all added irrLicht widgets + * \note Do not use directly. Use a state manager instead to get higher-level functionnality. + */ void clear(); + + /** \brief like GUIEngine::clear, but to be called before going into game */ void cleanForGame(); + /** \brief to be called after e.g. a resolution switch */ void reshowCurrentScreen(); + /** + * \brief called on every frame to trigger the rendering of the GUI + */ void render(float dt); + + /** \brief renders a "loading" screen */ void renderLoading(); - void transmitEvent(Widget* widget, std::string& name, const int playerID); + //void transmitEvent(Widget* widget, std::string& name, const int playerID); + /** \brief Finds a widget from its name (PROP_ID) in the current screen/dialog + * \param name the name (PROP_ID) of the widget to search for + * \return the widget that bears that name, or NULL if it was not found + */ Widget* getWidget(const char* name); + + /** \brief Finds a widget from its irrlicht widget ID in the current screen/dialog + * \param name the irrlicht widget ID (not to be confused with PROP_ID, which is a string) + * of the widget to search for + * \return the widget that bears that irrlicht ID, or NULL if it was not found + */ Widget* getWidget(const int id); } diff --git a/src/guiengine/event_handler.cpp b/src/guiengine/event_handler.cpp index 7cd685938..1ee70bb4d 100644 --- a/src/guiengine/event_handler.cpp +++ b/src/guiengine/event_handler.cpp @@ -141,7 +141,7 @@ EventPropagation EventHandler::onGUIEvent(const SEvent& event) if (playerID == -1) break; if (input_manager->masterPlayerOnly() && playerID != 0) break; - if (ribbon->mouseHovered(w, playerID) == EVENT_LET) transmitEvent(ribbon, ribbon->m_properties[PROP_ID], playerID); + if (ribbon->mouseHovered(w, playerID) == EVENT_LET) sendEventToUser(ribbon, ribbon->m_properties[PROP_ID], playerID); if (ribbon->m_event_handler != NULL) ribbon->m_event_handler->mouseHovered(w, playerID); ribbon->setFocusForPlayer(playerID); } @@ -265,10 +265,10 @@ EventPropagation EventHandler::onWidgetActivated(GUIEngine::Widget* w, const int if (ModalDialog::getCurrent()->processEvent(parent->m_properties[PROP_ID]) == EVENT_BLOCK) return EVENT_BLOCK; } - transmitEvent(parent, parent->m_properties[PROP_ID], playerID); + sendEventToUser(parent, parent->m_properties[PROP_ID], playerID); } } - else transmitEvent(w, w->m_properties[PROP_ID], playerID); + else sendEventToUser(w, w->m_properties[PROP_ID], playerID); return EVENT_BLOCK; } @@ -301,7 +301,7 @@ void EventHandler::processAction(const int action, const unsigned int value, Inp { if (widget_to_call->leftPressed(playerID) == EVENT_LET) { - transmitEvent(w, w->m_properties[PROP_ID], playerID); + sendEventToUser(w, w->m_properties[PROP_ID], playerID); } widget_to_call = widget_to_call->m_event_handler; } @@ -309,7 +309,7 @@ void EventHandler::processAction(const int action, const unsigned int value, Inp if (widget_to_call->leftPressed(playerID) == EVENT_LET) { - transmitEvent(widget_to_call, widget_to_call->m_properties[PROP_ID], playerID); + sendEventToUser(widget_to_call, widget_to_call->m_properties[PROP_ID], playerID); } } break; @@ -328,14 +328,14 @@ void EventHandler::processAction(const int action, const unsigned int value, Inp { if (widget_to_call->rightPressed(playerID) == EVENT_LET) { - transmitEvent(widget_to_call, w->m_properties[PROP_ID], playerID); + sendEventToUser(widget_to_call, w->m_properties[PROP_ID], playerID); } widget_to_call = widget_to_call->m_event_handler; } if (widget_to_call->rightPressed(playerID) == EVENT_LET) { - transmitEvent(widget_to_call, widget_to_call->m_properties[PROP_ID], playerID); + sendEventToUser(widget_to_call, widget_to_call->m_properties[PROP_ID], playerID); } } break; @@ -572,6 +572,13 @@ void EventHandler::navigateDown(const int playerID, Input::InputType type, const // ----------------------------------------------------------------------------- +void EventHandler::sendEventToUser(Widget* widget, std::string& name, const int playerID) +{ + getCurrentScreen()->eventCallback(widget, name, playerID); +} + +// ----------------------------------------------------------------------------- + EventHandler* event_handler_singleton = NULL; EventHandler* EventHandler::get() diff --git a/src/guiengine/event_handler.hpp b/src/guiengine/event_handler.hpp index d80dba93f..375bc0b0d 100644 --- a/src/guiengine/event_handler.hpp +++ b/src/guiengine/event_handler.hpp @@ -57,6 +57,13 @@ namespace GUIEngine void navigateUp(const int playerID, Input::InputType type, const bool pressedDown); void navigateDown(const int playerID, Input::InputType type, const bool pressedDown); + /** \brief send an event to the GUI module user's event callback + * \param widget the widget that triggerred this event + * \param name the name/ID (PROP_ID) of the widget that triggerred this event + * \param playerID ID of the player that triggerred this event + */ + void sendEventToUser(Widget* widget, std::string& name, const int playerID); + public: EventHandler(); ~EventHandler(); diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index 5f69a94c3..3aa4c949b 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -24,7 +24,6 @@ #include #include #include "items/item.hpp" -#include "lisp/lisp.hpp" class Kart; diff --git a/src/states_screens/state_manager.cpp b/src/states_screens/state_manager.cpp index 6866968b5..f79ddd7ef 100644 --- a/src/states_screens/state_manager.cpp +++ b/src/states_screens/state_manager.cpp @@ -18,6 +18,8 @@ #include "states_screens/state_manager.hpp" +#include "audio/music_manager.hpp" +#include "config/stk_config.hpp" #include "guiengine/engine.hpp" #include "guiengine/modaldialog.hpp" #include "guiengine/screen.hpp" @@ -170,6 +172,27 @@ void StateManager::escapePressed() } } +// ---------------------------------------------------------------------------- + +void StateManager::onGameStateChange(GameState previousState, GameState newState) +{ + if (newState == GAME) + { + irr_driver->hidePointer(); + } + else // menu (including in-game menu) + { + irr_driver->showPointer(); + + if (m_game_mode == MENU) + { + music_manager->startMusic(stk_config->m_title_music); + } + } +} + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- #if 0 #pragma mark - @@ -191,12 +214,14 @@ StateManager::ActivePlayer::~ActivePlayer() } // ~ActivePlayer // ---------------------------------------------------------------------------- + void StateManager::ActivePlayer::setPlayerProfile(PlayerProfile* player) { m_player = player; } // setPlayerProfile // ---------------------------------------------------------------------------- + void StateManager::ActivePlayer::setDevice(InputDevice* device) { // unset player from previous device he was assigned to, if any diff --git a/src/states_screens/state_manager.hpp b/src/states_screens/state_manager.hpp index c527a0c57..9209020ad 100644 --- a/src/states_screens/state_manager.hpp +++ b/src/states_screens/state_manager.hpp @@ -119,17 +119,25 @@ public: * so no need to delete it yourself. */ // void addActivePlayer(ActivePlayer* p); + int createActivePlayer(PlayerProfile *profile, InputDevice *device); void removeActivePlayer(int id); int activePlayerCount(); void resetActivePlayers(); + /** \return whether to reduce FPS at the moment + * \note this can be useful to avoid being too CPU/GPU intensive in parts of the + * game that don't require high framerates, like menus + */ bool throttleFPS(); + /** \brief implementing callback from base class AbstractStateManager */ void escapePressed(); - - //void eventCallback(GUIEngine::Widget* widget, const std::string& name); + + /** \brief implementing callback from base class AbstractStateManager */ + virtual void onGameStateChange(GUIEngine::GameState previousState, GUIEngine::GameState newState); + // singleton static StateManager* get();