65da30ba4705a936109e2d9950ec905d85d196df
[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 "gameconfig.hpp"
30 #include "main.hpp"
31 #include "resources.hpp"
32 #include "script_manager.hpp"
33 #include "screen.hpp"
34 #include "screen_fade.hpp"
35 #include "timer.hpp"
36 #include "player_status.hpp"
37
38 // the engine will be run with a logical framerate of 64fps.
39 // We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
40 // binary fraction...
41 static const float LOGICAL_FPS = 64.0;
42
43 MainLoop* main_loop = NULL;
44
45 MainLoop::MainLoop()
46   : speed(1.0)
47 {
48 }
49
50 MainLoop::~MainLoop()
51 {
52   for(std::vector<Screen*>::iterator i = screen_stack.begin();
53       i != screen_stack.end(); ++i) {
54     delete *i;
55   }
56 }
57
58 void
59 MainLoop::push_screen(Screen* screen, ScreenFade* screen_fade)
60 {
61   this->next_screen.reset(screen);
62   this->screen_fade.reset(screen_fade);
63   if(nextpop)
64     nextpush = false;
65   else
66     nextpush = true;
67   nextpop = false;
68   speed = 1.0;
69 }
70
71 void
72 MainLoop::exit_screen(ScreenFade* screen_fade)
73 {
74   next_screen.reset(NULL);
75   this->screen_fade.reset(screen_fade);
76   nextpop = true;
77   nextpush = false;
78 }
79
80 void
81 MainLoop::set_screen_fade(ScreenFade* screen_fade)
82 {
83   this->screen_fade.reset(screen_fade);
84 }
85
86 void
87 MainLoop::quit(ScreenFade* screen_fade)
88 {
89   for(std::vector<Screen*>::iterator i = screen_stack.begin();
90           i != screen_stack.end(); ++i)
91     delete *i;
92   screen_stack.clear();
93
94   exit_screen(screen_fade);
95 }
96
97 void
98 MainLoop::set_speed(float speed)
99 {
100   this->speed = speed;
101 }
102
103 void
104 MainLoop::draw_fps(DrawingContext& context, float fps_fps)
105 {
106   char str[60];
107   snprintf(str, sizeof(str), "%3.1f", fps_fps);
108   const char* fpstext = "FPS";
109   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), LEFT_ALLIGN, LAYER_FOREGROUND1);
110   context.draw_text(gold_text, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y + 20), RIGHT_ALLIGN, LAYER_FOREGROUND1);                    
111 }
112
113 void
114 MainLoop::run()
115 {
116   DrawingContext context; 
117   
118   unsigned int frame_count;
119   float fps_fps = 0;
120   Uint32 fps_ticks = SDL_GetTicks();
121   Uint32 fps_nextframe_ticks = SDL_GetTicks();
122   Uint32 ticks;
123   bool skipdraw = false;
124   
125   running = true;
126   while(running) {
127     if( (next_screen.get() != NULL || nextpop == true) &&
128             (screen_fade.get() == NULL || screen_fade->done())) {
129       if(current_screen.get() != NULL) {
130         current_screen->leave();
131       }
132
133       if(nextpop) {
134         if(screen_stack.empty()) {
135           running = false;
136           break;
137         }
138         next_screen.reset(screen_stack.back());
139         screen_stack.pop_back();
140         nextpop = false;
141         speed = 1.0;
142       }
143       if(nextpush && current_screen.get() != NULL) {
144         screen_stack.push_back(current_screen.release());
145       }
146       
147       next_screen->setup();
148       ScriptManager::instance->fire_wakeup_event(ScriptManager::SCREEN_SWITCHED);
149       current_screen.reset(next_screen.release());
150       next_screen.reset(NULL);
151       screen_fade.reset(NULL);
152     }
153
154     if(current_screen.get() == NULL)
155         break;
156       
157     float elapsed_time = 1.0 / LOGICAL_FPS;
158     ticks = SDL_GetTicks();
159     if(ticks > fps_nextframe_ticks) {
160       if(skipdraw == true) {
161         // already skipped last frame? we have to slow down the game then...
162         skipdraw = false;
163         fps_nextframe_ticks -= (Uint32) (1000.0 / LOGICAL_FPS);
164       } else {
165         // don't draw all frames when we're getting too slow
166         skipdraw = true;
167       }
168     } else {
169       skipdraw = false;
170       while(fps_nextframe_ticks > ticks) {
171         /* just wait */
172         // If we really have to wait long, then do an imprecise SDL_Delay()
173         Uint32 diff = fps_nextframe_ticks - ticks;
174         if(diff > 10) {
175           SDL_Delay(diff - 2);
176         }
177         ticks = SDL_GetTicks();
178       }
179     }
180     fps_nextframe_ticks = ticks + (Uint32) (1000.0 / LOGICAL_FPS);
181
182     if(!skipdraw) {
183       current_screen->draw(context);
184       if(Menu::current() != NULL)
185         Menu::current()->draw(context);
186       if(screen_fade.get() != NULL)
187         screen_fade->draw(context);
188       Console::instance->draw(context);
189
190       if(config->show_fps)
191         draw_fps(context, fps_fps);
192
193       context.do_drawing();
194
195       /* Calculate frames per second */
196       if(config->show_fps)
197       {
198         ++frame_count;
199         
200         if(SDL_GetTicks() - fps_ticks >= 500)
201         {
202           fps_fps = (float) frame_count / .5;
203           frame_count = 0;
204           fps_ticks = SDL_GetTicks();
205         }
206       }
207     }
208
209     elapsed_time *= speed;
210
211     game_time += elapsed_time;
212     ScriptManager::instance->update();
213     current_screen->update(elapsed_time);
214     if(screen_fade.get() != NULL)
215       screen_fade->update(elapsed_time);
216     Console::instance->update(elapsed_time);
217  
218     main_controller->update();
219     SDL_Event event;
220     while(SDL_PollEvent(&event)) {
221       main_controller->process_event(event);
222       if(Menu::current() != NULL)
223         Menu::current()->event(event);
224       if(event.type == SDL_QUIT)
225         quit();
226     }
227
228     sound_manager->update();
229   }
230 }
231