Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/core/GameState.cc @ 1660

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

GameState class added. Tested and working, but now comes the hard part: Implementing the actual states…

  • Property svn:eol-style set to native
File size: 8.0 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 GameState class.
33*/
34
35#include "GameState.h"
36#include <cassert>
37#include "Debug.h"
38#include "Exception.h"
39
40namespace orxonox
41{
42    /**
43    @brief
44        Constructor only initialises variables and sets the name permanently.
45    */
46    GameState::GameState(const std::string& name)
47        : name_(name)
48        , bPauseParent_(false)
49        , bActive_(false)
50        , bSuspended_(false)
51        , parent_(0)
52        , activeChild_(0)
53    {
54    }
55
56    /**
57    @brief
58        Adds a child to the current tree. The Child can contain children of its own.
59        But you cannot a state tree that already has an active state.
60    @param state
61        The state to be added.
62    */
63    void GameState::addChild(GameState* state)
64    {
65        if (!state)
66            return;
67        // check if the state/tree to be added has states in it that already exist in this tree.
68        for (std::map<std::string, GameState*>::const_iterator it = state->allChildren_.begin();
69            it != state->allChildren_.end(); ++it)
70        {
71            if (this->checkState(it->second->getName()))
72            {
73                ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
74                return;
75            }
76        }
77        if (this->checkState(state->name_))
78        {
79            ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
80            return;
81        }
82        // Make sure we don't add a tree that already has an active state.
83        if (state->getCurrentState())
84        {
85            ThrowException(GameState, "Cannot merge a tree that is already active.");
86            return;
87        }
88
89        // merge the child's children into this tree
90        for (std::map<std::string, GameState*>::const_iterator it = state->allChildren_.begin();
91            it != state->allChildren_.end(); ++it)
92        {
93            this->allChildren_[it->second->getName()] = it->second;
94            this->grandchildrenToChildren_[it->second] = state;
95            if (this->parent_)
96                this->parent_->grandchildAdded(this, it->second);
97        }
98        // merge 'state' into this tree
99        this->allChildren_[state->name_] = state;
100        this->grandchildrenToChildren_[state] = state;
101        if (this->parent_)
102            this->parent_->grandchildAdded(this, state);
103
104        // mark us as parent
105        state->parent_ = this;
106    }
107
108    /**
109    @brief
110        Tells a state that one of its children has added a child. This is necessary
111        to fill the internal maps correctly.
112    @param child
113        The child who notices this state.
114    @param grandchild
115        The child that has been added.
116    */
117    void GameState::grandchildAdded(GameState* child, GameState* grandchild)
118    {
119        // fill the two maps correctly.
120        this->allChildren_[grandchild->getName()] = grandchild;
121        this->grandchildrenToChildren_[grandchild] = child;
122        if (this->parent_)
123            this->parent_->grandchildAdded(this, grandchild);
124    }
125
126    /**
127    @brief
128        Checks whether a specific game states exists in the hierarchy.
129    @remarks
130        Remember that the every node has a map with all its child nodes.
131    */
132    GameState* GameState::checkState(const std::string& name)
133    {
134        if (this->parent_)
135            return this->parent_->checkState(name);
136        else
137        {
138            // The map only contains children, so check ourself first
139            if (name == this->name_)
140                return this;
141            // Search in the map. If there is no entry, we can be sure the state doesn't exist.
142            std::map<std::string, GameState*>::const_iterator it = this->allChildren_.find(name);
143            return (it!= this->allChildren_.end() ? it->second : 0);
144        }
145    }
146
147    /**
148    @brief
149        Returns the current active state.
150    @remarks
151        Remember that the current active state is the one that does not
152        have active children itself. Many states can be active at once.
153    */
154    GameState* GameState::getCurrentState()
155    {
156        if (this->bActive_)
157        {
158            if (this->activeChild_)
159                return this->activeChild_->getCurrentState();
160            else
161                return this;
162        }
163        else
164        {
165            if (this->parent_)
166                return this->parent_->getCurrentState();
167            else
168                return 0;
169        }
170    }
171
172    /**
173    @brief
174        Returns the root node of the tree.
175    */
176    GameState* GameState::getRootNode()
177    {
178        if (this->parent_)
179            return this->parent_->getRootNode();
180        else
181            return this;
182    }
183
184    /**
185    @brief
186        Makes a state transition according to the state tree. You can choose any state
187        in the tree to do the call. The function finds the current state on its own.
188    @param state
189        The state to be entered, has to exist in the tree.
190    */
191    void GameState::requestState(const std::string& name)
192    {
193        GameState* request = checkState(name);
194        GameState* current = getCurrentState();
195        if (request)
196        {
197            if (current)
198            {
199                // There is already an active state
200                current->makeTransition(request);
201            }
202            else
203            {
204                // no active state --> we have to activate the root node first.
205                GameState* root = getRootNode();
206                root->activate();
207                root->makeTransition(request);
208            }
209        }
210        else
211        {
212            COUT(2) << "Warning: GameState '" << name << "' doesn't exist." << std::endl;
213        }
214    }
215
216    /**
217    @brief
218        Internal method that actually makes the state transition. Since it is internal,
219        the method can assume certain things to be granted (like 'this' is always active).
220    */
221    void GameState::makeTransition(GameState* state)
222    {
223        // we're always already active
224        assert(this->bActive_);
225
226        if (state == this)
227            return;
228
229        // Check for 'state' in the children map first
230        std::map<GameState*, GameState*>::const_iterator it = this->grandchildrenToChildren_.find(state);
231        if (it != this->grandchildrenToChildren_.end())
232        {
233            // child state. Don't use 'state', might be a grandchild!
234            it->second->activate();
235            it->second->makeTransition(state);
236        }
237        else
238        {
239            // parent. We can be sure of this.
240            assert(this->parent_ != 0);
241
242            // first, leave this state.
243            this->deactivate();
244            this->parent_->makeTransition(state);
245        }
246    }
247
248    /**
249    @brief
250        Activates the state. Only sets bActive_ to true and notifies the parent.
251    */
252    void GameState::activate()
253    {
254        this->bActive_ = true;
255        if (this->parent_)
256            this->parent_->activeChild_ = this;
257        this->enter();
258    }
259
260    /**
261        Activates the state. Only sets bActive_ to false and notifies the parent.
262    */
263    void GameState::deactivate()
264    {
265        this->leave();
266        this->bActive_ = false;
267        if (this->parent_)
268            this->parent_->activeChild_ = 0;
269    }
270
271}
Note: See TracBrowser for help on using the repository browser.