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