- removed a few menu variables
[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         menu_process_current();
304
305       if(Menu::current() == main_menu)
306         {
307           switch (main_menu->check())
308             {
309             case 0:
310               // Start Game, ie. goto the slots menu
311               update_load_save_game_menu(load_game_menu);
312               break;
313             case 1:
314               // Contrib Menu
315               update_contrib_menu();
316               break;
317             case 3:
318               done = true;
319               done = leveleditor(1);
320               Menu::set_current(main_menu);
321               break;
322             case 4:
323               display_credits();
324               break;
325             case 5:
326               done = true;
327               break;
328             }
329         }
330       else if(Menu::current() == options_menu)
331         {
332           process_options_menu();
333         }
334       else if(Menu::current() == load_game_menu)
335         {
336           if (process_load_game_menu())
337             {
338               // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
339               // reset tux
340               scroll_x = 0;
341               //titletux.level_begin();
342               update_time = st_get_ticks();
343             }
344         }
345       else if(Menu::current() == contrib_menu)
346         {
347           check_contrib_menu();
348         }
349       else if (Menu::current() == contrib_subset_menu)
350         {
351           check_contrib_subset_menu();
352         }
353
354       mouse_cursor->draw();
355       
356       flipscreen();
357
358       /* Set the time of the last update and the time of the current update */
359       last_update_time = update_time;
360       update_time = st_get_ticks();
361
362       /* Pause: */
363       frame++;
364       SDL_Delay(25);
365
366     }
367   /* Free surfaces: */
368
369   delete bkg_title;
370   delete logo;
371   
372   /* Return to main! */
373   return done;
374 }
375
376 #define MAX_VEL 10
377 #define SPEED   1
378 #define SCROLL  60
379
380 void display_credits()
381 {
382   int done;
383   int scroll, speed;
384   Timer timer;
385   int n,d;
386   int length;
387   FILE* fi;
388   char temp[1024];
389   string_list_type names;
390   char filename[1024];
391   string_list_init(&names);
392   sprintf(filename,"%s/CREDITS", datadir.c_str());
393   if((fi = fopen(filename,"r")) != NULL)
394     {
395       while(fgets(temp, sizeof(temp), fi) != NULL)
396         {
397           temp[strlen(temp)-1]='\0';
398           string_list_add_item(&names,temp);
399         }
400       fclose(fi);
401     }
402   else
403     {
404       string_list_add_item(&names,"Credits were not found!");
405       string_list_add_item(&names,"Shame on the guy, who");
406       string_list_add_item(&names,"forgot to include them");
407       string_list_add_item(&names,"in your SuperTux distribution.");
408     }
409
410
411   timer.init(SDL_GetTicks());
412   timer.start(50);
413
414   scroll = 0;
415   speed = 2;
416   done = 0;
417
418   n = d = 0;
419
420   length = names.num_items;
421
422   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
423
424   while(done == 0)
425     {
426       /* in case of input, exit */
427       while(SDL_PollEvent(&event))
428         switch(event.type)
429           {
430           case SDL_KEYDOWN:
431             switch(event.key.keysym.sym)
432               {
433               case SDLK_UP:
434                 speed -= SPEED;
435                 break;
436               case SDLK_DOWN:
437                 speed += SPEED;
438                 break;
439               case SDLK_SPACE:
440               case SDLK_RETURN:
441                 if(speed >= 0)
442                   scroll += SCROLL;
443                 break;
444               case SDLK_ESCAPE:
445                 done = 1;
446                 break;
447               default:
448                 break;
449               }
450             break;
451           case SDL_QUIT:
452             done = 1;
453             break;
454           default:
455             break;
456           }
457
458       if(speed > MAX_VEL)
459         speed = MAX_VEL;
460       else if(speed < -MAX_VEL)
461         speed = -MAX_VEL;
462
463       /* draw the credits */
464
465       draw_background();
466
467       white_big_text->drawf("- Credits -", 0, screen->h-scroll, A_HMIDDLE, A_TOP, 2);
468
469       for(i = 0, n = 0, d = 0; i < length; i++,n++,d++)
470         {
471           if(names.item[i] == "")
472             n--;
473           else
474             {
475               if(names.item[i][0] == ' ')
476                 white_small_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll-10, A_HMIDDLE, A_TOP, 1);
477               else if(names.item[i][0] == '     ')
478                 white_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 1);
479               else if(names.item[i+1][0] == '-' || names.item[i][0] == '-')
480                 white_big_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 3);
481               else
482                 blue_text->drawf(names.item[i], 0, 60+screen->h+(n*18)+(d*18)-scroll, A_HMIDDLE, A_TOP, 1);
483             }
484         }
485
486       flipscreen();
487
488       if(60+screen->h+(n*18)+(d*18)-scroll < 0 && 20+60+screen->h+(n*18)+(d*18)-scroll < 0)
489         done = 1;
490
491       scroll += speed;
492       if(scroll < 0)
493         scroll = 0;
494
495       SDL_Delay(35);
496
497       if(timer.get_left() < 0)
498         {
499           frame++;
500           timer.start(50);
501         }
502     }
503   string_list_free(&names);
504
505   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
506   Menu::set_current(main_menu);
507 }