Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Mar 25, 2009, 5:23:00 PM (15 years ago)
Author:
rgrieder
Message:

Implemented new GameState concept. It doesn't differ that much from the old one, but there's still lots of changes.
The most important change is that one GameState can occur multiple times in the hierarchy.

Short log:

  • No RootGameState anymore. Simply the highest is root.
  • Game::requestGameState(name) can refer to the parent, grandparent, great-grandparent, etc. or one of the children.
  • Requested states are saved. So if you select "level", the next request (even right after the call) will be relative to "level"
  • Game::popState() will simply unload the current one
  • Re added Main.cc again because Game as well as GameState have been moved to the core
  • Adapted all GameStates to the new system

Things should already work, except for network support because standalone only works with a little hack.
We can now start creating a better hierarchy.

File:
1 moved

Legend:

Unmodified
Added
Removed
  • code/branches/gui/src/core/Game.cc

    r2843 r2844  
    3333*/
    3434
    35 #include "OrxonoxStableHeaders.h"
    3635#include "Game.h"
    3736
     
    4140#include "util/Debug.h"
    4241#include "util/Exception.h"
    43 #include "core/CommandLine.h"
    44 #include "core/ConsoleCommand.h"
    45 #include "core/Core.h"
    46 #include "core/Identifier.h"
    47 #include "core/CoreIncludes.h"
    48 #include "core/ConfigValueIncludes.h"
    49 
    50 #include "gamestates/GSRoot.h"
    51 #include "gamestates/GSGraphics.h"
    52 #include "gamestates/GSStandalone.h"
    53 #include "gamestates/GSServer.h"
    54 #include "gamestates/GSClient.h"
    55 #include "gamestates/GSDedicated.h"
    56 #include "gamestates/GSGUI.h"
    57 #include "gamestates/GSIOConsole.h"
    58 
    59 /*
    60 @brief
    61     Main method. Game starts here (except for static initialisations).
    62 */
    63 int main(int argc, char** argv)
    64 {
    65     {
    66         orxonox::Game orxonox(argc, argv);
    67         orxonox.run();
    68         // objects gets destroyed here!
    69     }
    70 
    71     // Clean up class hierarchy stuff (identifiers, xmlport, configvalue, consolecommand)
    72     // Needs to be done after Game destructor because of ~OrxonoxClass
    73     orxonox::Identifier::destroyAllIdentifiers();
    74 
    75     return 0;
    76 }
     42#include "Clock.h"
     43#include "CommandLine.h"
     44#include "ConsoleCommand.h"
     45#include "Core.h"
     46#include "CoreIncludes.h"
     47#include "ConfigValueIncludes.h"
     48#include "GameState.h"
    7749
    7850namespace orxonox
     
    8355    }
    8456
     57    struct _CoreExport GameStateTreeNode
     58    {
     59        GameState*                      state_;
     60        GameStateTreeNode*              parent_;
     61        std::vector<GameStateTreeNode*> children_;
     62    };
     63
    8564    SetCommandLineArgument(state, "gui").shortcut("s");
     65    SetCommandLineSwitch(startWithConsole);
    8666    SetConsoleCommandShortcutExternAlias(stop_game, "exit");
    8767
     68    std::map<std::string, GameState*> Game::allStates_s;
    8869    Game* Game::singletonRef_s = 0;
    8970
     
    9677        assert(singletonRef_s == 0);
    9778        singletonRef_s = this;
     79
     80        this->rootStateNode_ = 0;
     81        this->activeStateNode_ = 0;
    9882
    9983        this->abort_ = false;
     
    121105        // Destroy pretty much everyhting left
    122106        delete this->core_;
     107
     108        // Delete all GameStates created by the macros
     109        for (std::map<std::string, GameState*>::const_iterator it = allStates_s.begin(); it != allStates_s.end(); ++it)
     110            delete it->second;
    123111
    124112        assert(singletonRef_s);
     
    144132    void Game::run()
    145133    {
    146         // create the gamestates
    147         GSRoot root;
    148         GSGraphics graphics;
    149         GSStandalone standalone;
    150         GSServer server;
    151         GSClient client;
    152         GSDedicated dedicated;
    153         GSGUI gui;
    154         GSIOConsole ioConsole;
    155 
    156         // make the hierarchy
    157         root.addChild(&graphics);
    158         graphics.addChild(&standalone);
    159         graphics.addChild(&server);
    160         graphics.addChild(&client);
    161         graphics.addChild(&gui);
    162         root.addChild(&ioConsole);
    163         root.addChild(&dedicated);
    164 
    165         root.activate();
    166 
    167         // get initial state from command line
    168         root.gotoState(CommandLine::getValue("state"));
     134        // </EXPORT THIS>
     135        this->setStateHierarchy(
     136        "root"
     137        " graphics"
     138        "  gui"
     139        "  standalone"
     140        "   level"
     141        "  server"
     142        "   level"
     143        "  client"
     144        "   level"
     145        " dedicated"
     146        "  level"
     147        " ioConsole"
     148        );
     149        // </EXPORT THIS>
     150
     151
     152        // Always start with the root state
     153        this->requestedStateNodes_.push_back(this->rootStateNode_);
     154        this->activeStateNode_ = this->rootStateNode_;
     155        this->loadState(this->rootStateNode_->state_);
     156
     157        // <EXPORT THIS>
     158        if (CommandLine::getValue("startWithConsole").getBool())
     159        {
     160            // Start the game in the console
     161            this->requestState("ioConsole");
     162        }
     163        else
     164        {
     165            // Start in GUI main menu
     166            this->requestState("graphics");
     167            this->requestState("gui");
     168        }
     169        // </EXPORT THIS>
    169170
    170171        this->gameClock_->capture(); // first delta time should be about 0 seconds
    171         while (!this->abort_)
     172        while (!this->abort_ && !this->activeStates_.empty())
    172173        {
    173174            this->gameClock_->capture();
     
    179180            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
    180181
    181             // UPDATE
    182             root.tick(*this->gameClock_);
     182            // UPDATE STATE STACK
     183            while (this->requestedStateNodes_.size() > 1)
     184            {
     185                // Note: this->requestedStateNodes_.front() is the currently active state node
     186                std::vector<GameStateTreeNode*>::iterator it = this->requestedStateNodes_.begin() + 1;
     187                if (*it == this->activeStateNode_->parent_)
     188                    this->unloadState(this->activeStateNode_->state_);
     189                else // has to be child
     190                    this->loadState((*it)->state_);
     191                this->activeStateNode_ = *it;
     192                this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
     193            }
     194
     195            // UPDATE, bottom to top in the stack
     196            for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin();
     197                it != this->activeStates_.end(); ++it)
     198                (*it)->update(*this->gameClock_);
    183199
    184200            // STATISTICS
     
    206222                this->periodTime_ -= this->statisticsRefreshCycle_;
    207223            }
    208 
    209             if (root.stateRequest_ != "")
    210                 root.gotoState(root.stateRequest_);
    211         }
    212 
    213         root.gotoState("root");
    214         root.deactivate();
     224        }
     225
     226        // Unload all remaining states
     227        while (!this->activeStates_.empty())
     228            this->unloadState(this->activeStates_.back());
     229        this->activeStateNode_ = 0;
     230        this->requestedStateNodes_.clear();
    215231    }
    216232
     
    226242        this->periodTickTime_+=length;
    227243    }
     244
     245
     246    /***** GameState related *****/
     247
     248    void Game::requestState(const std::string& name)
     249    {
     250        GameState* state = this->getState(name);
     251        if (state == NULL || this->activeStateNode_ == NULL)
     252            return;
     253
     254        GameStateTreeNode* requestedNode = 0;
     255
     256        // this->requestedStateNodes_.back() is the currently active state
     257        GameStateTreeNode* lastRequestedNode = this->requestedStateNodes_.back();
     258
     259        // Already the active node?
     260        if (state == lastRequestedNode->state_)
     261        {
     262            COUT(2) << "Warning: Requesting the currently active state! Ignoring." << std::endl;
     263            return;
     264        }
     265
     266        // Check children first
     267        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
     268        {
     269            if (lastRequestedNode->children_[i]->state_ == state)
     270            {
     271                requestedNode = lastRequestedNode->children_[i];
     272                break;
     273            }
     274        }
     275
     276        // Check parent and all its grand parents
     277        GameStateTreeNode* currentNode = lastRequestedNode;
     278        while (requestedNode == NULL && currentNode->parent_ != NULL)
     279        {
     280            if (currentNode->state_ == state)
     281                requestedNode = currentNode;
     282            currentNode = currentNode->parent_;
     283        }
     284
     285        if (requestedNode == NULL)
     286            COUT(1) << "Error: Requested GameState transition is not allowed. Ignoring." << std::endl;
     287        else
     288            this->requestedStateNodes_.push_back(requestedNode);
     289    }
     290
     291    void Game::popState()
     292    {
     293        if (this->activeStateNode_ != NULL && this->requestedStateNodes_.back()->parent_)
     294            this->requestState(this->requestedStateNodes_.back()->parent_->state_->getName());
     295        else
     296            COUT(2) << "Warning: Could not pop GameState. Ignoring." << std::endl;
     297    }
     298
     299    GameState* Game::getState(const std::string& name)
     300    {
     301        std::map<std::string, GameState*>::const_iterator it = allStates_s.find(name);
     302        if (it != allStates_s.end())
     303            return it->second;
     304        else
     305        {
     306            COUT(1) << "Error: Could not find GameState '" << name << "'. Ignoring." << std::endl;
     307            return 0;
     308        }
     309    }
     310
     311    void Game::setStateHierarchy(const std::string& str)
     312    {
     313        // Split string into pieces of the form whitespacesText
     314        std::vector<std::pair<std::string, unsigned> > stateStrings;
     315        size_t pos = 0;
     316        size_t startPos = 0;
     317        while (pos < str.size())
     318        {
     319            unsigned indentation = 0;
     320            while(pos < str.size() && str[pos] == ' ')
     321                ++indentation, ++pos;
     322            startPos = pos;
     323            while(pos < str.size() && str[pos] != ' ')
     324                ++pos;
     325            stateStrings.push_back(std::pair<std::string, unsigned>(
     326                str.substr(startPos, pos - startPos), indentation));
     327        }
     328        unsigned int currentLevel = 0;
     329        GameStateTreeNode* currentNode = 0;
     330        for (std::vector<std::pair<std::string, unsigned> >::const_iterator it = stateStrings.begin(); it != stateStrings.end(); ++it)
     331        {
     332            std::string newStateName = it->first;
     333            unsigned newLevel = it->second;
     334            GameState* newState = this->getState(newStateName);
     335            if (!newState)
     336                ThrowException(GameState, std::string("GameState with name '") + newStateName + "' not found!");
     337            if (newLevel == 0)
     338            {
     339                // root
     340                if (this->rootStateNode_ != NULL)
     341                    ThrowException(GameState, "No two root GameStates are allowed!");
     342                GameStateTreeNode* newNode = new GameStateTreeNode;
     343                newNode->state_ = newState;
     344                newNode->parent_ = 0;
     345                this->rootStateNode_ = newNode;
     346                currentNode = this->rootStateNode_;
     347            }
     348            else if (currentNode)
     349            {
     350                GameStateTreeNode* newNode = new GameStateTreeNode;
     351                newNode->state_ = newState;
     352                if (newLevel < currentLevel)
     353                {
     354                    // Get down the hierarchy
     355                    do
     356                        currentNode = currentNode->parent_;
     357                    while (newLevel < --currentLevel);
     358                }
     359                if (newLevel == currentLevel)
     360                {
     361                    // same level
     362                    newNode->parent_ = currentNode->parent_;
     363                    newNode->parent_->children_.push_back(newNode);
     364                }
     365                else if (newLevel == currentLevel + 1)
     366                {
     367                    // child
     368                    newNode->parent_ = currentNode;
     369                    currentNode->children_.push_back(newNode);
     370                }
     371                else
     372                    ThrowException(GameState, "Indentation error while parsing the hierarchy.");
     373                currentNode = newNode;
     374                currentLevel = newLevel;
     375            }
     376            else
     377            {
     378                ThrowException(GameState, "No root GameState specified!");
     379            }
     380        }
     381    }
     382
     383    /*** Internal ***/
     384
     385    void Game::loadState(GameState* state)
     386    {
     387        state->activate();
     388        this->activeStates_.push_back(state);
     389    }
     390
     391    void Game::unloadState(orxonox::GameState* state)
     392    {
     393        state->deactivate();
     394        this->activeStates_.pop_back();
     395    }
     396
     397    /*static*/ bool Game::addGameState(GameState* state)
     398    {
     399        std::map<std::string, GameState*>::const_iterator it = allStates_s.find(state->getName());
     400        if (it == allStates_s.end())
     401            allStates_s[state->getName()] = state;
     402        else
     403            ThrowException(GameState, "Cannot add two GameStates with the same name to 'Game'.");
     404
     405        // just a required dummy return value
     406        return true;
     407    }
    228408}
Note: See TracChangeset for help on using the changeset viewer.