Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Still working on the GameStates, but I have to save the work because of some major changes.

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