]> git.localhorst.tv Git - l2e.git/blob - src/app/Application.cpp
better handling of nested state changes
[l2e.git] / src / app / Application.cpp
1 /*
2  * Application.cpp
3  *
4  *  Created on: Apr 8, 2012
5  *      Author: holy
6  */
7
8 #include "Application.h"
9
10 #include "State.h"
11
12 #include <cassert>
13
14 namespace app {
15
16 Application::Application(sdl::InitScreen *screen, State *initialState)
17 : screen(screen)
18 , states()
19 , last(SDL_GetTicks())
20 , inStateChage(false) {
21         assert(screen && "cannot create application without screen");
22         assert(initialState && "cannot create application without initial state");
23         RealPushState(initialState);
24 }
25
26 Application::~Application() {
27         PopAllStates();
28 }
29
30
31 State *Application::CurrentState() {
32         return states.empty() ? 0 : states.top();
33 }
34
35 void Application::UpdateState() {
36         inStateChage = true;
37         while (!stateChanges.empty()) {
38                 switch (stateChanges.front().type) {
39                         case StateCommand::PUSH:
40                                 RealPushState(stateChanges.front().state);
41                                 break;
42                         case StateCommand::POP:
43                                 RealPopState();
44                                 break;
45                         case StateCommand::CHANGE:
46                                 RealChangeState(stateChanges.front().state);
47                                 break;
48                 }
49                 stateChanges.pop();
50         }
51         inStateChage = false;
52 }
53
54 void Application::ChangeState(State *s) {
55         if (inStateChage) {
56                 RealChangeState(s);
57         } else {
58                 StateCommand cmd;
59                 cmd.type = StateCommand::CHANGE;
60                 cmd.state = s;
61                 stateChanges.push(cmd);
62         }
63 }
64
65 void Application::PushState(State *s) {
66         if (inStateChage) {
67                 RealPushState(s);
68         } else {
69                 StateCommand cmd;
70                 cmd.type = StateCommand::PUSH;
71                 cmd.state = s;
72                 stateChanges.push(cmd);
73         }
74 }
75
76 void Application::PopState() {
77         if (inStateChage) {
78                 RealPopState();
79         } else {
80                 StateCommand cmd;
81                 cmd.type = StateCommand::POP;
82                 cmd.state = 0;
83                 stateChanges.push(cmd);
84         }
85 }
86
87 void Application::RealChangeState(State *s) {
88         if (!states.empty()) {
89                 states.top()->PauseState(*this, screen->Screen());
90                 states.top()->ExitState(*this, screen->Screen());
91                 states.pop();
92         }
93         states.push(s);
94         s->EnterState(*this, screen->Screen());
95         s->ResumeState(*this, screen->Screen());
96 }
97
98 void Application::RealPushState(State *s) {
99         if (!states.empty()) {
100                 states.top()->PauseState(*this, screen->Screen());
101         }
102         states.push(s);
103         s->EnterState(*this, screen->Screen());
104         s->ResumeState(*this, screen->Screen());
105 }
106
107 void Application::RealPopState() {
108         if (states.empty()) return;
109         states.top()->PauseState(*this, screen->Screen());
110         states.top()->ExitState(*this, screen->Screen());
111         delete states.top();
112         states.pop();
113         if (!states.empty()) {
114                 states.top()->ResumeState(*this, screen->Screen());
115         }
116 }
117
118 void Application::Quit() {
119         PopAllStates();
120 }
121
122 void Application::PopAllStates() {
123         while (!states.empty()) {
124                 states.top()->PauseState(*this, screen->Screen());
125                 states.top()->ExitState(*this, screen->Screen());
126                 delete states.top();
127                 states.pop();
128         }
129 }
130
131
132 void Application::Run() {
133         while (CurrentState()) {
134                 Loop();
135         }
136 }
137
138 void Application::Loop() {
139         Uint32 now(SDL_GetTicks());
140         Uint32 deltaT(now - last);
141         GlobalTimers().Update(deltaT);
142         if (deltaT > 30) deltaT = 30;
143
144         if (CurrentState()) {
145                 CurrentState()->GraphicsTimers().Update(deltaT);
146         }
147         HandleEvents();
148         if (!StateChangePending()) {
149                 UpdateWorld(deltaT);
150                 Render();
151         }
152
153         last = now;
154         UpdateState();
155 }
156
157
158 void Application::HandleEvents() {
159         if (!CurrentState()) return;
160         input.ResetInteractiveState();
161         SDL_Event event;
162         while (SDL_PollEvent(&event)) {
163                 switch (event.type) {
164                         case SDL_QUIT:
165                                 PopAllStates();
166                                 break;
167                         case SDL_VIDEORESIZE:
168                                 screen->Resize(event.resize.w, event.resize.h);
169                                 CurrentState()->Resize(event.resize.w, event.resize.h);
170                                 break;
171                         case SDL_KEYDOWN:
172                         case SDL_KEYUP:
173                                 input.HandleKeyboardEvent(event.key);
174                                 break;
175                         default:
176                                 // skip event
177                                 break;
178                 }
179         }
180         if (CurrentState()) CurrentState()->HandleEvents(input);
181 }
182
183 void Application::UpdateWorld(Uint32 deltaT) {
184         if (!CurrentState()) return;
185         for (Uint32 i(0); i < deltaT && !StateChangePending(); ++i) {
186                 CurrentState()->PhysicsTimers().Update(0.001f);
187                 CurrentState()->UpdateWorld(0.001f);
188         }
189 }
190
191 void Application::Render(void) {
192         if (!CurrentState()) return;
193         CurrentState()->Render(screen->Screen());
194         screen->Flip();
195 }
196
197 }