Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/resource/src/core/Game.cc @ 3357

Last change on this file since 3357 was 3356, checked in by rgrieder, 16 years ago

GameStates requiring graphics (Level is not one of them because it uses showsGraphics() to distinguish) are now only constructed when basic graphic support is given (GraphicsManager, InputManager and GUIManager loaded).

  • Property svn:eol-style set to native
File size: 22.8 KB
RevLine 
[2805]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30@file
31@brief
32    Implementation of the Game class.
33*/
34
35#include "Game.h"
36
37#include <exception>
[3196]38#include <boost/weak_ptr.hpp>
[2805]39
40#include "util/Debug.h"
41#include "util/Exception.h"
[3304]42#include "util/Sleep.h"
[2850]43#include "util/SubString.h"
[2844]44#include "Clock.h"
45#include "CommandLine.h"
46#include "ConsoleCommand.h"
47#include "Core.h"
48#include "CoreIncludes.h"
49#include "ConfigValueIncludes.h"
[3355]50#include "GameMode.h"
[2844]51#include "GameState.h"
[2805]52
53namespace orxonox
54{
[3196]55    using boost::shared_ptr;
56    using boost::weak_ptr;
57
[2845]58    static void stop_game()
59        { Game::getInstance().stop(); }
60    SetConsoleCommandShortcutExternAlias(stop_game, "exit");
[2805]61
[3355]62    std::map<std::string, GameStateInfo> Game::gameStateDeclarations_s;
[3280]63    Game* Game::singletonRef_s = 0;
64
65
66    /**
67    @brief
68        Represents one node of the game state tree.
69    */
70    struct GameStateTreeNode
[2844]71    {
[3356]72        std::string name_;
[3196]73        weak_ptr<GameStateTreeNode> parent_;
74        std::vector<shared_ptr<GameStateTreeNode> > children_;
[2844]75    };
76
[2805]77
78    /**
79    @brief
[3280]80        Another helper class for the Game singleton: we cannot derive
81        Game from OrxonoxClass because we need to handle the Identifier
82        destruction in the Core destructor.
83    */
84    class GameConfiguration : public OrxonoxClass
85    {
86    public:
87        GameConfiguration()
88        {
89            RegisterRootObject(GameConfiguration);
90            this->setConfigValues();
91        }
92
93        void setConfigValues()
94        {
95            SetConfigValue(statisticsRefreshCycle_, 250000)
96                .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
97            SetConfigValue(statisticsAvgLength_, 1000000)
98                .description("Sets the time in microseconds interval at which average fps, etc. gets calculated.");
[3304]99            SetConfigValue(fpsLimit_, 50)
100                .description("Sets the desired framerate (0 for no limit).");
[3280]101        }
102
103        unsigned int statisticsRefreshCycle_;
104        unsigned int statisticsAvgLength_;
[3304]105        unsigned int fpsLimit_;
[3280]106    };
107
108
109    /**
110    @brief
[2805]111        Non-initialising constructor.
112    */
[3323]113    Game::Game(const std::string& cmdLine)
[2805]114    {
[3280]115        if (singletonRef_s != 0)
116        {
117            COUT(0) << "Error: The Game singleton cannot be recreated! Shutting down." << std::endl;
118            abort();
119        }
[2805]120        singletonRef_s = this;
121
[3280]122        this->bAbort_ = false;
123        bChangingState_ = false;
[2805]124
[3352]125#ifdef ORXONOX_PLATFORM_WINDOWS
126        minimumSleepTime_ = 1000/*us*/;
127#else
128        minimumSleepTime_ = 0/*us*/;
129#endif
130
[3280]131        // Create an empty root state
[3352]132        this->declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
[3280]133
[2846]134        // Set up a basic clock to keep time
135        this->gameClock_ = new Clock();
136
[3280]137        // Create the Core
[3323]138        this->core_ = new Core(cmdLine);
[2817]139
[3356]140        // After the core has been created, we can safely instantiate the GameStates that don't require graphics
[3280]141        for (std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.begin();
142            it != gameStateDeclarations_s.end(); ++it)
143        {
[3356]144            if (!it->second.bGraphicsMode)
145                constructedStates_[it->second.stateName] = GameStateFactory::fabricate(it->second);
[3280]146        }
147
148        // The empty root state is ALWAYS loaded!
149        this->rootStateNode_ = shared_ptr<GameStateTreeNode>(new GameStateTreeNode());
[3356]150        this->rootStateNode_->name_ = "emptyRootGameState";
151        this->loadedTopStateNode_ = this->rootStateNode_;
152        this->loadedStates_.push_back(this->getState(rootStateNode_->name_));
[3280]153
154        // Do this after the Core creation!
155        this->configuration_ = new GameConfiguration();
[2805]156    }
157
158    /**
159    @brief
160    */
161    Game::~Game()
162    {
[3280]163        // Destroy the configuration helper class instance
164        delete this->configuration_;
165
166        // Destroy the GameStates (note that the nodes still point to them, but doesn't matter)
[3356]167        for (std::map<std::string, GameState*>::const_iterator it = constructedStates_.begin();
168            it != constructedStates_.end(); ++it)
[3280]169            delete it->second;
170
171        // Destroy the Core and with it almost everything
[2805]172        delete this->core_;
[2846]173        delete this->gameClock_;
174
[3280]175        // Take care of the GameStateFactories
176        GameStateFactory::destroyFactories();
[2805]177
[3280]178        // Don't assign singletonRef_s with NULL! Recreation is not supported
[2817]179    }
180
[2805]181    /**
182    @brief
183        Main loop of the orxonox game.
184    @note
185        We use the Ogre::Timer to measure time since it uses the most precise
186        method an any platform (however the windows timer lacks time when under
187        heavy kernel load!).
188    */
189    void Game::run()
190    {
[3280]191        if (this->requestedStateNodes_.empty())
192            COUT(0) << "Warning: Starting game without requesting GameState. This automatically terminates the program." << std::endl;
[2805]193
[3352]194        // reset statistics
195        this->statisticsStartTime_ = 0;
196        this->statisticsTickTimes_.clear();
197        this->periodTickTime_ = 0;
198        this->periodTime_ = 0;
199        this->avgFPS_ = 0.0f;
200        this->avgTickTime_ = 0.0f;
201        this->excessSleepTime_ = 0;
202
[2845]203        // START GAME
[3304]204        // first delta time should be about 0 seconds
205        this->gameClock_->capture();
206        // A first item is required for the fps limiter
207        StatisticsTickInfo tickInfo = {0, 0};
208        statisticsTickTimes_.push_back(tickInfo);
[3356]209        while (!this->bAbort_ && (!this->loadedStates_.empty() || this->requestedStateNodes_.size() > 0))
[2805]210        {
[3352]211            // Generate the dt
[2807]212            this->gameClock_->capture();
[2805]213
[3352]214            // Statistics init
215            StatisticsTickInfo tickInfo = {gameClock_->getMicroseconds(), 0};
[2817]216            statisticsTickTimes_.push_back(tickInfo);
217            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
218
[3352]219            // Update the GameState stack if required
220            this->updateGameStateStack();
221
222            // Core preUpdate (doesn't throw)
223            if (!this->core_->preUpdate(*this->gameClock_))
[2844]224            {
[3352]225                this->stop();
226                break;
[2844]227            }
[2805]228
[3352]229            // Update the GameStates bottom up in the stack
230            this->updateGameStates();
231
232            // Core postUpdate (doesn't throw)
233            if (!this->core_->postUpdate(*this->gameClock_))
[3280]234            {
235                this->stop();
236                break;
237            }
238
[3352]239            // Evaluate statistics
240            this->updateStatistics();
241
242            // Limit framerate
243            this->updateFPSLimiter();
244        }
245
246        // UNLOAD all remaining states
[3356]247        while (this->loadedStates_.size() > 1)
248            this->unloadState(this->loadedStates_.back()->getName());
249        this->loadedTopStateNode_ = this->rootStateNode_;
[3352]250        this->requestedStateNodes_.clear();
251    }
252
253    void Game::updateGameStateStack()
254    {
255        while (this->requestedStateNodes_.size() > 0)
256        {
257            shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
[3356]258            assert(this->loadedTopStateNode_);
259            if (!this->loadedTopStateNode_->parent_.expired() && requestedStateNode == this->loadedTopStateNode_->parent_.lock())
260                this->unloadState(loadedTopStateNode_->name_);
[3352]261            else // has to be child
[3084]262            {
[3280]263                try
264                {
[3356]265                    this->loadState(requestedStateNode->name_);
[3280]266                }
267                catch (const std::exception& ex)
268                {
[3356]269                    COUT(1) << "Error: Loading GameState '" << requestedStateNode->name_ << "' failed: " << ex.what() << std::endl;
[3352]270                    // All scheduled operations have now been rendered inert --> flush them and issue a warning
271                    if (this->requestedStateNodes_.size() > 1)
272                        COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
273                    this->requestedStateNodes_.clear();
[3280]274                    break;
275                }
[3084]276            }
[3356]277            this->loadedTopStateNode_ = requestedStateNode;
[3352]278            this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
279        }
280    }
[3084]281
[3352]282    void Game::updateGameStates()
283    {
284        // Note: The first element is the empty root state, which doesn't need ticking
[3356]285        for (std::vector<GameState*>::const_iterator it = this->loadedStates_.begin() + 1;
286            it != this->loadedStates_.end(); ++it)
[3352]287        {
288            std::string exceptionMessage;
289            try
[3349]290            {
[3352]291                // Add tick time for most of the states
292                uint64_t timeBeforeTick;
[3355]293                if ((*it)->getInfo().bIgnoreTickTime)
[3352]294                    timeBeforeTick = this->gameClock_->getRealMicroseconds();
295                (*it)->update(*this->gameClock_);
[3355]296                if ((*it)->getInfo().bIgnoreTickTime)
[3352]297                    this->subtractTickTime(static_cast<int32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
298            }
299            catch (const std::exception& ex)
300            { exceptionMessage = ex.what(); }
301            catch (...)
302            { exceptionMessage = "Unknown exception"; }
303            if (!exceptionMessage.empty())
304            {
305                COUT(1) << "An exception occurred while updating '" << (*it)->getName() << "': " << exceptionMessage << std::endl;
306                COUT(1) << "This should really never happen!" << std::endl;
307                COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
[3356]308                shared_ptr<GameStateTreeNode> current = this->loadedTopStateNode_;
309                while (current->name_ != (*it)->getName() && current)
310                    current = current->parent_.lock();
311                if (current && current->parent_.lock())
312                    this->requestState(current->parent_.lock()->name_);
[3352]313                else
314                    this->stop();
[3349]315                break;
316            }
[3352]317        }
318    }
[3349]319
[3352]320    void Game::updateStatistics()
321    {
322        // Add the tick time of this frame (rendering time has already been subtracted)
323        uint64_t currentTime = gameClock_->getMicroseconds();
324        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
325        this->statisticsTickTimes_.back().tickLength += currentRealTime - currentTime;
326        this->periodTickTime_ += currentRealTime - currentTime;
327        if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
328        {
329            std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
330            assert(it != this->statisticsTickTimes_.end());
331            int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
332            if (static_cast<int64_t>(it->tickTime) < lastTime)
[2817]333            {
[3352]334                do
[2817]335                {
[3352]336                    assert(this->periodTickTime_ >= it->tickLength);
337                    this->periodTickTime_ -= it->tickLength;
338                    ++it;
339                    assert(it != this->statisticsTickTimes_.end());
340                } while (static_cast<int64_t>(it->tickTime) < lastTime);
341                this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
342            }
[2817]343
[3352]344            uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
345            this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
346            this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
[2817]347
[3352]348            this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
[2805]349        }
[3352]350    }
[2805]351
[3352]352    void Game::updateFPSLimiter()
353    {
354        // Why configuration_->fpsLimit_ - 1? No idea, but otherwise the fps rate is always (from 10 to 200!) one frame too high
355        uint32_t nextTime = gameClock_->getMicroseconds() - excessSleepTime_ + static_cast<uint32_t>(1000000.0f / (configuration_->fpsLimit_ - 1));
356        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
357        while (currentRealTime < nextTime - minimumSleepTime_)
358        {
359            usleep(nextTime - currentRealTime);
360            currentRealTime = gameClock_->getRealMicroseconds();
361        }
362        // Integrate excess to avoid steady state error
363        excessSleepTime_ = currentRealTime - nextTime;
364        // Anti windup
365        if (excessSleepTime_ > 50000) // 20ms is about the maximum time Windows would sleep for too long
366            excessSleepTime_ = 50000;
[2805]367    }
368
369    void Game::stop()
370    {
[3280]371        this->bAbort_ = true;
[2805]372    }
[2817]373
[3349]374    void Game::subtractTickTime(int32_t length)
[2817]375    {
376        assert(!this->statisticsTickTimes_.empty());
[3349]377        this->statisticsTickTimes_.back().tickLength -= length;
378        this->periodTickTime_ -= length;
[2817]379    }
[2844]380
381
382    /***** GameState related *****/
383
384    void Game::requestState(const std::string& name)
385    {
[3356]386        if (!this->checkState(name))
387        {
388            COUT(2) << "Warning: GameState named '" << name << "' doesn't exist!" << std::endl;
[2844]389            return;
[3356]390        }
[2844]391
[3280]392        //if (this->bChangingState_)
393        //{
394        //    COUT(2) << "Warning: Requesting GameStates while loading/unloading a GameState is illegal! Ignoring." << std::endl;
395        //    return;
396        //}
[2844]397
[3280]398        shared_ptr<GameStateTreeNode> lastRequestedNode;
399        if (this->requestedStateNodes_.empty())
[3356]400            lastRequestedNode = this->loadedTopStateNode_;
[3280]401        else
402            lastRequestedNode = this->requestedStateNodes_.back();
[3356]403        if (name == lastRequestedNode->name_)
[2844]404        {
405            COUT(2) << "Warning: Requesting the currently active state! Ignoring." << std::endl;
406            return;
407        }
408
409        // Check children first
[3280]410        std::vector<shared_ptr<GameStateTreeNode> > requestedNodes;
[2844]411        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
412        {
[3356]413            if (lastRequestedNode->children_[i]->name_ == name)
[2844]414            {
[3280]415                requestedNodes.push_back(lastRequestedNode->children_[i]);
[2844]416                break;
417            }
418        }
419
[3280]420        if (requestedNodes.empty())
[2844]421        {
[3280]422            // Check parent and all its grand parents
423            shared_ptr<GameStateTreeNode> currentNode = lastRequestedNode;
424            while (currentNode != NULL)
425            {
[3356]426                if (currentNode->name_ == name)
[3280]427                    break;
428                currentNode = currentNode->parent_.lock();
429                requestedNodes.push_back(currentNode);
430            }
[2844]431        }
432
[3280]433        if (requestedNodes.empty())
[2844]434            COUT(1) << "Error: Requested GameState transition is not allowed. Ignoring." << std::endl;
435        else
[3280]436            this->requestedStateNodes_.insert(requestedStateNodes_.end(), requestedNodes.begin(), requestedNodes.end());
[2844]437    }
438
[2850]439    void Game::requestStates(const std::string& names)
440    {
441        SubString tokens(names, ",;", " ");
442        for (unsigned int i = 0; i < tokens.size(); ++i)
443            this->requestState(tokens[i]);
444    }
445
[2844]446    void Game::popState()
447    {
[3280]448        shared_ptr<GameStateTreeNode> lastRequestedNode;
449        if (this->requestedStateNodes_.empty())
[3356]450            lastRequestedNode = this->loadedTopStateNode_;
[2844]451        else
[3280]452            lastRequestedNode = this->requestedStateNodes_.back();
453        if (lastRequestedNode != this->rootStateNode_)
[3356]454            this->requestState(lastRequestedNode->parent_.lock()->name_);
[3280]455        else
456            COUT(2) << "Warning: Can't pop the internal dummy root GameState" << std::endl;
[2844]457    }
458
459    GameState* Game::getState(const std::string& name)
460    {
[3356]461        std::map<std::string, GameState*>::const_iterator it = constructedStates_.find(name);
462        if (it != constructedStates_.end())
[2844]463            return it->second;
464        else
465        {
[3356]466            std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.find(name);
467            if (it != gameStateDeclarations_s.end())
468                COUT(1) << "Error: GameState '" << name << "' has not yet been loaded." << std::endl;
469            else
470                COUT(1) << "Error: Could not find GameState '" << name << "'." << std::endl;
[2844]471            return 0;
472        }
473    }
474
475    void Game::setStateHierarchy(const std::string& str)
476    {
477        // Split string into pieces of the form whitespacesText
478        std::vector<std::pair<std::string, unsigned> > stateStrings;
479        size_t pos = 0;
480        size_t startPos = 0;
481        while (pos < str.size())
482        {
483            unsigned indentation = 0;
484            while(pos < str.size() && str[pos] == ' ')
485                ++indentation, ++pos;
486            startPos = pos;
487            while(pos < str.size() && str[pos] != ' ')
488                ++pos;
[3280]489            stateStrings.push_back(std::make_pair(str.substr(startPos, pos - startPos), indentation));
[2844]490        }
491        unsigned int currentLevel = 0;
[3280]492        shared_ptr<GameStateTreeNode> currentNode = this->rootStateNode_;
[2844]493        for (std::vector<std::pair<std::string, unsigned> >::const_iterator it = stateStrings.begin(); it != stateStrings.end(); ++it)
494        {
495            std::string newStateName = it->first;
[3280]496            unsigned newLevel = it->second + 1; // empty root is 0
[3356]497            if (!this->checkState(newStateName))
[3280]498                ThrowException(GameState, "GameState with name '" << newStateName << "' not found!");
[3356]499            if (newStateName == this->rootStateNode_->name_)
[3280]500                ThrowException(GameState, "You shouldn't use 'emptyRootGameState' in the hierarchy...");
501            shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
[3356]502            newNode->name_ = newStateName;
[3280]503
504            if (newLevel <= currentLevel)
[2844]505            {
[3280]506                do
507                    currentNode = currentNode->parent_.lock();
508                while (newLevel <= --currentLevel);
[2844]509            }
[3280]510            if (newLevel == currentLevel + 1)
[2844]511            {
[3280]512                // Add the child
513                newNode->parent_ = currentNode;
514                currentNode->children_.push_back(newNode);
[2844]515            }
516            else
[3280]517                ThrowException(GameState, "Indentation error while parsing the hierarchy.");
518            currentNode = newNode;
519            currentLevel = newLevel;
[2844]520        }
521    }
522
523    /*** Internal ***/
524
[3355]525    void Game::loadGraphics()
526    {
527        if (!GameMode::bShowsGraphics_s)
528        {
529            core_->loadGraphics();
530            GameMode::bShowsGraphics_s = true;
[3356]531
532            // Construct all the GameStates that require graphics
533            for (std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.begin();
534                it != gameStateDeclarations_s.end(); ++it)
535            {
536                if (it->second.bGraphicsMode)
537                {
538                    if (!constructedStates_.insert(std::make_pair(
539                        it->second.stateName, GameStateFactory::fabricate(it->second))).second)
540                        assert(false); // GameState was already created!
541                }
542            }
[3355]543        }
544    }
545
546    void Game::unloadGraphics()
547    {
548        if (GameMode::bShowsGraphics_s)
549        {
[3356]550            // Destroy all the GameStates that require graphics
551            for (std::map<std::string, GameState*>::iterator it = constructedStates_.begin(); it != constructedStates_.end();)
552            {
553                if (it->second->getInfo().bGraphicsMode)
554                {
555                    delete it->second;
556                    it = constructedStates_.erase(it);
557                }
558                else
559                    ++it;
560            }
561
[3355]562            core_->unloadGraphics();
563            GameMode::bShowsGraphics_s = false;
564        }
565    }
566
[3356]567    bool Game::checkState(const std::string& name) const
[2844]568    {
[3356]569        std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.find(name);
570        if (it == gameStateDeclarations_s.end())
571            return false;
572        else
573            return true;
574    }
575
576    void Game::loadState(const std::string& name)
577    {
[3280]578        this->bChangingState_ = true;
[3355]579        // If state requires graphics, load it
[3356]580        if (gameStateDeclarations_s[name].bGraphicsMode)
[3355]581            this->loadGraphics();
[3356]582        GameState* state = this->getState(name);
[3280]583        state->activate();
[3356]584        if (!this->loadedStates_.empty())
585            this->loadedStates_.back()->activity_.topState = false;
586        this->loadedStates_.push_back(state);
[2850]587        state->activity_.topState = true;
[3280]588        this->bChangingState_ = false;
[2844]589    }
590
[3356]591    void Game::unloadState(const std::string& name)
[2844]592    {
[3356]593        GameState* state = this->getState(name);
[3280]594        this->bChangingState_ = true;
[2850]595        state->activity_.topState = false;
[3356]596        this->loadedStates_.pop_back();
597        if (!this->loadedStates_.empty())
598            this->loadedStates_.back()->activity_.topState = true;
[3280]599        try
600        {
601            state->deactivate();
[3355]602            // Check if graphis is still required
603            bool graphicsRequired = false;
[3356]604            for (unsigned i = 0; i < loadedStates_.size(); ++i)
605                graphicsRequired |= loadedStates_[i]->getInfo().bGraphicsMode;
[3355]606            if (!graphicsRequired)
607                this->unloadGraphics();
[3280]608        }
609        catch (const std::exception& ex)
610        {
[3356]611            COUT(2) << "Warning: Unloading GameState '" << name << "' threw an exception: " << ex.what() << std::endl;
[3280]612            COUT(2) << "         There might be potential resource leaks involved! To avoid this, improve exception-safety." << std::endl;
613        }
614        this->bChangingState_ = false;
[2844]615    }
616
[3280]617    std::map<std::string, Game::GameStateFactory*> Game::GameStateFactory::factories_s;
618
[3355]619    /*static*/ GameState* Game::GameStateFactory::fabricate(const GameStateInfo& info)
[2844]620    {
[3355]621        std::map<std::string, GameStateFactory*>::const_iterator it = factories_s.find(info.className);
[3280]622        assert(it != factories_s.end());
[3355]623        return it->second->fabricateInternal(info);
[2844]624    }
[2927]625
[3280]626    /*static*/ void Game::GameStateFactory::destroyFactories()
[2927]627    {
[3280]628        for (std::map<std::string, GameStateFactory*>::const_iterator it = factories_s.begin(); it != factories_s.end(); ++it)
[2927]629            delete it->second;
[3280]630        factories_s.clear();
[2927]631    }
[2805]632}
Note: See TracBrowser for help on using the repository browser.