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