fix a few warnings
[supertux.git] / src / mainloop.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 #include <config.h>
20
21 #include "mainloop.hpp"
22
23 #include <stdlib.h>
24 #include <SDL.h>
25 #include "video/drawing_context.hpp"
26 #include "control/joystickkeyboardcontroller.hpp"
27 #include "gui/menu.hpp"
28 #include "audio/sound_manager.hpp"
29 #include "scripting/time_scheduler.hpp"
30 #include "scripting/squirrel_util.hpp"
31 #include "gameconfig.hpp"
32 #include "main.hpp"
33 #include "resources.hpp"
34 #include "screen.hpp"
35 #include "screen_fade.hpp"
36 #include "timer.hpp"
37 #include "player_status.hpp"
38 #include "random_generator.hpp"
39
40 // the engine will be run with a logical framerate of 64fps.
41 // We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
42 // binary fraction...
43 static const float LOGICAL_FPS = 64.0;
44 /** ticks (as returned from SDL_GetTicks) per frame */
45 static const Uint32 TICKS_PER_FRAME = (Uint32) (1000.0 / LOGICAL_FPS);
46 /** don't skip more than every 2nd frame */
47 static const int MAX_FRAME_SKIP = 2;
48
49 float game_speed = 1.0f;
50
51 MainLoop* main_loop = NULL;
52
53 MainLoop::MainLoop()
54   : speed(1.0), nextpop(false), nextpush(false), fps(0), screenshot_requested(false)
55 {
56   using namespace Scripting;
57   TimeScheduler::instance = new TimeScheduler();
58 }
59
60 MainLoop::~MainLoop()
61 {
62   using namespace Scripting;
63   delete TimeScheduler::instance;
64   TimeScheduler::instance = NULL;
65
66   for(std::vector<Screen*>::iterator i = screen_stack.begin();
67       i != screen_stack.end(); ++i) {
68     delete *i;
69   }
70 }
71
72 void
73 MainLoop::push_screen(Screen* screen, ScreenFade* screen_fade)
74 {
75   this->next_screen.reset(screen);
76   this->screen_fade.reset(screen_fade);
77   nextpush = !nextpop;
78   nextpop = false;
79   speed = 1.0;
80 }
81
82 void
83 MainLoop::exit_screen(ScreenFade* screen_fade)
84 {
85   next_screen.reset(NULL);
86   this->screen_fade.reset(screen_fade);
87   nextpop = true;
88   nextpush = false;
89 }
90
91 void
92 MainLoop::set_screen_fade(ScreenFade* screen_fade)
93 {
94   this->screen_fade.reset(screen_fade);
95 }
96
97 void
98 MainLoop::quit(ScreenFade* screen_fade)
99 {
100   for(std::vector<Screen*>::iterator i = screen_stack.begin();
101           i != screen_stack.end(); ++i)
102     delete *i;
103   screen_stack.clear();
104
105   exit_screen(screen_fade);
106 }
107
108 void
109 MainLoop::set_speed(float speed)
110 {
111   this->speed = speed;
112 }
113
114 float
115 MainLoop::get_speed() const
116 {
117   return speed;
118 }
119
120 void
121 MainLoop::draw_fps(DrawingContext& context, float fps_fps)
122 {
123   char str[60];
124   snprintf(str, sizeof(str), "%3.1f", fps_fps);
125   const char* fpstext = "FPS";
126   context.draw_text(white_text, fpstext, Vector(SCREEN_WIDTH - white_text->get_text_width(fpstext) - gold_text->get_text_width(" 99999") - BORDER_X, BORDER_Y + 20), ALIGN_LEFT, LAYER_HUD);
127   context.draw_text(gold_text, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y + 20), ALIGN_RIGHT, LAYER_HUD);
128 }
129
130 void
131 MainLoop::draw(DrawingContext& context)
132 {
133   static Uint32 fps_ticks = SDL_GetTicks();
134   static int frame_count = 0;
135
136   current_screen->draw(context);
137   if(Menu::current() != NULL)
138     Menu::current()->draw(context);
139   if(screen_fade.get() != NULL)
140     screen_fade->draw(context);
141   Console::instance->draw(context);
142
143   if(config->show_fps)
144     draw_fps(context, fps);
145
146   // if a screenshot was requested, pass request on to drawing_context
147   if (screenshot_requested) {
148     context.take_screenshot();
149     screenshot_requested = false;
150   }
151   context.do_drawing();
152
153   /* Calculate frames per second */
154   if(config->show_fps)
155   {
156     ++frame_count;
157
158     if(SDL_GetTicks() - fps_ticks >= 500)
159     {
160       fps = (float) frame_count / .5;
161       frame_count = 0;
162       fps_ticks = SDL_GetTicks();
163     }
164   }
165 }
166
167 void
168 MainLoop::update_gamelogic(float elapsed_time)
169 {
170   Scripting::update_debugger();
171   Scripting::TimeScheduler::instance->update(game_time);
172   current_screen->update(elapsed_time);
173   if(screen_fade.get() != NULL)
174     screen_fade->update(elapsed_time);
175   Console::instance->update(elapsed_time);
176 }
177
178 void
179 MainLoop::process_events()
180 {
181   main_controller->update();
182   SDL_Event event;
183   while(SDL_PollEvent(&event)) {
184     main_controller->process_event(event);
185     if(Menu::current() != NULL)
186       Menu::current()->event(event);
187     if(event.type == SDL_QUIT)
188       quit();
189     else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_F11) {
190       config->use_fullscreen = !config->use_fullscreen;
191       init_video();
192     }
193     else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
194       take_screenshot();
195     }
196   }
197 }
198
199 void
200 MainLoop::handle_screen_switch()
201 {
202   while( (next_screen.get() != NULL || nextpop) &&
203       (screen_fade.get() == NULL || screen_fade->done())) {
204     if(current_screen.get() != NULL) {
205       current_screen->leave();
206     }
207
208     if(nextpop) {
209       if(screen_stack.empty()) {
210         running = false;
211         break;
212       }
213       next_screen.reset(screen_stack.back());
214       screen_stack.pop_back();
215     }
216     if(nextpush && current_screen.get() != NULL) {
217       screen_stack.push_back(current_screen.release());
218     }
219
220     nextpush = false;
221     nextpop = false;
222     speed = 1.0;
223     if(next_screen.get() != NULL)
224       next_screen->setup();
225     current_screen.reset(next_screen.release());
226     screen_fade.reset(NULL);
227
228     waiting_threads.wakeup();
229   }
230 }
231
232 void
233 MainLoop::run(DrawingContext &context)
234 {
235   Uint32 last_ticks = 0;
236   Uint32 elapsed_ticks = 0;
237
238   running = true;
239   while(running) {
240
241     handle_screen_switch();
242     if(!running || current_screen.get() == NULL)
243       break;
244
245     Uint32 ticks = SDL_GetTicks();
246     elapsed_ticks += ticks - last_ticks;
247     last_ticks = ticks;
248
249     Uint32 ticks_per_frame = (Uint32) (TICKS_PER_FRAME * game_speed);
250
251     if (elapsed_ticks > ticks_per_frame*4) {
252       // when the game loads up or levels are switched the
253       // elapsed_ticks grows extremly large, so we just ignore those
254       // large time jumps
255       elapsed_ticks = 0;
256     }
257
258     int frames = 0;
259
260     if (elapsed_ticks > ticks_per_frame) 
261       {
262         while(elapsed_ticks > ticks_per_frame && frames < MAX_FRAME_SKIP) 
263           {
264             elapsed_ticks -= ticks_per_frame;
265             float timestep = 1.0 / LOGICAL_FPS;
266             real_time += timestep;
267             timestep *= speed;
268             game_time += timestep;
269
270             process_events();
271             update_gamelogic(timestep);
272             frames += 1;
273           }
274
275         draw(context);
276       }
277
278     sound_manager->update();
279
280     SDL_Delay(0);
281   }
282 }
283
284 void 
285 MainLoop::take_screenshot()
286 {
287   screenshot_requested = true;
288 }
289