33a28e24f02bb09526fb5f78bbc11bdad2c028b7
[supertux.git] / src / title.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 // 
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 //  02111-1307, USA.
21
22 #include <iostream>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <SDL.h>
29 #include <SDL_image.h>
30
31 #ifndef WIN32
32 #include <sys/types.h>
33 #include <ctype.h>
34 #endif
35
36 #include "defines.h"
37 #include "globals.h"
38 #include "title.h"
39 #include "screen.h"
40 #include "high_scores.h"
41 #include "menu.h"
42 #include "texture.h"
43 #include "timer.h"
44 #include "setup.h"
45 #include "level.h"
46 #include "gameloop.h"
47 #include "leveleditor.h"
48 #include "scene.h"
49 #include "player.h"
50 #include "math.h"
51 #include "tile.h"
52 #include "resources.h"
53
54 static Surface* bkg_title;
55 static Surface* logo;
56 static Surface* img_choose_subset;
57
58 static bool walking;
59 static Timer random_timer;
60
61 static int frame;
62 static unsigned int last_update_time;
63 static unsigned int update_time;
64
65 void display_credits();
66
67 std::vector<st_subset> contrib_subsets;
68 std::string current_contrib_subset;
69
70 void generate_contrib_menu()
71 {
72   string_list_type level_subsets = dsubdirs("/levels", "info");
73
74   contrib_menu->clear();
75   contrib_menu->additem(MN_LABEL,"Contrib Levels",0,0);
76   contrib_menu->additem(MN_HL,"",0,0);
77
78   for (int i = 0; i < level_subsets.num_items; ++i)
79     {
80       st_subset subset;
81       subset.load(level_subsets.item[i]);
82       contrib_menu->additem(MN_GOTO, subset.title.c_str(), i, contrib_subset_menu);
83       contrib_subsets.push_back(subset);
84     }
85
86   contrib_menu->additem(MN_HL,"",0,0);
87   contrib_menu->additem(MN_BACK,"Back",0,0);
88
89   string_list_free(&level_subsets);
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       if (contrib_subset_menu->get_item(index).kind == MN_ACTION)
139         {
140           index -= 1; // FIXME: Hack
141           std::cout << "Sarting level: " << index << std::endl;
142           GameSession session(current_contrib_subset, index, ST_GL_PLAY);
143           session.run();
144           Menu::set_current(main_menu);
145         }
146     }  
147 }
148
149 void draw_background()
150 {
151   /* Draw the title background: */
152
153   bkg_title->draw_bg();
154 }
155
156 void draw_demo(GameSession* session, double frame_ratio)
157 {
158   World::set_current(session->get_world());
159   //World* world  = session->get_world();
160   Level* plevel = session->get_level();
161   Player* tux = session->get_world()->get_tux();
162   
163   /* FIXME:
164   // update particle systems
165   std::vector<ParticleSystem*>::iterator p;
166   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
167     {
168       (*p)->simulate(frame_ratio);
169     }
170
171   // Draw particle systems (background)
172   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
173     {
174       (*p)->draw(scroll_x, 0, 0);
175     }
176   */
177
178   // Draw interactive tiles:
179   for (int y = 0; y < 15; ++y)
180     {
181       for (int x = 0; x < 21; ++x)
182         {
183           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
184                      plevel->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
185         }
186     }
187
188   global_frame_counter++;
189   tux->key_event(SDLK_RIGHT,DOWN);
190   
191   if(random_timer.check())
192     {
193       if(walking)
194         tux->key_event(SDLK_UP,UP);
195       else
196         tux->key_event(SDLK_UP,DOWN);
197     }
198   else
199     {
200       random_timer.start(rand() % 3000 + 3000);
201       walking = !walking;
202     }
203   
204   // Wrap around at the end of the level back to the beginnig
205   if(plevel->width * 32 - 320 < tux->base.x)
206     {
207       tux->base.x = tux->base.x - (plevel->width * 32 - 640);
208       scroll_x = tux->base.x - 320;
209     }
210
211   float last_tux_x_pos = tux->base.x;
212   tux->action(frame_ratio);
213
214   // Jump if tux stays in the same position for one loop, ie. if he is
215   // stuck behind a wall
216   if (last_tux_x_pos == tux->base.x)
217     walking = false;
218
219   tux->draw();
220 }
221
222 /* --- TITLE SCREEN --- */
223 void title(void)
224 {
225   st_subset subset;
226   random_timer.init(true);
227
228   walking = true;
229
230   st_pause_ticks_init();
231
232   GameSession session(datadir + "/levels/misc/menu.stl", 0, ST_GL_DEMO_GAME);
233
234   clearscreen(0, 0, 0);
235   updatescreen();
236
237   /* Load images: */
238   bkg_title = new Surface(datadir + "/images/title/background.jpg", IGNORE_ALPHA);
239   logo = new Surface(datadir + "/images/title/logo.png", USE_ALPHA);
240   img_choose_subset = new Surface(datadir + "/images/status/choose-level-subset.png", USE_ALPHA);
241
242   /* --- Main title loop: --- */
243   frame = 0;
244
245   /* Draw the title background: */
246   bkg_title->draw_bg();
247
248   update_time = st_get_ticks();
249   random_timer.start(rand() % 2000 + 2000);
250
251   Mix_Music* music = load_song(datadir + "/music/theme.mod");
252   play_music(music);
253
254   Menu::set_current(main_menu);
255   while (Menu::current())
256     {
257       // Calculate the movement-factor
258       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
259       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
260         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
261       /* Lower the frame_ratio that Tux doesn't jump to hectically throught the demo. */
262       frame_ratio /= 2;
263
264       SDL_Event event;
265       while (SDL_PollEvent(&event))
266         {
267           if (Menu::current())
268             {
269               Menu::current()->event(event);
270             }
271          // FIXME: QUIT signal should be handled more generic, not locally
272           if (event.type == SDL_QUIT)
273             Menu::set_current(0);
274         }
275
276       /* Draw the background: */
277       draw_background();
278       draw_demo(&session, frame_ratio);
279       
280       if (Menu::current() == main_menu)
281         logo->draw( 160, 30);
282
283       white_small_text->draw(" SuperTux " VERSION "\n"
284                              "Copyright (c) 2003 SuperTux Devel Team\n"
285                              "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"
286                              "are welcome to redistribute it under certain conditions; see the file COPYING\n"
287                              "for details.\n",
288                              0, 420, 0);
289
290       /* Don't draw menu, if quit is true */
291       Menu* menu = Menu::current();
292       if(menu)
293         {
294           menu->draw();
295           menu->action();
296         
297           if(menu == main_menu)
298             {
299               switch (main_menu->check())
300                 {
301                 case 0:
302                   // Start Game, ie. goto the slots menu
303                   update_load_save_game_menu(load_game_menu);
304                   break;
305                 case 1:
306                   // Contrib Menu
307                   puts("Entering contrib menu");
308                   generate_contrib_menu();
309                   break;
310                 case 3:
311                   halt_music();
312                   leveleditor(1);
313                   Menu::set_current(main_menu);
314                   break;
315                 case 4:
316                   display_credits();
317                   Menu::set_current(main_menu);
318                   break;
319                 case 6:
320                   Menu::set_current(0);
321                   break;
322                 }
323             }
324           else if(menu == options_menu)
325             {
326               process_options_menu();
327             }
328           else if(menu == load_game_menu)
329             {
330               if (process_load_game_menu())
331                 {
332                   // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
333                   // reset tux
334                   scroll_x = 0;
335                   //titletux.level_begin();
336                   update_time = st_get_ticks();
337                 }
338             }
339           else if(menu == contrib_menu)
340             {
341               check_contrib_menu();
342             }
343           else if (menu == contrib_subset_menu)
344             {
345               check_contrib_subset_menu();
346             }
347         }
348
349       mouse_cursor->draw();
350       
351       flipscreen();
352
353       /* Set the time of the last update and the time of the current update */
354       last_update_time = update_time;
355       update_time = st_get_ticks();
356
357       /* Pause: */
358       frame++;
359       SDL_Delay(25);
360
361       play_music(music);
362     }
363   /* Free surfaces: */
364
365   delete bkg_title;
366   delete logo;
367   free_music(music);
368 }
369
370 #define MAX_VEL 10
371 #define SPEED   1
372 #define SCROLL  60
373 #define ITEMS_SPACE 4
374
375 void display_credits()
376 {
377   int done;
378   int scroll, speed;
379   int y;
380   Timer timer;
381   int length;
382   FILE* fi;
383   char temp[1024];
384   string_list_type names;
385   char filename[1024];
386   string_list_init(&names);
387   sprintf(filename,"%s/CREDITS", datadir.c_str());
388   if((fi = fopen(filename,"r")) != NULL)
389     {
390       while(fgets(temp, sizeof(temp), fi) != NULL)
391         {
392           temp[strlen(temp)-1]='\0';
393           string_list_add_item(&names,temp);
394         }
395       fclose(fi);
396     }
397   else
398     {
399       string_list_add_item(&names,"Credits were not found!");
400       string_list_add_item(&names,"Shame on the guy, who");
401       string_list_add_item(&names,"forgot to include them");
402       string_list_add_item(&names,"in your SuperTux distribution.");
403     }
404
405
406   timer.init(SDL_GetTicks());
407   timer.start(50);
408
409   scroll = 0;
410   speed = 2;
411   done = 0;
412
413   length = names.num_items;
414
415   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
416
417   while(done == 0)
418     {
419       /* in case of input, exit */
420       SDL_Event event;
421       while(SDL_PollEvent(&event))
422         switch(event.type)
423           {
424           case SDL_KEYDOWN:
425             switch(event.key.keysym.sym)
426               {
427               case SDLK_UP:
428                 speed -= SPEED;
429                 break;
430               case SDLK_DOWN:
431                 speed += SPEED;
432                 break;
433               case SDLK_SPACE:
434               case SDLK_RETURN:
435                 if(speed >= 0)
436                   scroll += SCROLL;
437                 break;
438               case SDLK_ESCAPE:
439                 done = 1;
440                 break;
441               default:
442                 break;
443               }
444             break;
445           case SDL_QUIT:
446             done = 1;
447             break;
448           default:
449             break;
450           }
451
452       if(speed > MAX_VEL)
453         speed = MAX_VEL;
454       else if(speed < -MAX_VEL)
455         speed = -MAX_VEL;
456
457       /* draw the credits */
458
459       draw_background();
460
461       white_big_text->drawf("- Credits -", 0, screen->h-scroll, A_HMIDDLE, A_TOP, 2);
462
463       y = 0;
464       for(int i = 0; i < length; i++)
465         {
466         switch(names.item[i][0])
467           {
468           case ' ':
469             white_small_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
470             y += white_small_text->h+ITEMS_SPACE;
471             break;
472           case '        ':
473             white_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
474             y += white_text->h+ITEMS_SPACE;
475             break;
476           case '-':
477             white_big_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 3);
478             y += white_big_text->h+ITEMS_SPACE;
479             break;
480           default:
481             blue_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
482             y += blue_text->h+ITEMS_SPACE;
483             break;
484           }
485         }
486
487       flipscreen();
488
489       if(60+screen->h+y-scroll < 0 && 20+60+screen->h+y-scroll < 0)
490         done = 1;
491
492       scroll += speed;
493       if(scroll < 0)
494         scroll = 0;
495
496       SDL_Delay(35);
497
498       if(timer.get_left() < 0)
499         {
500           frame++;
501           timer.start(50);
502         }
503     }
504   string_list_free(&names);
505
506   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
507   Menu::set_current(main_menu);
508 }