Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/GameState.cc @ 2403

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

Moved Exception to util now that debug has been moved.

  • Property svn:eol-style set to native
File size: 11.4 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 GameStateBase class.
33*/
34
35#include "GameState.h"
36#include "util/Debug.h"
37#include "util/Exception.h"
38
39namespace orxonox
40{
41    /**
42    @brief
43        Constructor only initialises variables and sets the name permanently.
44    */
45    GameStateBase::GameStateBase(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    GameStateBase::~GameStateBase()
60    {
61        OrxAssert(this->operation_.active == false, "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 GameStateBase::addChild(GameStateBase* 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, GameStateBase*>::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, GameStateBase*>::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 GameStateBase::removeChild(GameStateBase* state)
116    {
117        std::map<GameStateBase*, GameStateBase*>::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<GameStateBase*, GameStateBase*>::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 GameStateBase::removeChild(const std::string& name)
153    {
154        GameStateBase* 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 GameStateBase::grandchildAdded(GameStateBase* child, GameStateBase* 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 GameStateBase::grandchildRemoved(GameStateBase* 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    GameStateBase* GameStateBase::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, GameStateBase*>::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    GameStateBase* GameStateBase::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    GameStateBase* GameStateBase::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 GameStateBase::isInSubtree(GameStateBase* 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 GameStateBase::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 GameStateBase::makeTransition(GameStateBase* source, GameStateBase* 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<GameStateBase*, GameStateBase*>::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 GameStateBase::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 GameStateBase::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 GameStateBase::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.