Finally!!
[supertux.git] / src / title.cpp
1 /*
2   title.c
3   
4   Super Tux - Title Screen
5   
6   by Bill Kendrick
7   bill@newbreedsoftware.com
8   http://www.newbreedsoftware.com/supertux/
9   
10   April 11, 2000 - March 15, 2004
11 */
12
13 #include <iostream>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <SDL.h>
20 #include <SDL_image.h>
21
22 #ifndef WIN32
23 #include <sys/types.h>
24 #include <ctype.h>
25 #endif
26
27 #include "defines.h"
28 #include "globals.h"
29 #include "title.h"
30 #include "screen.h"
31 #include "high_scores.h"
32 #include "menu.h"
33 #include "texture.h"
34 #include "timer.h"
35 #include "setup.h"
36 #include "level.h"
37 #include "gameloop.h"
38 #include "leveleditor.h"
39 #include "scene.h"
40 #include "player.h"
41 #include "math.h"
42 #include "tile.h"
43 #include "resources.h"
44
45 static Surface* bkg_title;
46 static Surface* logo;
47 static Surface* img_choose_subset;
48
49 static bool walking;
50 static Timer random_timer;
51
52 static SDL_Event event;
53 static SDLKey key;
54 static int frame, i;
55 static unsigned int last_update_time;
56 static unsigned int update_time;
57
58 void display_credits();
59
60 std::vector<st_subset> contrib_subsets;
61 std::string current_contrib_subset;
62
63 void update_contrib_menu()
64 {
65   // FIXME: Hack to update only once
66   static bool up_to_date = false;
67
68   if (!up_to_date)
69     {
70       string_list_type level_subsets = dsubdirs("/levels", "info");
71
72       contrib_menu->clear();
73       contrib_menu->additem(MN_LABEL,"Contrib Levels",0,0);
74       contrib_menu->additem(MN_HL,"",0,0);
75
76       for (int i = 0; i < level_subsets.num_items; ++i)
77         {
78           st_subset subset;
79           subset.load(level_subsets.item[i]);
80           contrib_menu->additem(MN_GOTO, subset.title.c_str(), i, contrib_subset_menu);
81           contrib_subsets.push_back(subset);
82         }
83
84       contrib_menu->additem(MN_HL,"",0,0);
85       contrib_menu->additem(MN_BACK,"Back",0,0);
86
87       string_list_free(&level_subsets);
88       up_to_date = true;
89     }
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       index -= 1; // FIXME: Hack
139       std::cout << "Sarting level: " << index << std::endl;
140       GameSession session(current_contrib_subset, index, ST_GL_PLAY);
141       session.run();
142       menu_reset();
143       Menu::set_current(main_menu);
144       show_menu = 1;
145     }  
146 }
147
148 void draw_background()
149 {
150   /* Draw the title background: */
151
152   bkg_title->draw_bg();
153 }
154
155 void draw_demo(GameSession* session, double frame_ratio)
156 {
157   World::set_current(session->get_world());
158   //World* world  = session->get_world();
159   Level* plevel = session->get_level();
160   Player* tux = session->get_world()->get_tux();
161   
162   /* FIXME:
163   // update particle systems
164   std::vector<ParticleSystem*>::iterator p;
165   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
166     {
167       (*p)->simulate(frame_ratio);
168     }
169
170   // Draw particle systems (background)
171   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
172     {
173       (*p)->draw(scroll_x, 0, 0);
174     }
175   */
176
177   // Draw interactive tiles:
178   for (int y = 0; y < 15; ++y)
179     {
180       for (int x = 0; x < 21; ++x)
181         {
182           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
183                      plevel->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
184         }
185     }
186
187   global_frame_counter++;
188   tux->key_event(SDLK_RIGHT,DOWN);
189   
190   if(random_timer.check())
191     {
192       if(walking)
193         tux->key_event(SDLK_UP,UP);
194       else
195         tux->key_event(SDLK_UP,DOWN);
196     }
197   else
198     {
199       random_timer.start(rand() % 3000 + 3000);
200       walking = !walking;
201     }
202   
203   // Wrap around at the end of the level back to the beginnig
204   if(plevel->width * 32 - 320 < tux->base.x)
205     {
206       tux->base.x = tux->base.x - (plevel->width * 32 - 640);
207       scroll_x = tux->base.x - 320;
208     }
209
210
211
212   float last_tux_x_pos = tux->base.x;
213   tux->action(frame_ratio);
214
215   // Jump if tux stays in the same position for one loop, ie. if he is
216   // stuck behind a wall
217   if (last_tux_x_pos == tux->base.x)
218     walking = false;
219
220   tux->draw();
221 }
222
223 /* --- TITLE SCREEN --- */
224 bool title(void)
225 {
226   st_subset subset;
227   random_timer.init(true);
228
229   walking = true;
230
231   st_pause_ticks_init();
232
233   GameSession session(datadir + "/levels/misc/menu.stl", 0, ST_GL_DEMO_GAME);
234
235   //FIXME:activate_particle_systems();
236
237   /* Reset menu variables */
238   menu_reset();
239   Menu::set_current(main_menu);
240
241   clearscreen(0, 0, 0);
242   updatescreen();
243
244   /* Load images: */
245
246   bkg_title = new Surface(datadir + "/images/title/background.jpg", IGNORE_ALPHA);
247   logo = new Surface(datadir + "/images/title/logo.png", USE_ALPHA);
248   img_choose_subset = new Surface(datadir + "/images/status/choose-level-subset.png", USE_ALPHA);
249
250   /* --- Main title loop: --- */
251   bool done = 0;
252   show_menu = 1;
253   frame = 0;
254
255   /* Draw the title background: */
256   bkg_title->draw_bg();
257   load_hs();
258
259   update_time = st_get_ticks();
260   random_timer.start(rand() % 2000 + 2000);
261
262   while (!done)
263     {
264       /* Calculate the movement-factor */
265       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
266       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
267         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
268       /* Lower the frame_ratio that Tux doesn't jump to hectically throught the demo. */
269       frame_ratio /= 2;
270
271       /* Handle events: */
272
273       while (SDL_PollEvent(&event))
274         {
275           current_menu->event(event);
276           if (event.type == SDL_QUIT)
277             {
278               done = true;
279             }
280           else if (event.type == SDL_KEYDOWN)
281             {
282               /* Keypress... */
283               key = event.key.keysym.sym;
284
285               /* Check for menu events */
286               //menu_event(event);
287
288               if (!show_menu)
289                 {
290                   /* Escape: Quit: */
291                   done = true;
292                 }
293             }
294         }
295
296       /* Draw the background: */
297       draw_background();
298       draw_demo(&session, frame_ratio);
299       
300       if (current_menu == main_menu)
301         logo->draw( 160, 30);
302
303       white_small_text->draw(
304                 " SuperTux " VERSION "\n"
305                 "Copyright (c) 2003 SuperTux Devel Team\n"
306                 "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"
307                 "are welcome to redistribute it under certain conditions; see the file COPYING\n"
308                 "for details.\n",
309                 0, 420, 0);
310
311       /* Draw the high score: */
312       /*
313         sprintf(str, "High score: %d", hs_score);
314         text_drawf(&gold_text, str, 0, -40, A_HMIDDLE, A_BOTTOM, 1);
315         sprintf(str, "by %s", hs_name);
316         text_drawf(&gold_text, str, 0, -20, A_HMIDDLE, A_BOTTOM, 1);
317       */
318
319       /* Don't draw menu, if quit is true */
320       if(show_menu && !done)
321         menu_process_current();
322
323       if(current_menu == main_menu)
324         {
325           switch (main_menu->check())
326             {
327 #if 0
328             case 0:
329               string_list_type level_subsets;
330               level_subsets = dsubdirs("/levels", "info");
331
332               // Quick Play
333               // FIXME: obsolete
334               done = 0;
335               i = 0;
336               if(level_subsets.num_items != 0)
337                 {
338                   subset.load(level_subsets.item[0]);
339                   while(!done)
340                     {
341                       img_choose_subset->draw((screen->w - img_choose_subset.w) / 2, 0);
342                       if(level_subsets.num_items != 0)
343                         {
344                           subset.image->draw((screen->w - subset.image.w) / 2 + 25,78);
345                           if(level_subsets.num_items > 1)
346                             {
347                               if(i > 0)
348                                 arrow_left->draw((screen->w / 2) - ((subset.title.length()+2)*16)/2,20);
349                               if(i < level_subsets.num_items-1)
350                                 arrow_right->draw((screen->w / 2) + ((subset.description.length())*16)/2,20);
351                             }
352                           text_drawf(&gold_text, subset.title.c_str(), 0, 20, A_HMIDDLE, A_TOP, 1);
353                           text_drawf(&gold_text, subset.description.c_str(), 20, -20, A_HMIDDLE, A_BOTTOM, 1);
354                         }
355                       updatescreen();
356                       SDL_Delay(50);
357                       while(SDL_PollEvent(&event) && !done)
358                         {
359                           switch(event.type)
360                             {
361                             case SDL_QUIT:
362                               done = true;
363                               break;
364                             case SDL_KEYDOWN:           // key pressed
365                               // Keypress...
366                               key = event.key.keysym.sym;
367
368                               if(key == SDLK_LEFT)
369                                 {
370                                   if(i > 0)
371                                     {
372                                       --i;
373                                       subset.free();
374                                       subset.load(level_subsets.item[i]);
375                                     }
376                                 }
377                               else if(key == SDLK_RIGHT)
378                                 {
379                                   if(i < level_subsets.num_items -1)
380                                     {
381                                       ++i;
382                                       subset.free();
383                                       subset.load(level_subsets.item[i]);
384                                     }
385                                 }
386                               else if(key == SDLK_SPACE || key == SDLK_RETURN)
387                                 {
388                                   done = true;
389                                   quit = gameloop(subset.name.c_str(),1,ST_GL_PLAY);
390                                   subset.free();
391                                 }
392                               else if(key == SDLK_ESCAPE)
393                                 {
394                                   done = true;
395                                 }
396                               break;
397                             default:
398                               break;
399                             }
400                         }
401                     }
402                 }
403               // reset tux
404               scroll_x = 0;
405               titletux.level_begin();
406               update_time = st_get_ticks();
407               break;
408 #endif
409             case 0:
410               // Start Game, ie. goto the slots menu
411               update_load_save_game_menu(load_game_menu);
412               break;
413             case 1:
414               // Contrib Menu
415               update_contrib_menu();
416               break;
417             case 3:
418               done = true;
419               done = leveleditor(1);
420               menu_reset();
421               show_menu = 1;
422               Menu::set_current(main_menu);
423               break;
424             case 4:
425               display_credits();
426               break;
427             case 5:
428               done = true;
429               break;
430             }
431         }
432       else if(current_menu == options_menu)
433         {
434           process_options_menu();
435         }
436       else if(current_menu == load_game_menu)
437         {
438           if (process_load_game_menu())
439             {
440               // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
441               // reset tux
442               scroll_x = 0;
443               //titletux.level_begin();
444               update_time = st_get_ticks();
445             }
446         }
447       else if(current_menu == contrib_menu)
448         {
449           check_contrib_menu();
450         }
451       else if (current_menu == contrib_subset_menu)
452         {
453           check_contrib_subset_menu();
454         }
455
456       mouse_cursor->draw();
457       
458       flipscreen();
459
460       /* Set the time of the last update and the time of the current update */
461       last_update_time = update_time;
462       update_time = st_get_ticks();
463
464       /* Pause: */
465       frame++;
466       SDL_Delay(25);
467
468     }
469   /* Free surfaces: */
470
471   delete bkg_title;
472   delete logo;
473   
474   /* Return to main! */
475   return done;
476 }
477
478 #define MAX_VEL 10
479 #define SPEED   1
480 #define SCROLL  60
481
482 void display_credits()
483 {
484   int done;
485   int scroll, speed;
486   Timer timer;
487   int n,d;
488   int length;
489   FILE* fi;
490   char temp[1024];
491   string_list_type names;
492   char filename[1024];
493   string_list_init(&names);
494   sprintf(filename,"%s/CREDITS", datadir.c_str());
495   if((fi = fopen(filename,"r")) != NULL)
496     {
497       while(fgets(temp, sizeof(temp), fi) != NULL)
498         {
499           temp[strlen(temp)-1]='\0';
500           string_list_add_item(&names,temp);
501         }
502       fclose(fi);
503     }
504   else
505     {
506       string_list_add_item(&names,"Credits were not found!");
507       string_list_add_item(&names,"Shame on the guy, who");
508       string_list_add_item(&names,"forgot to include them");
509       string_list_add_item(&names,"in your SuperTux distribution.");
510     }
511
512
513   timer.init(SDL_GetTicks());
514   timer.start(50);
515
516   scroll = 0;
517   speed = 2;
518   done = 0;
519
520   n = d = 0;
521
522   length = names.num_items;
523
524   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
525
526   while(done == 0)
527     {
528       /* in case of input, exit */
529       while(SDL_PollEvent(&event))
530         switch(event.type)
531           {
532           case SDL_KEYDOWN:
533             switch(event.key.keysym.sym)
534               {
535               case SDLK_UP:
536                 speed -= SPEED;
537                 break;
538               case SDLK_DOWN:
539                 speed += SPEED;
540                 break;
541               case SDLK_SPACE:
542               case SDLK_RETURN:
543                 if(speed >= 0)
544                   scroll += SCROLL;
545                 break;
546               case SDLK_ESCAPE:
547                 done = 1;
548                 break;
549               default:
550                 break;
551               }
552             break;
553           case SDL_QUIT:
554             done = 1;
555             break;
556           default:
557             break;
558           }
559
560       if(speed > MAX_VEL)
561         speed = MAX_VEL;
562       else if(speed < -MAX_VEL)
563         speed = -MAX_VEL;
564
565       /* draw the credits */
566
567       draw_background();
568
569       white_big_text->drawf("- Credits -", 0, screen->h-scroll, A_HMIDDLE, A_TOP, 2);
570
571       for(i = 0, n = 0, d = 0; i < length; i++,n++,d++)
572         {
573           if(names.item[i] == "")
574             n--;
575           else
576             {
577               if(names.item[i][0] == ' ')
578                 white_small_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll-10, A_HMIDDLE, A_TOP, 1);
579               else if(names.item[i][0] == '     ')
580                 white_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 1);
581               else if(names.item[i+1][0] == '-' || names.item[i][0] == '-')
582                 white_big_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 3);
583               else
584                 blue_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 1);
585             }
586         }
587
588       flipscreen();
589
590       if(60+screen->h+(n*18)+(d*18)-scroll < 0 && 20+60+screen->h+(n*18)+(d*18)-scroll < 0)
591         done = 1;
592
593       scroll += speed;
594       if(scroll < 0)
595         scroll = 0;
596
597       SDL_Delay(35);
598
599       if(timer.get_left() < 0)
600         {
601           frame++;
602           timer.start(50);
603         }
604     }
605   string_list_free(&names);
606
607   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
608   show_menu = 1;
609   Menu::set_current(main_menu);
610 }