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