0a2daebdecb00803ac6aafab0012e92ace0da49f
[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 MainLoop* main_loop = NULL;
50
51 MainLoop::MainLoop()
52   : speed(1.0), nextpop(false), nextpush(false), fps(0), screenshot_requested(false)
53 {
54   using namespace Scripting;
55   TimeScheduler::instance = new TimeScheduler();
56 }
57
58 MainLoop::~MainLoop()
59 {
60   using namespace Scripting;
61   delete TimeScheduler::instance;
62   TimeScheduler::instance = NULL;
63
64   for(std::vector<Screen*>::iterator i = screen_stack.begin();
65       i != screen_stack.end(); ++i) {
66     delete *i;
67   }
68 }
69
70 void
71 MainLoop::push_screen(Screen* screen, ScreenFade* screen_fade)
72 {
73   this->next_screen.reset(screen);
74   this->screen_fade.reset(screen_fade);
75   nextpush = !nextpop;
76   nextpop = false;
77   speed = 1.0;
78 }
79
80 void
81 MainLoop::exit_screen(ScreenFade* screen_fade)
82 {
83   next_screen.reset(NULL);
84   this->screen_fade.reset(screen_fade);
85   nextpop = true;
86   nextpush = false;
87 }
88
89 void
90 MainLoop::set_screen_fade(ScreenFade* screen_fade)
91 {
92   this->screen_fade.reset(screen_fade);
93 }
94
95 void
96 MainLoop::quit(ScreenFade* screen_fade)
97 {
98   for(std::vector<Screen*>::iterator i = screen_stack.begin();
99           i != screen_stack.end(); ++i)
100     delete *i;
101   screen_stack.clear();
102
103   exit_screen(screen_fade);
104 }
105
106 void
107 MainLoop::set_speed(float speed)
108 {
109   this->speed = speed;
110 }
111
112 void
113 MainLoop::draw_fps(DrawingContext& context, float fps_fps)
114 {
115   char str[60];
116   snprintf(str, sizeof(str), "%3.1f", fps_fps);
117   const char* fpstext = "FPS";
118   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);
119   context.draw_text(gold_text, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y + 20), ALIGN_RIGHT, LAYER_HUD);
120 }
121
122 void
123 MainLoop::draw(DrawingContext& context)
124 {
125   static Uint32 fps_ticks = SDL_GetTicks();
126   static int frame_count = 0;
127
128   current_screen->draw(context);
129   if(Menu::current() != NULL)
130     Menu::current()->draw(context);
131   if(screen_fade.get() != NULL)
132     screen_fade->draw(context);
133   Console::instance->draw(context);
134
135   if(config->show_fps)
136     draw_fps(context, fps);
137
138   // if a screenshot was requested, pass request on to drawing_context
139   if (screenshot_requested) {
140     context.take_screenshot();
141     screenshot_requested = false;
142   }
143   context.do_drawing();
144
145   /* Calculate frames per second */
146   if(config->show_fps)
147   {
148     ++frame_count;
149
150     if(SDL_GetTicks() - fps_ticks >= 500)
151     {
152       fps = (float) frame_count / .5;
153       frame_count = 0;
154       fps_ticks = SDL_GetTicks();
155     }
156   }
157 }
158
159 void
160 MainLoop::update_gamelogic(float elapsed_time)
161 {
162   Scripting::update_debugger();
163   Scripting::TimeScheduler::instance->update(game_time);
164   current_screen->update(elapsed_time);
165   if(screen_fade.get() != NULL)
166     screen_fade->update(elapsed_time);
167   Console::instance->update(elapsed_time);
168 }
169
170 void
171 MainLoop::process_events()
172 {
173   main_controller->update();
174   SDL_Event event;
175   while(SDL_PollEvent(&event)) {
176     main_controller->process_event(event);
177     if(Menu::current() != NULL)
178       Menu::current()->event(event);
179     if(event.type == SDL_QUIT)
180       quit();
181     else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_F11) {
182       config->use_fullscreen = !config->use_fullscreen;
183       init_video();
184     }
185     else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
186       take_screenshot();
187     }
188   }
189 }
190
191 void
192 MainLoop::handle_screen_switch()
193 {
194   while( (next_screen.get() != NULL || nextpop) &&
195       (screen_fade.get() == NULL || screen_fade->done())) {
196     if(current_screen.get() != NULL) {
197       current_screen->leave();
198     }
199
200     if(nextpop) {
201       if(screen_stack.empty()) {
202         running = false;
203         break;
204       }
205       next_screen.reset(screen_stack.back());
206       screen_stack.pop_back();
207     }
208     if(nextpush && current_screen.get() != NULL) {
209       screen_stack.push_back(current_screen.release());
210     }
211
212     nextpush = false;
213     nextpop = false;
214     speed = 1.0;
215     if(next_screen.get() != NULL)
216       next_screen->setup();
217     current_screen.reset(next_screen.release());
218     screen_fade.reset(NULL);
219
220     waiting_threads.wakeup();
221   }
222 }
223
224 void
225 MainLoop::run()
226 {
227   DrawingContext context;
228
229   Uint32 last_ticks = 0;
230   Uint32 elapsed_ticks = 0;
231
232   running = true;
233   while(running) {
234
235     handle_screen_switch();
236     if(!running || current_screen.get() == NULL)
237       break;
238
239     Uint32 ticks = SDL_GetTicks();
240     elapsed_ticks += ticks - last_ticks;
241     last_ticks = ticks;
242
243     if (elapsed_ticks > TICKS_PER_FRAME*4) {
244       // when the game loads up or levels are switched the
245       // elapsed_ticks grows extremly large, so we just ignore those
246       // large time jumps
247       elapsed_ticks = 0;
248     }
249
250     int frames = 0;
251
252     if (elapsed_ticks > TICKS_PER_FRAME) {
253       while(elapsed_ticks > TICKS_PER_FRAME && frames < MAX_FRAME_SKIP) {
254         elapsed_ticks -= TICKS_PER_FRAME;
255         float timestep = 1.0 / LOGICAL_FPS;
256         real_time += timestep;
257         timestep *= speed;
258         game_time += timestep;
259
260         process_events();
261         update_gamelogic(timestep);
262         frames += 1;
263       }
264
265       draw(context);
266     }
267
268     sound_manager->update();
269
270     SDL_Delay(0);
271   }
272 }
273
274 void 
275 MainLoop::take_screenshot()
276 {
277   screenshot_requested = true;
278 }
279