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