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