Added Marek music to cvs and play it in the main menu.
[supertux.git] / src / title.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 // 
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 //  02111-1307, USA.
21
22 #include <iostream>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <SDL.h>
29 #include <SDL_image.h>
30
31 #ifndef WIN32
32 #include <sys/types.h>
33 #include <ctype.h>
34 #endif
35
36 #include "defines.h"
37 #include "globals.h"
38 #include "title.h"
39 #include "screen.h"
40 #include "high_scores.h"
41 #include "menu.h"
42 #include "texture.h"
43 #include "timer.h"
44 #include "setup.h"
45 #include "level.h"
46 #include "gameloop.h"
47 #include "leveleditor.h"
48 #include "scene.h"
49 #include "player.h"
50 #include "math.h"
51 #include "tile.h"
52 #include "resources.h"
53
54 static Surface* bkg_title;
55 static Surface* logo;
56 static Surface* img_choose_subset;
57
58 static bool walking;
59 static Timer random_timer;
60
61 static int frame;
62 static unsigned int last_update_time;
63 static unsigned int update_time;
64
65 void display_credits();
66
67 std::vector<st_subset> contrib_subsets;
68 std::string current_contrib_subset;
69
70 void generate_contrib_menu()
71 {
72   string_list_type level_subsets = dsubdirs("/levels", "info");
73
74   contrib_menu->clear();
75   contrib_menu->additem(MN_LABEL,"Contrib Levels",0,0);
76   contrib_menu->additem(MN_HL,"",0,0);
77
78   for (int i = 0; i < level_subsets.num_items; ++i)
79     {
80       st_subset subset;
81       subset.load(level_subsets.item[i]);
82       contrib_menu->additem(MN_GOTO, subset.title.c_str(), i, contrib_subset_menu);
83       contrib_subsets.push_back(subset);
84     }
85
86   contrib_menu->additem(MN_HL,"",0,0);
87   contrib_menu->additem(MN_BACK,"Back",0,0);
88
89   string_list_free(&level_subsets);
90 }
91
92 void check_contrib_menu()
93 {
94   static int current_subset = -1;
95
96   int index = contrib_menu->check();
97   if (index != -1)
98     {
99       index -= 2; // FIXME: Hack
100       if (index >= 0 && index <= int(contrib_subsets.size()))
101         {
102           if (current_subset != index)
103             {
104               current_subset = index;
105               // FIXME: This shouln't be busy looping
106               st_subset& subset = contrib_subsets[index];
107           
108               current_contrib_subset = subset.name;
109
110               std::cout << "Updating the contrib subset menu..." << subset.levels << std::endl;
111       
112               contrib_subset_menu->clear();
113
114               contrib_subset_menu->additem(MN_LABEL, subset.title, 0,0);
115               contrib_subset_menu->additem(MN_HL,"",0,0);
116               for (int i = 1; i <= subset.levels; ++i)
117                 {
118                   Level level;
119                   level.load(subset.name, i);
120                   contrib_subset_menu->additem(MN_ACTION, level.name, 0, 0);
121                 }
122               contrib_subset_menu->additem(MN_HL,"",0,0);      
123               contrib_subset_menu->additem(MN_BACK, "Back", 0, 0);
124             }
125         }
126       else
127         {
128           // Back button
129         }
130     }
131 }
132
133 void check_contrib_subset_menu()
134 {
135   int index = contrib_subset_menu->check();
136   if (index != -1)
137     {
138       if (contrib_subset_menu->get_item(index).kind == MN_ACTION)
139         {
140           index -= 1; // FIXME: Hack
141           std::cout << "Sarting level: " << index << std::endl;
142           GameSession session(current_contrib_subset, index, ST_GL_PLAY);
143           session.run();
144           Menu::set_current(main_menu);
145         }
146     }  
147 }
148
149 void draw_background()
150 {
151   /* Draw the title background: */
152
153   bkg_title->draw_bg();
154 }
155
156 void draw_demo(GameSession* session, double frame_ratio)
157 {
158   World::set_current(session->get_world());
159   //World* world  = session->get_world();
160   Level* plevel = session->get_level();
161   Player* tux = session->get_world()->get_tux();
162   
163   /* FIXME:
164   // update particle systems
165   std::vector<ParticleSystem*>::iterator p;
166   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
167     {
168       (*p)->simulate(frame_ratio);
169     }
170
171   // Draw particle systems (background)
172   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
173     {
174       (*p)->draw(scroll_x, 0, 0);
175     }
176   */
177
178   // Draw interactive tiles:
179   for (int y = 0; y < 15; ++y)
180     {
181       for (int x = 0; x < 21; ++x)
182         {
183           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
184                      plevel->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
185         }
186     }
187
188   global_frame_counter++;
189   tux->key_event(SDLK_RIGHT,DOWN);
190   
191   if(random_timer.check())
192     {
193       if(walking)
194         tux->key_event(SDLK_UP,UP);
195       else
196         tux->key_event(SDLK_UP,DOWN);
197     }
198   else
199     {
200       random_timer.start(rand() % 3000 + 3000);
201       walking = !walking;
202     }
203   
204   // Wrap around at the end of the level back to the beginnig
205   if(plevel->width * 32 - 320 < tux->base.x)
206     {
207       tux->base.x = tux->base.x - (plevel->width * 32 - 640);
208       scroll_x = tux->base.x - 320;
209     }
210
211   float last_tux_x_pos = tux->base.x;
212   tux->action(frame_ratio);
213
214   // Jump if tux stays in the same position for one loop, ie. if he is
215   // stuck behind a wall
216   if (last_tux_x_pos == tux->base.x)
217     walking = false;
218
219   tux->draw();
220 }
221
222 /* --- TITLE SCREEN --- */
223 void title(void)
224 {
225   st_subset subset;
226   random_timer.init(true);
227
228   walking = true;
229
230   st_pause_ticks_init();
231
232   GameSession session(datadir + "/levels/misc/menu.stl", 0, ST_GL_DEMO_GAME);
233
234   clearscreen(0, 0, 0);
235   updatescreen();
236
237   /* Load images: */
238   bkg_title = new Surface(datadir + "/images/title/background.jpg", IGNORE_ALPHA);
239   logo = new Surface(datadir + "/images/title/logo.png", USE_ALPHA);
240   img_choose_subset = new Surface(datadir + "/images/status/choose-level-subset.png", USE_ALPHA);
241
242   /* --- Main title loop: --- */
243   frame = 0;
244
245   /* Draw the title background: */
246   bkg_title->draw_bg();
247
248   update_time = st_get_ticks();
249   random_timer.start(rand() % 2000 + 2000);
250
251   Mix_Music* music = load_song(datadir + "/music/theme.mod");
252   play_music(music, 1);
253
254   Menu::set_current(main_menu);
255   while (Menu::current())
256     {
257       // Calculate the movement-factor
258       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
259       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
260         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
261       /* Lower the frame_ratio that Tux doesn't jump to hectically throught the demo. */
262       frame_ratio /= 2;
263
264       SDL_Event event;
265       while (SDL_PollEvent(&event))
266         {
267           if (Menu::current())
268             {
269               Menu::current()->event(event);
270             }
271          // FIXME: QUIT signal should be handled more generic, not locally
272           if (event.type == SDL_QUIT)
273             Menu::set_current(0);
274         }
275
276       /* Draw the background: */
277       draw_background();
278       draw_demo(&session, frame_ratio);
279       
280       if (Menu::current() == main_menu)
281         logo->draw( 160, 30);
282
283       white_small_text->draw(" SuperTux " VERSION "\n"
284                              "Copyright (c) 2003 SuperTux Devel Team\n"
285                              "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"
286                              "are welcome to redistribute it under certain conditions; see the file COPYING\n"
287                              "for details.\n",
288                              0, 420, 0);
289
290       /* Don't draw menu, if quit is true */
291       Menu* menu = Menu::current();
292       if(menu)
293         {
294           menu->draw();
295           menu->action();
296         
297           if(menu == main_menu)
298             {
299               switch (main_menu->check())
300                 {
301                 case 0:
302                   // Start Game, ie. goto the slots menu
303                   update_load_save_game_menu(load_game_menu);
304                   break;
305                 case 1:
306                   // Contrib Menu
307                   puts("Entering contrib menu");
308                   generate_contrib_menu();
309                   break;
310                 case 3:
311                   halt_music();
312                   leveleditor(1);
313                   Menu::set_current(main_menu);
314                   break;
315                 case 4:
316                   display_credits();
317                   Menu::set_current(main_menu);
318                   break;
319                 case 6:
320                   Menu::set_current(0);
321                   break;
322                 }
323             }
324           else if(menu == options_menu)
325             {
326               process_options_menu();
327             }
328           else if(menu == load_game_menu)
329             {
330               if (process_load_game_menu())
331                 {
332                   // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
333                   // reset tux
334                   scroll_x = 0;
335                   //titletux.level_begin();
336                   update_time = st_get_ticks();
337                 }
338             }
339           else if(menu == contrib_menu)
340             {
341               check_contrib_menu();
342             }
343           else if (menu == contrib_subset_menu)
344             {
345               check_contrib_subset_menu();
346             }
347         }
348
349       mouse_cursor->draw();
350       
351       flipscreen();
352
353       /* Set the time of the last update and the time of the current update */
354       last_update_time = update_time;
355       update_time = st_get_ticks();
356
357       /* Pause: */
358       frame++;
359       SDL_Delay(25);
360
361       if(!playing_music)
362        play_music(music, 1);
363     }
364   /* Free surfaces: */
365
366   delete bkg_title;
367   delete logo;
368   free_music(music);
369 }
370
371 #define MAX_VEL 10
372 #define SPEED   1
373 #define SCROLL  60
374 #define ITEMS_SPACE 4
375
376 void display_credits()
377 {
378   int done;
379   int scroll, speed;
380   int y;
381   Timer timer;
382   int length;
383   FILE* fi;
384   char temp[1024];
385   string_list_type names;
386   char filename[1024];
387   string_list_init(&names);
388   sprintf(filename,"%s/CREDITS", datadir.c_str());
389   if((fi = fopen(filename,"r")) != NULL)
390     {
391       while(fgets(temp, sizeof(temp), fi) != NULL)
392         {
393           temp[strlen(temp)-1]='\0';
394           string_list_add_item(&names,temp);
395         }
396       fclose(fi);
397     }
398   else
399     {
400       string_list_add_item(&names,"Credits were not found!");
401       string_list_add_item(&names,"Shame on the guy, who");
402       string_list_add_item(&names,"forgot to include them");
403       string_list_add_item(&names,"in your SuperTux distribution.");
404     }
405
406
407   timer.init(SDL_GetTicks());
408   timer.start(50);
409
410   scroll = 0;
411   speed = 2;
412   done = 0;
413
414   length = names.num_items;
415
416   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
417
418   while(done == 0)
419     {
420       /* in case of input, exit */
421       SDL_Event event;
422       while(SDL_PollEvent(&event))
423         switch(event.type)
424           {
425           case SDL_KEYDOWN:
426             switch(event.key.keysym.sym)
427               {
428               case SDLK_UP:
429                 speed -= SPEED;
430                 break;
431               case SDLK_DOWN:
432                 speed += SPEED;
433                 break;
434               case SDLK_SPACE:
435               case SDLK_RETURN:
436                 if(speed >= 0)
437                   scroll += SCROLL;
438                 break;
439               case SDLK_ESCAPE:
440                 done = 1;
441                 break;
442               default:
443                 break;
444               }
445             break;
446           case SDL_QUIT:
447             done = 1;
448             break;
449           default:
450             break;
451           }
452
453       if(speed > MAX_VEL)
454         speed = MAX_VEL;
455       else if(speed < -MAX_VEL)
456         speed = -MAX_VEL;
457
458       /* draw the credits */
459
460       draw_background();
461
462       white_big_text->drawf("- Credits -", 0, screen->h-scroll, A_HMIDDLE, A_TOP, 2);
463
464       y = 0;
465       for(int i = 0; i < length; i++)
466         {
467         switch(names.item[i][0])
468           {
469           case ' ':
470             white_small_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
471             y += white_small_text->h+ITEMS_SPACE;
472             break;
473           case '        ':
474             white_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
475             y += white_text->h+ITEMS_SPACE;
476             break;
477           case '-':
478             white_big_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 3);
479             y += white_big_text->h+ITEMS_SPACE;
480             break;
481           default:
482             blue_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
483             y += blue_text->h+ITEMS_SPACE;
484             break;
485           }
486         }
487
488       flipscreen();
489
490       if(60+screen->h+y-scroll < 0 && 20+60+screen->h+y-scroll < 0)
491         done = 1;
492
493       scroll += speed;
494       if(scroll < 0)
495         scroll = 0;
496
497       SDL_Delay(35);
498
499       if(timer.get_left() < 0)
500         {
501           frame++;
502           timer.start(50);
503         }
504     }
505   string_list_free(&names);
506
507   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
508   Menu::set_current(main_menu);
509 }