Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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