2 more evil mainloops are gone
[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   nextpop = false;
64   speed = 1.0;
65 }
66
67 void
68 MainLoop::exit_screen(ScreenFade* screen_fade)
69 {
70   next_screen.reset(NULL);
71   this->screen_fade.reset(screen_fade);
72   nextpop = true;
73 }
74
75 void
76 MainLoop::set_screen_fade(ScreenFade* screen_fade)
77 {
78   this->screen_fade.reset(screen_fade);
79 }
80
81 void
82 MainLoop::quit(ScreenFade* screen_fade)
83 {
84   for(std::vector<Screen*>::iterator i = screen_stack.begin();
85           i != screen_stack.end(); ++i)
86     delete *i;
87
88   exit_screen(screen_fade);
89 }
90
91 void
92 MainLoop::set_speed(float speed)
93 {
94   this->speed = speed;
95 }
96
97 void
98 MainLoop::draw_fps(DrawingContext& context, float fps_fps)
99 {
100   char str[60];
101   snprintf(str, sizeof(str), "%3.1f", fps_fps);
102   const char* fpstext = "FPS";
103   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);
104   context.draw_text(gold_text, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y + 20), RIGHT_ALLIGN, LAYER_FOREGROUND1);                    
105 }
106
107 void
108 MainLoop::run()
109 {
110   DrawingContext context; 
111   
112   unsigned int frame_count;
113   float fps_fps = 0;
114   Uint32 fps_ticks = SDL_GetTicks();
115   Uint32 fps_nextframe_ticks = SDL_GetTicks();
116   Uint32 ticks;
117   bool skipdraw = false;
118   
119   running = true;
120   while(running) {
121     if( (next_screen.get() != NULL || nextpop == true) &&
122             (screen_fade.get() == NULL || screen_fade->done())) {
123       if(current_screen.get() != NULL) {
124         current_screen->leave();
125       }
126
127       if(nextpop) {
128         if(screen_stack.empty()) {
129           running = false;
130           break;
131         }
132         next_screen.reset(screen_stack.back());
133         screen_stack.pop_back();
134         nextpop = false;
135         speed = 1.0;
136       } else if(current_screen.get() != NULL) {
137         screen_stack.push_back(current_screen.release());
138       }
139       
140       next_screen->setup();
141       ScriptManager::instance->fire_wakeup_event(ScriptManager::SCREEN_SWITCHED);
142       current_screen.reset(next_screen.release());
143       next_screen.reset(NULL);
144       screen_fade.reset(NULL);
145     }
146
147     if(current_screen.get() == NULL)
148         break;
149       
150     float elapsed_time = 1.0 / LOGICAL_FPS;
151     ticks = SDL_GetTicks();
152     if(ticks > fps_nextframe_ticks) {
153       if(skipdraw == true) {
154         // already skipped last frame? we have to slow down the game then...
155         skipdraw = false;
156         fps_nextframe_ticks -= (Uint32) (1000.0 / LOGICAL_FPS);
157       } else {
158         // don't draw all frames when we're getting too slow
159         skipdraw = true;
160       }
161     } else {
162       skipdraw = false;
163       while(fps_nextframe_ticks > ticks) {
164         /* just wait */
165         // If we really have to wait long, then do an imprecise SDL_Delay()
166         Uint32 diff = fps_nextframe_ticks - ticks;
167         if(diff > 10) {
168           SDL_Delay(diff - 2);
169         }
170         ticks = SDL_GetTicks();
171       }
172     }
173     fps_nextframe_ticks = ticks + (Uint32) (1000.0 / LOGICAL_FPS);
174
175     if(!skipdraw) {
176       current_screen->draw(context);
177       if(Menu::current() != NULL)
178         Menu::current()->draw(context);
179       if(screen_fade.get() != NULL)
180         screen_fade->draw(context);
181       Console::instance->draw(context);
182
183       if(config->show_fps)
184         draw_fps(context, fps_fps);
185
186       context.do_drawing();
187
188       /* Calculate frames per second */
189       if(config->show_fps)
190       {
191         ++frame_count;
192         
193         if(SDL_GetTicks() - fps_ticks >= 500)
194         {
195           fps_fps = (float) frame_count / .5;
196           frame_count = 0;
197           fps_ticks = SDL_GetTicks();
198         }
199       }
200     }
201
202     elapsed_time *= speed;
203
204     game_time += elapsed_time;
205     ScriptManager::instance->update();
206     current_screen->update(elapsed_time);
207     if(screen_fade.get() != NULL)
208       screen_fade->update(elapsed_time);
209     Console::instance->update(elapsed_time);
210  
211     main_controller->update();
212     SDL_Event event;
213     while(SDL_PollEvent(&event)) {
214       main_controller->process_event(event);
215       if(Menu::current() != NULL)
216         Menu::current()->event(event);
217       if(event.type == SDL_QUIT)
218         quit();
219     }
220
221     sound_manager->update();
222   }
223 }
224