Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/Game.cc @ 3036

Last change on this file since 3036 was 3036, checked in by bknecht, 15 years ago

we do now save what level you chose last time and will preselect it for you on restart ;-). Of course you can still use —level or -l to choose your level in the old way.

  • Property svn:eol-style set to native
File size: 14.1 KB
Line 
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>
38#include <cassert>
39
40#include "util/Debug.h"
41#include "util/Exception.h"
42#include "util/SubString.h"
43#include "Clock.h"
44#include "CommandLine.h"
45#include "ConsoleCommand.h"
46#include "Core.h"
47#include "CoreIncludes.h"
48#include "ConfigValueIncludes.h"
49#include "GameState.h"
50
51namespace orxonox
52{
53    static void stop_game()
54        { Game::getInstance().stop(); }
55    SetConsoleCommandShortcutExternAlias(stop_game, "exit");
56
57    struct _CoreExport GameStateTreeNode
58    {
59        GameState*                      state_;
60        GameStateTreeNode*              parent_;
61        std::vector<GameStateTreeNode*> children_;
62    };
63
64    std::map<std::string, GameState*> Game::allStates_s;
65    Game* Game::singletonRef_s = 0;
66
67    /**
68    @brief
69        Non-initialising constructor.
70    */
71    Game::Game(int argc, char** argv)
72    {
73        assert(singletonRef_s == 0);
74        singletonRef_s = this;
75
76        this->rootStateNode_ = 0;
77        this->activeStateNode_ = 0;
78
79        this->abort_ = false;
80
81        // reset statistics
82        this->statisticsStartTime_ = 0;
83        this->statisticsTickTimes_.clear();
84        this->periodTickTime_ = 0;
85        this->periodTime_ = 0;
86        this->avgFPS_ = 0.0f;
87        this->avgTickTime_ = 0.0f;
88
89
90        // Set up a basic clock to keep time
91        this->gameClock_ = new Clock();
92
93        this->core_ = new orxonox::Core();
94        this->core_->initialise(argc, argv);
95
96        RegisterRootObject(Game);
97        this->setConfigValues();
98    }
99
100    /**
101    @brief
102    */
103    Game::~Game()
104    {
105        // Destroy pretty much everyhting left
106        delete this->core_;
107
108        // Delete all the created nodes
109        for (std::vector<GameStateTreeNode*>::const_iterator it = this->allStateNodes_.begin(); it != this->allStateNodes_.end(); ++it)
110            delete *it;
111
112        delete this->gameClock_;
113
114        assert(singletonRef_s);
115        singletonRef_s = 0;
116    }
117
118    void Game::setConfigValues()
119    {
120        SetConfigValue(statisticsRefreshCycle_, 250000)
121            .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
122        SetConfigValue(statisticsAvgLength_, 1000000)
123            .description("Sets the time in microseconds interval at which average fps, etc. gets calculated.");
124        SetConfigValue(levelName_, "presentation_dm.oxw")
125            .description("Sets the preselection of the level in the main menu.");
126    }
127
128    void Game::setLevel(std::string levelName)
129    {
130        ModifyConfigValue(levelName_, set, levelName);
131    }
132
133    const std::string& Game::getLevel()
134    {
135        std::string levelName;
136        CommandLine::getValue("level", &levelName);
137        if (levelName == "")
138            return levelName_;
139        else
140            return levelName;
141    }
142
143    /**
144    @brief
145        Main loop of the orxonox game.
146    @note
147        We use the Ogre::Timer to measure time since it uses the most precise
148        method an any platform (however the windows timer lacks time when under
149        heavy kernel load!).
150    */
151    void Game::run()
152    {
153        // Always start with the ROOT state
154        this->requestedStateNodes_.push_back(this->rootStateNode_);
155        this->activeStateNode_ = this->rootStateNode_;
156        this->loadState(this->rootStateNode_->state_);
157
158        // START GAME
159        this->gameClock_->capture(); // first delta time should be about 0 seconds
160        while (!this->abort_ && !this->activeStates_.empty())
161        {
162            this->gameClock_->capture();
163            uint64_t currentTime = this->gameClock_->getMicroseconds();
164
165            // STATISTICS
166            statisticsTickInfo tickInfo = {currentTime, 0};
167            statisticsTickTimes_.push_back(tickInfo);
168            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
169
170            // UPDATE STATE STACK
171            while (this->requestedStateNodes_.size() > 1)
172            {
173                // Note: this->requestedStateNodes_.front() is the currently active state node
174                std::vector<GameStateTreeNode*>::iterator it = this->requestedStateNodes_.begin() + 1;
175                if (*it == this->activeStateNode_->parent_)
176                    this->unloadState(this->activeStateNode_->state_);
177                else // has to be child
178                    this->loadState((*it)->state_);
179                this->activeStateNode_ = *it;
180                this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
181            }
182
183            // UPDATE, bottom to top in the stack
184            this->core_->update(*this->gameClock_);
185            for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin();
186                it != this->activeStates_.end(); ++it)
187                (*it)->update(*this->gameClock_);
188
189            // STATISTICS
190            if (this->periodTime_ > statisticsRefreshCycle_)
191            {
192                std::list<statisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
193                assert(it != this->statisticsTickTimes_.end());
194                int64_t lastTime = currentTime - this->statisticsAvgLength_;
195                if ((int64_t)it->tickTime < lastTime)
196                {
197                    do
198                    {
199                        assert(this->periodTickTime_ > it->tickLength);
200                        this->periodTickTime_ -= it->tickLength;
201                        ++it;
202                        assert(it != this->statisticsTickTimes_.end());
203                    } while ((int64_t)it->tickTime < lastTime);
204                    this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
205                }
206
207                uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
208                this->avgFPS_ = (float)framesPerPeriod / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0;
209                this->avgTickTime_ = (float)this->periodTickTime_ / framesPerPeriod / 1000.0;
210
211                this->periodTime_ -= this->statisticsRefreshCycle_;
212            }
213        }
214
215        // UNLOAD all remaining states
216        while (!this->activeStates_.empty())
217            this->unloadState(this->activeStates_.back());
218        this->activeStateNode_ = 0;
219        this->requestedStateNodes_.clear();
220    }
221
222    void Game::stop()
223    {
224        this->abort_ = true;
225    }
226
227    void Game::addTickTime(uint32_t length)
228    {
229        assert(!this->statisticsTickTimes_.empty());
230        this->statisticsTickTimes_.back().tickLength += length;
231        this->periodTickTime_+=length;
232    }
233
234
235    /***** GameState related *****/
236
237    void Game::requestState(const std::string& name)
238    {
239        GameState* state = this->getState(name);
240        if (state == NULL || this->activeStateNode_ == NULL)
241            return;
242
243        GameStateTreeNode* requestedNode = 0;
244
245        // this->requestedStateNodes_.back() is the currently active state
246        GameStateTreeNode* lastRequestedNode = this->requestedStateNodes_.back();
247
248        // Already the active node?
249        if (state == lastRequestedNode->state_)
250        {
251            COUT(2) << "Warning: Requesting the currently active state! Ignoring." << std::endl;
252            return;
253        }
254
255        // Check children first
256        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
257        {
258            if (lastRequestedNode->children_[i]->state_ == state)
259            {
260                requestedNode = lastRequestedNode->children_[i];
261                break;
262            }
263        }
264
265        // Check parent and all its grand parents
266        GameStateTreeNode* currentNode = lastRequestedNode;
267        while (requestedNode == NULL && currentNode != NULL)
268        {
269            if (currentNode->state_ == state)
270                requestedNode = currentNode;
271            currentNode = currentNode->parent_;
272        }
273
274        if (requestedNode == NULL)
275            COUT(1) << "Error: Requested GameState transition is not allowed. Ignoring." << std::endl;
276        else
277            this->requestedStateNodes_.push_back(requestedNode);
278    }
279
280    void Game::requestStates(const std::string& names)
281    {
282        SubString tokens(names, ",;", " ");
283        for (unsigned int i = 0; i < tokens.size(); ++i)
284            this->requestState(tokens[i]);
285    }
286
287    void Game::popState()
288    {
289        if (this->activeStateNode_ != NULL && this->requestedStateNodes_.back()->parent_)
290            this->requestState(this->requestedStateNodes_.back()->parent_->state_->getName());
291        else
292            COUT(2) << "Warning: Could not pop GameState. Ignoring." << std::endl;
293    }
294
295    GameState* Game::getState(const std::string& name)
296    {
297        std::map<std::string, GameState*>::const_iterator it = allStates_s.find(getLowercase(name));
298        if (it != allStates_s.end())
299            return it->second;
300        else
301        {
302            COUT(1) << "Error: Could not find GameState '" << name << "'. Ignoring." << std::endl;
303            return 0;
304        }
305    }
306
307    void Game::setStateHierarchy(const std::string& str)
308    {
309        // Split string into pieces of the form whitespacesText
310        std::vector<std::pair<std::string, unsigned> > stateStrings;
311        size_t pos = 0;
312        size_t startPos = 0;
313        while (pos < str.size())
314        {
315            unsigned indentation = 0;
316            while(pos < str.size() && str[pos] == ' ')
317                ++indentation, ++pos;
318            startPos = pos;
319            while(pos < str.size() && str[pos] != ' ')
320                ++pos;
321            stateStrings.push_back(std::pair<std::string, unsigned>(
322                str.substr(startPos, pos - startPos), indentation));
323        }
324        unsigned int currentLevel = 0;
325        GameStateTreeNode* currentNode = 0;
326        for (std::vector<std::pair<std::string, unsigned> >::const_iterator it = stateStrings.begin(); it != stateStrings.end(); ++it)
327        {
328            std::string newStateName = it->first;
329            unsigned newLevel = it->second;
330            GameState* newState = this->getState(newStateName);
331            if (!newState)
332                ThrowException(GameState, std::string("GameState with name '") + newStateName + "' not found!");
333            if (newLevel == 0)
334            {
335                // root
336                if (this->rootStateNode_ != NULL)
337                    ThrowException(GameState, "No two root GameStates are allowed!");
338                GameStateTreeNode* newNode = new GameStateTreeNode;
339                this->allStateNodes_.push_back(newNode);
340                newNode->state_ = newState;
341                newNode->parent_ = 0;
342                this->rootStateNode_ = newNode;
343                currentNode = this->rootStateNode_;
344            }
345            else if (currentNode)
346            {
347                GameStateTreeNode* newNode = new GameStateTreeNode;
348                this->allStateNodes_.push_back(newNode);
349                newNode->state_ = newState;
350                if (newLevel < currentLevel)
351                {
352                    // Get down the hierarchy
353                    do
354                        currentNode = currentNode->parent_;
355                    while (newLevel < --currentLevel);
356                }
357                if (newLevel == currentLevel)
358                {
359                    // same level
360                    newNode->parent_ = currentNode->parent_;
361                    newNode->parent_->children_.push_back(newNode);
362                }
363                else if (newLevel == currentLevel + 1)
364                {
365                    // child
366                    newNode->parent_ = currentNode;
367                    currentNode->children_.push_back(newNode);
368                }
369                else
370                    ThrowException(GameState, "Indentation error while parsing the hierarchy.");
371                currentNode = newNode;
372                currentLevel = newLevel;
373            }
374            else
375            {
376                ThrowException(GameState, "No root GameState specified!");
377            }
378        }
379    }
380
381    /*** Internal ***/
382
383    void Game::loadState(GameState* state)
384    {
385        if (!this->activeStates_.empty())
386            this->activeStates_.back()->activity_.topState = false;
387        state->activate();
388        state->activity_.topState = true;
389        this->activeStates_.push_back(state);
390    }
391
392    void Game::unloadState(orxonox::GameState* state)
393    {
394        state->activity_.topState = false;
395        state->deactivate();
396        this->activeStates_.pop_back();
397        if (!this->activeStates_.empty())
398            this->activeStates_.back()->activity_.topState = true;
399    }
400
401    /*static*/ bool Game::addGameState(GameState* state)
402    {
403        std::map<std::string, GameState*>::const_iterator it = allStates_s.find(getLowercase(state->getName()));
404        if (it == allStates_s.end())
405            allStates_s[getLowercase(state->getName())] = state;
406        else
407            ThrowException(GameState, "Cannot add two GameStates with the same name to 'Game'.");
408
409        // just a required dummy return value
410        return true;
411    }
412
413    /*static*/ void Game::destroyStates()
414    {
415        // Delete all GameStates created by the macros
416        for (std::map<std::string, GameState*>::const_iterator it = allStates_s.begin(); it != allStates_s.end(); ++it)
417            delete it->second;
418        allStates_s.clear();
419    }
420}
Note: See TracBrowser for help on using the repository browser.