Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Modified the GameState hierarchy so that you can get the parent with the actual type by calling getParent().

  • Property svn:eol-style set to native
File size: 11.2 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 "Debug.h"
37#include "Exception.h"
38
39namespace orxonox
40{
41    /**
42    @brief
43        Constructor only initialises variables and sets the name permanently.
44    */
45    GameState::GameState(const std::string& name)
46        : name_(name)
47        //, parent_(0)
48        , activeChild_(0)
49        //, bPausegetParent()(false)
50    {
51        Operations temp = {false, false, false, false, false};
52        this->operation_ = temp;
53    }
54
55    /**
56    @brief
57        Destructor only checks that we don't delete an active state.
58    */
59    GameState::~GameState()
60    {
61        OrxAssert(!isInSubtree(getCurrentState()), "Deleting an active GameState is a very bad idea..");
62    }
63
64    /**
65    @brief
66        Adds a child to the current tree. The Child can contain children of its own.
67        But you cannot a state tree that already has an active state.
68    @param state
69        The state to be added.
70    */
71    void GameState::addChild(GameState* state)
72    {
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)
78        {
79            if (this->getState(it->second->getName()))
80            {
81                ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
82                return;
83            }
84        }
85        if (this->getState(state->name_))
86        {
87            ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
88            return;
89        }
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);
106    }
107
108    /**
109    @brief
110        Removes a child by instance. This splits the tree in two parts,
111        each of them functional on its own.
112    @param state
113        GameState by instance pointer
114    */
115    void GameState::removeChild(GameState* state)
116    {
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        }
135        else
136        {
137            ThrowException(GameState, "Game state '" + name_ + "' doesn't have a child named '"
138                + state->getName() + "'.");
139            //COUT(2) << "Warning: Game state '" << name_ << "' doesn't have a child named '"
140            //    << state->getName() << "'. Removal skipped." << std::endl;
141        }
142    }
143
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)
153    {
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        }
164    }
165
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)
176    {
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);
182    }
183
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)
194    {
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;
365    }
366}
Note: See TracBrowser for help on using the repository browser.