Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

started implementing the GameStates. Not much for now, but most of the Orxonox.cc code has been copied to GSRoot, GSGraphics and GSLevel.
There is no level currently, but the main menu is shown. This is more of an svn save because I would really like to have Member ConsoleCommands.

  • Property svn:eol-style set to native
File size: 11.5 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        Destructor only checks that we don't delete an active state.
59    */
60    GameState::~GameState()
61    {
62        if (this->bActive_)
63        {
64            if (this->parent_)
65                this->requestState(this->parent_->getName());
66            else
67                this->requestState("");
68        }
69    }
70
71    /**
72    @brief
73        Adds a child to the current tree. The Child can contain children of its own.
74        But you cannot a state tree that already has an active state.
75    @param state
76        The state to be added.
77    */
78    void GameState::addChild(GameState* state)
79    {
80        if (!state)
81            return;
82        // check if the state/tree to be added has states in it that already exist in this tree.
83        for (std::map<std::string, GameState*>::const_iterator it = state->allChildren_.begin();
84            it != state->allChildren_.end(); ++it)
85        {
86            if (this->checkState(it->second->getName()))
87            {
88                ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
89                return;
90            }
91        }
92        if (this->checkState(state->name_))
93        {
94            ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
95            return;
96        }
97        // Make sure we don't add a tree that already has an active state.
98        if (state->getCurrentState())
99        {
100            ThrowException(GameState, "Cannot merge a tree that is already active.");
101            return;
102        }
103
104        // merge the child's children into this tree
105        for (std::map<std::string, GameState*>::const_iterator it = state->allChildren_.begin();
106            it != state->allChildren_.end(); ++it)
107        {
108            this->allChildren_[it->second->getName()] = it->second;
109            this->grandchildrenToChildren_[it->second] = state;
110            if (this->parent_)
111                this->parent_->grandchildAdded(this, it->second);
112        }
113        // merge 'state' into this tree
114        this->allChildren_[state->name_] = state;
115        this->grandchildrenToChildren_[state] = state;
116        if (this->parent_)
117            this->parent_->grandchildAdded(this, state);
118
119        // mark us as parent
120        state->parent_ = this;
121    }
122
123    /**
124    @brief
125        Removes a child by instance. This splits the tree in two parts,
126        each of them functional on its own.
127    @param state
128        GameState by instance pointer
129    */
130    void GameState::removeChild(GameState* state)
131    {
132        std::map<GameState*, GameState*>::iterator it = this->grandchildrenToChildren_.find(state);
133        if (it != this->grandchildrenToChildren_.end())
134        {
135            if (state->isActive())
136            {
137                ThrowException(GameState, "Cannot remove active game state child '"
138                    + state->getName() + "' from '" + name_ + "'.");
139                //COUT(2) << "Warning: Cannot remove active game state child '" << state->getName()
140                //    << "' from '" << name_ << "'." << std::endl;
141            }
142            else
143            {
144                for (std::map<GameState*, GameState*>::const_iterator it = state->grandchildrenToChildren_.begin();
145                    it != state->grandchildrenToChildren_.end(); ++it)
146                {
147                    this->grandchildRemoved(it->first);
148                }
149                this->grandchildRemoved(state);
150            }
151        }
152        else
153        {
154            ThrowException(GameState, "Game state '" + name_ + "' doesn't have a child named '"
155                + state->getName() + "'. Removal skipped.");
156            //COUT(2) << "Warning: Game state '" << name_ << "' doesn't have a child named '"
157            //    << state->getName() << "'. Removal skipped." << std::endl;
158        }
159    }
160
161    /**
162    @brief
163        Removes a child by name. This splits the tree in two parts,
164        each of them functional on its own.
165    @param state
166        GameState by name
167    */
168
169    void GameState::removeChild(const std::string& name)
170    {
171        GameState* state = checkState(name);
172        if (state)
173        {
174            removeChild(state);
175        }
176        else
177        {
178            ThrowException(GameState, "GameState '" + name + "' doesn't exist.");
179            //COUT(2) << "Warning: GameState '" << name << "' doesn't exist." << std::endl;
180        }
181    }
182
183    /**
184    @brief
185        Tells a state that one of its children has added a child. This is necessary
186        to fill the internal maps correctly.
187    @param child
188        The child who notices this state.
189    @param grandchild
190        The child that has been added.
191    */
192    void GameState::grandchildAdded(GameState* child, GameState* grandchild)
193    {
194        // fill the two maps correctly.
195        this->allChildren_[grandchild->getName()] = grandchild;
196        this->grandchildrenToChildren_[grandchild] = child;
197        if (this->parent_)
198            this->parent_->grandchildAdded(this, grandchild);
199    }
200
201    /**
202    @brief
203        Tells a state that one of its children has removed a child. This is necessary
204        to fill the internal maps correctly.
205    @param child
206        The child who notices this state.
207    @param grandchild
208        The child that has been removed.
209    */
210    void GameState::grandchildRemoved(GameState* grandchild)
211    {
212        // adjust the two maps correctly.
213        this->allChildren_.erase(grandchild->getName());
214        this->grandchildrenToChildren_.erase(grandchild);
215        if (this->parent_)
216            this->parent_->grandchildRemoved(grandchild);
217    }
218
219    /**
220    @brief
221        Checks whether a specific game states exists in the hierarchy.
222    @remarks
223        Remember that the every node has a map with all its child nodes.
224    */
225    GameState* GameState::checkState(const std::string& name)
226    {
227        if (this->parent_)
228            return this->parent_->checkState(name);
229        else
230        {
231            // The map only contains children, so check ourself first
232            if (name == this->name_)
233                return this;
234            // Search in the map. If there is no entry, we can be sure the state doesn't exist.
235            std::map<std::string, GameState*>::const_iterator it = this->allChildren_.find(name);
236            return (it!= this->allChildren_.end() ? it->second : 0);
237        }
238    }
239
240    /**
241    @brief
242        Returns the current active state.
243    @remarks
244        Remember that the current active state is the one that does not
245        have active children itself. Many states can be active at once.
246    */
247    GameState* GameState::getCurrentState()
248    {
249        if (this->bActive_)
250        {
251            if (this->activeChild_)
252                return this->activeChild_->getCurrentState();
253            else
254                return this;
255        }
256        else
257        {
258            if (this->parent_)
259                return this->parent_->getCurrentState();
260            else
261                return 0;
262        }
263    }
264
265    /**
266    @brief
267        Returns the root node of the tree.
268    */
269    GameState* GameState::getRootNode()
270    {
271        if (this->parent_)
272            return this->parent_->getRootNode();
273        else
274            return this;
275    }
276
277    /**
278    @brief
279        Makes a state transition according to the state tree. You can choose any state
280        in the tree to do the call. The function finds the current state on its own.
281    @param state
282        The state to be entered, has to exist in the tree.
283    */
284    void GameState::requestState(const std::string& name)
285    {
286        if (name == "")
287        {
288            // user would like to leave every state.
289            GameState* current = getCurrentState();
290            if (current)
291            {
292                // Deactivate all states but root
293                GameState* root = getRootNode();
294                current->makeTransition(root);
295                // Kick root too
296                root->deactivate();
297            }
298        }
299        else
300        {
301            GameState* request = checkState(name);
302            GameState* current = getCurrentState();
303            if (request)
304            {
305                if (current)
306                {
307                    // There is already an active state
308                    current->makeTransition(request);
309                }
310                else
311                {
312                    // no active state --> we have to activate the root node first.
313                    GameState* root = getRootNode();
314                    root->activate();
315                    root->makeTransition(request);
316                }
317            }
318            else
319            {
320                COUT(2) << "Warning: GameState '" << name << "' doesn't exist." << std::endl;
321            }
322        }
323    }
324
325    /**
326    @brief
327        Internal method that actually makes the state transition. Since it is internal,
328        the method can assume certain things to be granted (like 'this' is always active).
329    */
330    void GameState::makeTransition(GameState* state)
331    {
332        // we're always already active
333        assert(this->bActive_);
334
335        if (state == this)
336            return;
337
338        // Check for 'state' in the children map first
339        std::map<GameState*, GameState*>::const_iterator it = this->grandchildrenToChildren_.find(state);
340        if (it != this->grandchildrenToChildren_.end())
341        {
342            // child state. Don't use 'state', might be a grandchild!
343            it->second->activate();
344            it->second->makeTransition(state);
345        }
346        else
347        {
348            // parent. We can be sure of this.
349            assert(this->parent_ != 0);
350
351            // first, leave this state.
352            this->deactivate();
353            this->parent_->makeTransition(state);
354        }
355    }
356
357    /**
358    @brief
359        Activates the state. Only sets bActive_ to true and notifies the parent.
360    */
361    void GameState::activate()
362    {
363        this->bActive_ = true;
364        if (this->parent_)
365            this->parent_->activeChild_ = this;
366        this->enter();
367    }
368
369    /**
370        Activates the state. Only sets bActive_ to false and notifies the parent.
371    */
372    void GameState::deactivate()
373    {
374        this->leave();
375        this->bActive_ = false;
376        if (this->parent_)
377            this->parent_->activeChild_ = 0;
378    }
379
380}
Note: See TracBrowser for help on using the repository browser.