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 edited

Legend:

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

    r2817 r2844  
    3434
    3535#include "GameState.h"
     36#include <cassert>
    3637#include "util/Debug.h"
    3738#include "util/Exception.h"
     39#include "Clock.h"
    3840
    3941namespace orxonox
     
    4648        : name_(name)
    4749        , parent_(0)
    48         , activeChild_(0)
    49         //, bPausegetParent()(false)
    5050    {
    51         Operations temp = {false, false, false, false, false};
    52         this->operation_ = temp;
     51        State temp = {false, false, false, false, false};
     52        this->activity_ = temp;
    5353    }
    5454
     
    5959    GameState::~GameState()
    6060    {
    61         OrxAssert(this->operation_.active == false, "Deleting an active GameState is a very bad idea..");
     61        OrxAssert(this->activity_.active == false, "Deleting an active GameState is a very bad idea..");
    6262    }
    6363
     
    7171    void GameState::addChild(GameState* state)
    7272    {
    73         if (!state)
    74             return;
    75         // check if the state/tree to be added has states in it that already exist in this tree.
    76         for (std::map<std::string, GameState*>::const_iterator it = state->allChildren_.begin();
    77             it != state->allChildren_.end(); ++it)
     73        assert(state != NULL);
     74
     75        std::map<std::string, GameState*>::const_iterator it = this->children_.find(state->getName());
     76        if (it == this->children_.end())
    7877        {
    79             if (this->getState(it->second->getName()))
    80             {
    81                 ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
    82                 return;
    83             }
     78            this->children_[state->getName()] = state;
     79            // mark us as parent
     80            state->setParent(this);
    8481        }
    85         if (this->getState(state->name_))
     82        else
    8683        {
    87             ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
    88             return;
     84            ThrowException(GameState, "Cannot add two children with the same name");
    8985        }
    90         // Make sure we don't add a tree that already has an active state.
    91         if (state->getCurrentState())
    92         {
    93             ThrowException(GameState, "Cannot merge a tree that is already active.");
    94             return;
    95         }
    96 
    97         // merge the child's children into this tree
    98         for (std::map<std::string, GameState*>::const_iterator it = state->allChildren_.begin();
    99             it != state->allChildren_.end(); ++it)
    100             this->grandchildAdded(state, it->second);
    101         // merge 'state' into this tree
    102         this->grandchildAdded(state, state);
    103 
    104         // mark us as parent
    105         state->setParent(this);
    10686    }
    10787
     
    11595    void GameState::removeChild(GameState* state)
    11696    {
    117         std::map<GameState*, GameState*>::iterator it = this->grandchildrenToChildren_.find(state);
    118         if (it != this->grandchildrenToChildren_.end())
    119         {
    120             if (state->isInSubtree(getCurrentState()))
    121             {
    122                 ThrowException(GameState, "Cannot remove an active game state child '"
    123                     + state->getName() + "' from '" + name_ + "'.");
    124                 //COUT(2) << "Warning: Cannot remove an active game state child '" << state->getName()
    125                 //    << "' from '" << name_ << "'." << std::endl;
    126             }
    127             else
    128             {
    129                 for (std::map<GameState*, GameState*>::const_iterator it = state->grandchildrenToChildren_.begin();
    130                     it != state->grandchildrenToChildren_.end(); ++it)
    131                     this->grandchildRemoved(it->first);
    132                 this->grandchildRemoved(state);
    133             }
    134         }
     97        assert(state != NULL);
     98
     99        std::map<std::string, GameState*>::iterator it = this->children_.find(state->getName());
     100        if (it != this->children_.end())
     101            this->children_.erase(it);
    135102        else
    136103        {
    137104            ThrowException(GameState, "Game state '" + name_ + "' doesn't have a child named '"
    138105                + state->getName() + "'.");
    139             //COUT(2) << "Warning: Game state '" << name_ << "' doesn't have a child named '"
    140             //    << state->getName() << "'. Removal skipped." << std::endl;
    141106        }
    142107    }
    143108
    144     /**
    145     @brief
    146         Removes a child by name. This splits the tree in two parts,
    147         each of them functional on its own.
    148     @param state
    149         GameState by name
    150     */
    151 
    152     void GameState::removeChild(const std::string& name)
     109    void GameState::activateInternal()
    153110    {
    154         GameState* state = getState(name);
    155         if (state)
    156         {
    157             removeChild(state);
    158         }
    159         else
    160         {
    161             ThrowException(GameState, "GameState '" + name + "' doesn't exist.");
    162             //COUT(2) << "Warning: GameState '" << name << "' doesn't exist." << std::endl;
    163         }
     111        this->activity_.activating = true;
     112        this->activate();
     113        this->activity_.activating = false;
     114        this->activity_.active = true;
    164115    }
    165116
    166     /**
    167     @brief
    168         Tells a state that one of its children has added a child. This is necessary
    169         to fill the internal maps correctly.
    170     @param child
    171         The child who notices this state.
    172     @param grandchild
    173         The child that has been added.
    174     */
    175     inline void GameState::grandchildAdded(GameState* child, GameState* grandchild)
     117    void GameState::deactivateInternal()
    176118    {
    177         // fill the two maps correctly.
    178         this->allChildren_[grandchild->getName()] = grandchild;
    179         this->grandchildrenToChildren_[grandchild] = child;
    180         if (this->getParent())
    181             this->getParent()->grandchildAdded(this, grandchild);
     119        this->activity_.active = false;
     120        this->activity_.deactivating = true;
     121        this->activate();
     122        this->activity_.deactivating = false;
     123        this->activity_.suspended = false;
     124        this->activity_.updating = false;
    182125    }
    183126
    184     /**
    185     @brief
    186         Tells a state that one of its children has removed a child. This is necessary
    187         to fill the internal maps correctly.
    188     @param child
    189         The child who notices this state.
    190     @param grandchild
    191         The child that has been removed.
    192     */
    193     inline void GameState::grandchildRemoved(GameState* grandchild)
     127    void GameState::updateInternal(const Clock& time)
    194128    {
    195         // adjust the two maps correctly.
    196         this->allChildren_.erase(grandchild->getName());
    197         this->grandchildrenToChildren_.erase(grandchild);
    198         if (this->getParent())
    199             this->getParent()->grandchildRemoved(grandchild);
    200     }
    201 
    202     /**
    203     @brief
    204         Checks whether a specific game states exists in the hierarchy.
    205     @remarks
    206         Remember that the every node has a map with all its child nodes.
    207     */
    208     GameState* GameState::getState(const std::string& name)
    209     {
    210         if (this->getParent())
    211             return this->getParent()->getState(name);
    212         else
    213         {
    214             // The map only contains children, so check ourself first
    215             if (name == this->name_)
    216                 return this;
    217             // Search in the map. If there is no entry, we can be sure the state doesn't exist.
    218             std::map<std::string, GameState*>::const_iterator it = this->allChildren_.find(name);
    219             return (it!= this->allChildren_.end() ? it->second : 0);
    220         }
    221     }
    222 
    223     /**
    224     @brief
    225         Returns the root node of the tree.
    226     */
    227     GameState* GameState::getRoot()
    228     {
    229         if (this->getParent())
    230             return this->getParent()->getRoot();
    231         else
    232             return this;
    233     }
    234 
    235     /**
    236     @brief
    237         Returns the current active state.
    238     @remarks
    239         Remember that the current active state is the one that does not
    240         have active children itself. Many states can be active at once.
    241     */
    242     GameState* GameState::getCurrentState()
    243     {
    244         if (this->operation_.active)
    245         {
    246             if (this->activeChild_)
    247                 return this->activeChild_->getCurrentState();
    248             else
    249                 return this;
    250         }
    251         else
    252         {
    253             if (this->getParent())
    254                 return this->getParent()->getCurrentState();
    255             else
    256                 return 0;
    257         }
    258     }
    259 
    260     /**
    261     @brief
    262         Determines whether 'state' is in this subtree, including this node.
    263     */
    264     bool GameState::isInSubtree(GameState* state) const
    265     {
    266         return (grandchildrenToChildren_.find(state) != grandchildrenToChildren_.end()
    267                 || state == this);
    268     }
    269 
    270     /**
    271     @brief
    272         Makes a state transition according to the state tree. You can choose any state
    273         in the tree to do the call. The function finds the current state on its own.
    274     @param state
    275         The state to be entered, has to exist in the tree.
    276     */
    277     void GameState::requestState(const std::string& name)
    278     {
    279         assert(getRoot());
    280         getRoot()->requestState(name);
    281     }
    282 
    283     /**
    284     @brief
    285         Internal method that actually makes the state transition. Since it is internal,
    286         the method can assume certain things to be granted (like 'this' is always active).
    287     */
    288     void GameState::makeTransition(GameState* source, GameState* destination)
    289     {
    290         if (source == this->getParent())
    291         {
    292             // call is from the parent
    293             this->activate();
    294         }
    295         else if (source == 0)
    296         {
    297             // call was just started by root
    298             // don't do anyting yet
    299         }
    300         else
    301         {
    302             // call is from a child
    303             this->activeChild_ = 0;
    304         }
    305 
    306         if (destination == this)
    307             return;
    308 
    309         // Check for 'destination' in the children map first
    310         std::map<GameState*, GameState*>::const_iterator it
    311             = this->grandchildrenToChildren_.find(destination);
    312         if (it != this->grandchildrenToChildren_.end())
    313         {
    314             // child state. Don't use 'state', might be a grandchild!
    315             this->activeChild_ = it->second;
    316             it->second->makeTransition(this, destination);
    317         }
    318         else
    319         {
    320             // parent. We can be sure of this.
    321             assert(this->getParent() != 0);
    322 
    323             this->deactivate();
    324             this->getParent()->makeTransition(this, destination);
    325         }
    326     }
    327 
    328     /**
    329     @brief
    330         Activates the state. Only sets bActive_ to true and notifies the parent.
    331     */
    332     void GameState::activate()
    333     {
    334         this->operation_.active = true;
    335         this->operation_.entering = true;
    336         this->enter();
    337         this->operation_.entering = false;
    338     }
    339 
    340     /**
    341         Activates the state. Only sets bActive_ to false and notifies the parent.
    342     */
    343     void GameState::deactivate()
    344     {
    345         this->operation_.leaving = true;
    346         this->leave();
    347         this->operation_.leaving = false;
    348         this->operation_.active = false;
    349     }
    350 
    351     /**
    352     @brief
    353         Update method that calls ticked() with enclosed bRunning_ = true
    354         If there was a state transition request within ticked() then this
    355         method will transition in the end.
    356     @param dt Delta time
    357     @note
    358         This method is not virtual! You cannot override it therefore.
    359     */
    360     void GameState::tick(const Clock& time)
    361     {
    362         this->operation_.running = true;
    363         this->ticked(time);
    364         this->operation_.running = false;
     129        this->activity_.updating = true;
     130        this->update(time);
     131        this->activity_.updating = false;
    365132    }
    366133}
Note: See TracChangeset for help on using the changeset viewer.