7a35815137d76533ca00ff9eef2aa3ded1867846
[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::set_current(main_menu);
143     }  
144 }
145
146 void draw_background()
147 {
148   /* Draw the title background: */
149
150   bkg_title->draw_bg();
151 }
152
153 void draw_demo(GameSession* session, double frame_ratio)
154 {
155   World::set_current(session->get_world());
156   //World* world  = session->get_world();
157   Level* plevel = session->get_level();
158   Player* tux = session->get_world()->get_tux();
159   
160   /* FIXME:
161   // update particle systems
162   std::vector<ParticleSystem*>::iterator p;
163   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
164     {
165       (*p)->simulate(frame_ratio);
166     }
167
168   // Draw particle systems (background)
169   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
170     {
171       (*p)->draw(scroll_x, 0, 0);
172     }
173   */
174
175   // Draw interactive tiles:
176   for (int y = 0; y < 15; ++y)
177     {
178       for (int x = 0; x < 21; ++x)
179         {
180           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
181                      plevel->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
182         }
183     }
184
185   global_frame_counter++;
186   tux->key_event(SDLK_RIGHT,DOWN);
187   
188   if(random_timer.check())
189     {
190       if(walking)
191         tux->key_event(SDLK_UP,UP);
192       else
193         tux->key_event(SDLK_UP,DOWN);
194     }
195   else
196     {
197       random_timer.start(rand() % 3000 + 3000);
198       walking = !walking;
199     }
200   
201   // Wrap around at the end of the level back to the beginnig
202   if(plevel->width * 32 - 320 < tux->base.x)
203     {
204       tux->base.x = tux->base.x - (plevel->width * 32 - 640);
205       scroll_x = tux->base.x - 320;
206     }
207
208   float last_tux_x_pos = tux->base.x;
209   tux->action(frame_ratio);
210
211   // Jump if tux stays in the same position for one loop, ie. if he is
212   // stuck behind a wall
213   if (last_tux_x_pos == tux->base.x)
214     walking = false;
215
216   tux->draw();
217 }
218
219 /* --- TITLE SCREEN --- */
220 bool title(void)
221 {
222   st_subset subset;
223   random_timer.init(true);
224
225   walking = true;
226
227   st_pause_ticks_init();
228
229   GameSession session(datadir + "/levels/misc/menu.stl", 0, ST_GL_DEMO_GAME);
230
231   clearscreen(0, 0, 0);
232   updatescreen();
233
234   /* Load images: */
235
236   bkg_title = new Surface(datadir + "/images/title/background.jpg", IGNORE_ALPHA);
237   logo = new Surface(datadir + "/images/title/logo.png", USE_ALPHA);
238   img_choose_subset = new Surface(datadir + "/images/status/choose-level-subset.png", USE_ALPHA);
239
240   /* --- Main title loop: --- */
241   bool done = 0;
242   frame = 0;
243
244   /* Draw the title background: */
245   bkg_title->draw_bg();
246   load_hs();
247
248   update_time = st_get_ticks();
249   random_timer.start(rand() % 2000 + 2000);
250
251   Menu::set_current(main_menu);
252   while (!done)
253     {
254       // Calculate the movement-factor
255       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
256       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
257         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
258       /* Lower the frame_ratio that Tux doesn't jump to hectically throught the demo. */
259       frame_ratio /= 2;
260
261       /* Handle events: */
262
263       while (SDL_PollEvent(&event))
264         {
265           if (event.type == SDL_QUIT)
266             {
267               done = true;
268             }
269           else if (event.type == SDL_KEYDOWN)
270             {
271               /* Keypress... */
272               key = event.key.keysym.sym;
273
274               if (Menu::current())
275                 {
276                   Menu::current()->event(event);
277                 }
278
279               if (!Menu::current())
280                 {
281                   /* Escape: Quit: */
282                   done = true;
283                 }
284             }
285         }
286
287       /* Draw the background: */
288       draw_background();
289       draw_demo(&session, frame_ratio);
290       
291       if (Menu::current() == main_menu)
292         logo->draw( 160, 30);
293
294       white_small_text->draw(" SuperTux " VERSION "\n"
295                              "Copyright (c) 2003 SuperTux Devel Team\n"
296                              "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"
297                              "are welcome to redistribute it under certain conditions; see the file COPYING\n"
298                              "for details.\n",
299                              0, 420, 0);
300
301       /* Don't draw menu, if quit is true */
302       if(!done)
303         {
304           Menu::current()->action();
305           Menu::current()->draw();
306         }
307
308       if(Menu::current() == main_menu)
309         {
310           switch (main_menu->check())
311             {
312             case 0:
313               // Start Game, ie. goto the slots menu
314               update_load_save_game_menu(load_game_menu);
315               break;
316             case 1:
317               // Contrib Menu
318               update_contrib_menu();
319               break;
320             case 3:
321               done = true;
322               done = leveleditor(1);
323               Menu::set_current(main_menu);
324               break;
325             case 4:
326               display_credits();
327               break;
328             case 5:
329               done = true;
330               break;
331             }
332         }
333       else if(Menu::current() == options_menu)
334         {
335           process_options_menu();
336         }
337       else if(Menu::current() == load_game_menu)
338         {
339           if (process_load_game_menu())
340             {
341               // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
342               // reset tux
343               scroll_x = 0;
344               //titletux.level_begin();
345               update_time = st_get_ticks();
346             }
347         }
348       else if(Menu::current() == contrib_menu)
349         {
350           check_contrib_menu();
351         }
352       else if (Menu::current() == contrib_subset_menu)
353         {
354           check_contrib_subset_menu();
355         }
356
357       mouse_cursor->draw();
358       
359       flipscreen();
360
361       /* Set the time of the last update and the time of the current update */
362       last_update_time = update_time;
363       update_time = st_get_ticks();
364
365       /* Pause: */
366       frame++;
367       SDL_Delay(25);
368
369     }
370   /* Free surfaces: */
371
372   delete bkg_title;
373   delete logo;
374   
375   /* Return to main! */
376   return done;
377 }
378
379 #define MAX_VEL 10
380 #define SPEED   1
381 #define SCROLL  60
382
383 void display_credits()
384 {
385   int done;
386   int scroll, speed;
387   Timer timer;
388   int n,d;
389   int length;
390   FILE* fi;
391   char temp[1024];
392   string_list_type names;
393   char filename[1024];
394   string_list_init(&names);
395   sprintf(filename,"%s/CREDITS", datadir.c_str());
396   if((fi = fopen(filename,"r")) != NULL)
397     {
398       while(fgets(temp, sizeof(temp), fi) != NULL)
399         {
400           temp[strlen(temp)-1]='\0';
401           string_list_add_item(&names,temp);
402         }
403       fclose(fi);
404     }
405   else
406     {
407       string_list_add_item(&names,"Credits were not found!");
408       string_list_add_item(&names,"Shame on the guy, who");
409       string_list_add_item(&names,"forgot to include them");
410       string_list_add_item(&names,"in your SuperTux distribution.");
411     }
412
413
414   timer.init(SDL_GetTicks());
415   timer.start(50);
416
417   scroll = 0;
418   speed = 2;
419   done = 0;
420
421   n = d = 0;
422
423   length = names.num_items;
424
425   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
426
427   while(done == 0)
428     {
429       /* in case of input, exit */
430       while(SDL_PollEvent(&event))
431         switch(event.type)
432           {
433           case SDL_KEYDOWN:
434             switch(event.key.keysym.sym)
435               {
436               case SDLK_UP:
437                 speed -= SPEED;
438                 break;
439               case SDLK_DOWN:
440                 speed += SPEED;
441                 break;
442               case SDLK_SPACE:
443               case SDLK_RETURN:
444                 if(speed >= 0)
445                   scroll += SCROLL;
446                 break;
447               case SDLK_ESCAPE:
448                 done = 1;
449                 break;
450               default:
451                 break;
452               }
453             break;
454           case SDL_QUIT:
455             done = 1;
456             break;
457           default:
458             break;
459           }
460
461       if(speed > MAX_VEL)
462         speed = MAX_VEL;
463       else if(speed < -MAX_VEL)
464         speed = -MAX_VEL;
465
466       /* draw the credits */
467
468       draw_background();
469
470       white_big_text->drawf("- Credits -", 0, screen->h-scroll, A_HMIDDLE, A_TOP, 2);
471
472       for(i = 0, n = 0, d = 0; i < length; i++,n++,d++)
473         {
474           if(names.item[i] == "")
475             n--;
476           else
477             {
478               if(names.item[i][0] == ' ')
479                 white_small_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll-10, A_HMIDDLE, A_TOP, 1);
480               else if(names.item[i][0] == '     ')
481                 white_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 1);
482               else if(names.item[i+1][0] == '-' || names.item[i][0] == '-')
483                 white_big_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 3);
484               else
485                 blue_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 1);
486             }
487         }
488
489       flipscreen();
490
491       if(60+screen->h+(n*18)+(d*18)-scroll < 0 && 20+60+screen->h+(n*18)+(d*18)-scroll < 0)
492         done = 1;
493
494       scroll += speed;
495       if(scroll < 0)
496         scroll = 0;
497
498       SDL_Delay(35);
499
500       if(timer.get_left() < 0)
501         {
502           frame++;
503           timer.start(50);
504         }
505     }
506   string_list_free(&names);
507
508   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
509   Menu::set_current(main_menu);
510 }