1 /*
2  * Copyright (C) 2004 Ivo Danihelka (ivo@danihelka.net)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 #include "StateManager.h"
10 
11 #include "GameState.h"
12 
13 #include "MessagerAgent.h"
14 #include "SimpleMsg.h"
15 #include "LogicException.h"
16 
17 //-----------------------------------------------------------------
~StateManager()18 StateManager::~StateManager()
19 {
20     emptyTrash();
21 
22     t_states::iterator end = m_states.end();
23     for (t_states::iterator i = m_states.begin(); i != end; ++i) {
24         delete (*i);
25     }
26 }
27 //-----------------------------------------------------------------
28     void
emptyTrash()29 StateManager::emptyTrash()
30 {
31     t_states::iterator end = m_trash.end();
32     for (t_states::iterator i = m_trash.begin(); i != end; ++i) {
33         delete (*i);
34     }
35     m_trash.clear();
36 }
37 //-----------------------------------------------------------------
38 /**
39  * Update current state and states on background
40  * and empty trash.
41  * The states at bottom will be updated as first.
42  */
43 void
updateGame()44 StateManager::updateGame()
45 {
46     t_states::iterator end = m_states.end();
47     for (t_states::iterator i = m_states.begin(); i != end; /* empty */) {
48         //NOTE: state can remove self and thus invalide current iterator
49         GameState *cur = *(i++);
50         if (cur->isRunning()) {
51             cur->updateState();
52         }
53     }
54 
55     emptyTrash();
56 }
57 
58 //-----------------------------------------------------------------
59 /**
60  * Throw given state to the trash.
61  * NOTE: given state can be still active, cannot be deleted
62  */
63 void
removeState(GameState * state)64 StateManager::removeState(GameState *state)
65 {
66     state->cleanState();
67     m_trash.push_back(state);
68     m_states.remove(state);
69 }
70 //-----------------------------------------------------------------
71 /**
72  * Remove given state and set this one.
73  */
74 void
changeState(GameState * who,GameState * new_state)75 StateManager::changeState(GameState *who, GameState *new_state)
76 {
77     insertAfter(who, new_state);
78     removeState(who);
79     new_state->initState(this);
80     checkStack();
81 }
82 //-----------------------------------------------------------------
83 /**
84  * Pause given state and activate this one.
85  */
86 void
pushState(GameState * who,GameState * new_state)87 StateManager::pushState(GameState *who, GameState *new_state)
88 {
89     insertAfter(who, new_state);
90     new_state->initState(this);
91     checkStack();
92 }
93 
94 //-----------------------------------------------------------------
95 /**
96  * Remove given state and resume paused states below it.
97  */
98 void
popState(GameState * who)99 StateManager::popState(GameState *who)
100 {
101     removeState(who);
102 
103     if (!m_states.empty()) {
104         checkStack();
105     }
106     else {
107         MessagerAgent::agent()->forwardNewMsg(
108                 new SimpleMsg(Name::APP_NAME, "quit"));
109     }
110 }
111 
112 //-----------------------------------------------------------------
113 /**
114  * Insert new state after given state.
115  * @param who active state or NULL to insert at the beginning
116  * @param new_state state to insert
117  */
118 void
insertAfter(GameState * who,GameState * new_state)119 StateManager::insertAfter(GameState *who, GameState *new_state)
120 {
121     if (NULL == who) {
122         m_states.push_front(new_state);
123     }
124     else {
125         t_states::iterator it = findIter(who);
126         m_states.insert(++it, new_state);
127     }
128 }
129 //-----------------------------------------------------------------
130 /**
131  * Find iterator under given state.
132  * @throws LogicException when state is not found
133  */
134     StateManager::t_states::iterator
findIter(GameState * who)135 StateManager::findIter(GameState *who)
136 {
137     t_states::iterator end = m_states.end();
138     for (t_states::iterator i = m_states.begin(); i != end; ++i) {
139         if (who == (*i)) {
140             return i;
141         }
142     }
143     throw LogicException(ExInfo("game state is not found in stack")
144             .addInfo("state", who ? who->getName() : "(null)"));
145 }
146 //-----------------------------------------------------------------
147 /**
148  * Preserve stack consistency.
149  * - Node at top must be running.
150  * - Only running node with allowBg have running states below.
151  * @throws LogicException stack is empty
152  */
153 void
checkStack()154 StateManager::checkStack()
155 {
156     if (m_states.empty()) {
157         throw LogicException(ExInfo("game state stack is empty"));
158     }
159 
160     t_states::iterator topIt = m_states.end();
161     --topIt;
162     GameState *top = (*topIt);
163     if (top->isOnBg()) {
164         top->noteFg();
165     }
166     pauseBg(topIt);
167     resumeBg(topIt);
168     installHandlers();
169 }
170 //-----------------------------------------------------------------
171 /**
172  * Pause all running states below on stack which are not allowed.
173  * The toppers will be paused first but the order should be insignificant
174  * @param stateIt states bellow will be check
175  */
176 void
pauseBg(t_states::iterator stateIt)177 StateManager::pauseBg(t_states::iterator stateIt)
178 {
179     if (stateIt != m_states.begin()) {
180         t_states::iterator prev = stateIt;
181         --prev;
182         if (!(*stateIt)->isRunning() || !(*stateIt)->allowBg()) {
183             if ((*prev)->isRunning()) {
184                 (*prev)->pauseState();
185             }
186         }
187         pauseBg(prev);
188     }
189 }
190 //-----------------------------------------------------------------
191 /**
192  * Recursively resume the given state and all states below on stack
193  * which are allowed.
194  * The lower ones will be resumed first but the order should be insignificant.
195  * @param stateIt state to run
196  */
197 void
resumeBg(t_states::iterator stateIt)198 StateManager::resumeBg(t_states::iterator stateIt)
199 {
200     if ((*stateIt)->allowBg() && stateIt != m_states.begin()) {
201         t_states::iterator prev = stateIt;
202         --prev;
203         resumeBg(prev);
204         (*prev)->noteBg();
205     }
206 
207     if (!(*stateIt)->isRunning()) {
208         (*stateIt)->resumeState();
209     }
210 }
211 //-----------------------------------------------------------------
212 /**
213  * Let all running states to install input and draw handler.
214  * And all paused states will uninstall their handlers.
215  * The lowers will be called first, the order is significant.
216  */
217 void
installHandlers()218 StateManager::installHandlers()
219 {
220     t_states::iterator end = m_states.end();
221     for (t_states::iterator i = m_states.begin(); i != end; ++i) {
222         if ((*i)->isRunning()) {
223             (*i)->unHandlers();
224             (*i)->installHandlers();
225         }
226         else {
227             (*i)->unHandlers();
228         }
229     }
230 }
231 
232