Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2324 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
RevLine 
[1660]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
[1689]32    Implementation of GameStateBase class.
[1660]33*/
34
35#include "GameState.h"
[1755]36#include "util/Debug.h"
[1764]37#include "util/Exception.h"
[1660]38
39namespace orxonox
40{
41    /**
42    @brief
43        Constructor only initialises variables and sets the name permanently.
44    */
[1689]45    GameStateBase::GameStateBase(const std::string& name)
[1660]46        : name_(name)
[1688]47        //, parent_(0)
[1660]48        , activeChild_(0)
[1688]49        //, bPausegetParent()(false)
[1660]50    {
[1670]51        Operations temp = {false, false, false, false, false};
52        this->operation_ = temp;
[1660]53    }
54
55    /**
56    @brief
[1661]57        Destructor only checks that we don't delete an active state.
58    */
[1689]59    GameStateBase::~GameStateBase()
[1661]60    {
[1690]61        OrxAssert(this->operation_.active == false, "Deleting an active GameState is a very bad idea..");
[1661]62    }
63
64    /**
65    @brief
[1660]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    */
[1689]71    void GameStateBase::addChild(GameStateBase* state)
[1660]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.
[1689]76        for (std::map<std::string, GameStateBase*>::const_iterator it = state->allChildren_.begin();
[1660]77            it != state->allChildren_.end(); ++it)
78        {
[1672]79            if (this->getState(it->second->getName()))
[1660]80            {
81                ThrowException(GameState, "Cannot add a GameState to the hierarchy twice.");
82                return;
83            }
84        }
[1672]85        if (this->getState(state->name_))
[1660]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
[1689]98        for (std::map<std::string, GameStateBase*>::const_iterator it = state->allChildren_.begin();
[1660]99            it != state->allChildren_.end(); ++it)
[1672]100            this->grandchildAdded(state, it->second);
[1660]101        // merge 'state' into this tree
[1672]102        this->grandchildAdded(state, state);
[1660]103
104        // mark us as parent
[1688]105        state->setParent(this);
[1660]106    }
107
108    /**
109    @brief
[1661]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    */
[1689]115    void GameStateBase::removeChild(GameStateBase* state)
[1661]116    {
[1689]117        std::map<GameStateBase*, GameStateBase*>::iterator it = this->grandchildrenToChildren_.find(state);
[1661]118        if (it != this->grandchildrenToChildren_.end())
119        {
[1672]120            if (state->isInSubtree(getCurrentState()))
[1661]121            {
[1672]122                ThrowException(GameState, "Cannot remove an active game state child '"
[1661]123                    + state->getName() + "' from '" + name_ + "'.");
[1672]124                //COUT(2) << "Warning: Cannot remove an active game state child '" << state->getName()
[1661]125                //    << "' from '" << name_ << "'." << std::endl;
126            }
127            else
128            {
[1689]129                for (std::map<GameStateBase*, GameStateBase*>::const_iterator it = state->grandchildrenToChildren_.begin();
[1661]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 '"
[1672]138                + state->getName() + "'.");
[1661]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
[1689]152    void GameStateBase::removeChild(const std::string& name)
[1661]153    {
[1689]154        GameStateBase* state = getState(name);
[1661]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
[1660]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    */
[1689]175    inline void GameStateBase::grandchildAdded(GameStateBase* child, GameStateBase* grandchild)
[1660]176    {
177        // fill the two maps correctly.
178        this->allChildren_[grandchild->getName()] = grandchild;
179        this->grandchildrenToChildren_[grandchild] = child;
[1688]180        if (this->getParent())
181            this->getParent()->grandchildAdded(this, grandchild);
[1660]182    }
183
184    /**
185    @brief
[1661]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    */
[1689]193    inline void GameStateBase::grandchildRemoved(GameStateBase* grandchild)
[1661]194    {
195        // adjust the two maps correctly.
196        this->allChildren_.erase(grandchild->getName());
197        this->grandchildrenToChildren_.erase(grandchild);
[1688]198        if (this->getParent())
199            this->getParent()->grandchildRemoved(grandchild);
[1661]200    }
201
202    /**
203    @brief
[1660]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    */
[1689]208    GameStateBase* GameStateBase::getState(const std::string& name)
[1660]209    {
[1688]210        if (this->getParent())
211            return this->getParent()->getState(name);
[1660]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.
[1689]218            std::map<std::string, GameStateBase*>::const_iterator it = this->allChildren_.find(name);
[1660]219            return (it!= this->allChildren_.end() ? it->second : 0);
220        }
221    }
222
223    /**
224    @brief
[1672]225        Returns the root node of the tree.
226    */
[1689]227    GameStateBase* GameStateBase::getRoot()
[1672]228    {
[1688]229        if (this->getParent())
230            return this->getParent()->getRoot();
[1672]231        else
232            return this;
233    }
234
235    /**
236    @brief
[1660]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    */
[1689]242    GameStateBase* GameStateBase::getCurrentState()
[1660]243    {
[1670]244        if (this->operation_.active)
[1660]245        {
246            if (this->activeChild_)
247                return this->activeChild_->getCurrentState();
248            else
249                return this;
250        }
251        else
252        {
[1672]253            if (this->getParent())
254                return this->getParent()->getCurrentState();
[1660]255            else
256                return 0;
257        }
258    }
259
260    /**
261    @brief
[1672]262        Determines whether 'state' is in this subtree, including this node.
[1660]263    */
[1689]264    bool GameStateBase::isInSubtree(GameStateBase* state) const
[1660]265    {
[1672]266        return (grandchildrenToChildren_.find(state) != grandchildrenToChildren_.end()
267                || state == this);
[1660]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    */
[1689]277    void GameStateBase::requestState(const std::string& name)
[1660]278    {
[1672]279        assert(getRoot());
280        getRoot()->requestState(name);
[1660]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    */
[1689]288    void GameStateBase::makeTransition(GameStateBase* source, GameStateBase* destination)
[1660]289    {
[1672]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        }
[1660]305
[1672]306        if (destination == this)
[1660]307            return;
308
[1672]309        // Check for 'destination' in the children map first
[1689]310        std::map<GameStateBase*, GameStateBase*>::const_iterator it
[1672]311            = this->grandchildrenToChildren_.find(destination);
[1660]312        if (it != this->grandchildrenToChildren_.end())
313        {
314            // child state. Don't use 'state', might be a grandchild!
[1672]315            this->activeChild_ = it->second;
316            it->second->makeTransition(this, destination);
[1660]317        }
318        else
319        {
320            // parent. We can be sure of this.
[1672]321            assert(this->getParent() != 0);
[1660]322
[1672]323            this->deactivate();
324            this->getParent()->makeTransition(this, destination);
[1660]325        }
326    }
327
328    /**
329    @brief
330        Activates the state. Only sets bActive_ to true and notifies the parent.
331    */
[1689]332    void GameStateBase::activate()
[1660]333    {
[1670]334        this->operation_.active = true;
335        this->operation_.entering = true;
[1660]336        this->enter();
[1670]337        this->operation_.entering = false;
[1660]338    }
339
340    /**
341        Activates the state. Only sets bActive_ to false and notifies the parent.
342    */
[1689]343    void GameStateBase::deactivate()
[1660]344    {
[1670]345        this->operation_.leaving = true;
[1660]346        this->leave();
[1670]347        this->operation_.leaving = false;
348        this->operation_.active = false;
[1660]349    }
350
[1670]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    */
[1689]360    void GameStateBase::tick(const Clock& time)
[1670]361    {
362        this->operation_.running = true;
[1674]363        this->ticked(time);
[1670]364        this->operation_.running = false;
365    }
[1660]366}
Note: See TracBrowser for help on using the repository browser.