- changed way badguy removal is handled (ie. with a flag now)
[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           halt_music();
143           GameSession session(current_contrib_subset, index, ST_GL_PLAY);
144           session.run();
145           Menu::set_current(main_menu);
146         }
147     }  
148 }
149
150 void draw_background()
151 {
152   /* Draw the title background: */
153
154   bkg_title->draw_bg();
155 }
156
157 void draw_demo(GameSession* session, double frame_ratio)
158 {
159   World::set_current(session->get_world());
160   //World* world  = session->get_world();
161   Level* plevel = session->get_level();
162   Player* tux = session->get_world()->get_tux();
163
164   session->get_world()->play_music(LEVEL_MUSIC);
165   
166   /* FIXME:
167   // update particle systems
168   std::vector<ParticleSystem*>::iterator p;
169   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
170     {
171       (*p)->simulate(frame_ratio);
172     }
173
174   // Draw particle systems (background)
175   for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
176     {
177       (*p)->draw(scroll_x, 0, 0);
178     }
179   */
180
181   // Draw interactive tiles:
182   for (int y = 0; y < 15; ++y)
183     {
184       for (int x = 0; x < 21; ++x)
185         {
186           Tile::draw(32*x - fmodf(scroll_x, 32), y * 32,
187                      plevel->ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]);
188         }
189     }
190
191   global_frame_counter++;
192   tux->key_event((SDLKey) keymap.right,DOWN);
193   
194   if(random_timer.check())
195     {
196       if(walking)
197         tux->key_event((SDLKey) keymap.jump,UP);
198       else
199         tux->key_event((SDLKey) keymap.jump,DOWN);
200     }
201   else
202     {
203       random_timer.start(rand() % 3000 + 3000);
204       walking = !walking;
205     }
206   
207   // Wrap around at the end of the level back to the beginnig
208   if(plevel->width * 32 - 320 < tux->base.x)
209     {
210       tux->base.x = tux->base.x - (plevel->width * 32 - 640);
211       scroll_x = tux->base.x - 320;
212     }
213
214   float last_tux_x_pos = tux->base.x;
215   tux->action(frame_ratio);
216
217   // Jump if tux stays in the same position for one loop, ie. if he is
218   // stuck behind a wall
219   if (last_tux_x_pos == tux->base.x)
220     walking = false;
221
222   tux->draw();
223 }
224
225 /* --- TITLE SCREEN --- */
226 void title(void)
227 {
228   st_subset subset;
229   random_timer.init(true);
230
231   walking = true;
232
233   st_pause_ticks_init();
234
235   GameSession session(datadir + "/levels/misc/menu.stl", 0, ST_GL_DEMO_GAME);
236
237   clearscreen(0, 0, 0);
238   updatescreen();
239
240   /* Load images: */
241   bkg_title = new Surface(datadir + "/images/title/background.jpg", IGNORE_ALPHA);
242   logo = new Surface(datadir + "/images/title/logo.png", USE_ALPHA);
243   img_choose_subset = new Surface(datadir + "/images/status/choose-level-subset.png", USE_ALPHA);
244
245   /* --- Main title loop: --- */
246   frame = 0;
247
248   /* Draw the title background: */
249   bkg_title->draw_bg();
250
251   update_time = st_get_ticks();
252   random_timer.start(rand() % 2000 + 2000);
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   /* 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 #define ITEMS_SPACE 4
371
372 void display_credits()
373 {
374   int done;
375   int scroll, speed;
376   int y;
377   Timer timer;
378   int length;
379   FILE* fi;
380   char temp[1024];
381   string_list_type names;
382   char filename[1024];
383   string_list_init(&names);
384   sprintf(filename,"%s/CREDITS", datadir.c_str());
385   if((fi = fopen(filename,"r")) != NULL)
386     {
387       while(fgets(temp, sizeof(temp), fi) != NULL)
388         {
389           temp[strlen(temp)-1]='\0';
390           string_list_add_item(&names,temp);
391         }
392       fclose(fi);
393     }
394   else
395     {
396       string_list_add_item(&names,"Credits were not found!");
397       string_list_add_item(&names,"Shame on the guy, who");
398       string_list_add_item(&names,"forgot to include them");
399       string_list_add_item(&names,"in your SuperTux distribution.");
400     }
401
402
403   timer.init(SDL_GetTicks());
404   timer.start(50);
405
406   scroll = 0;
407   speed = 2;
408   done = 0;
409
410   length = names.num_items;
411
412   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
413
414   while(done == 0)
415     {
416       /* in case of input, exit */
417       SDL_Event event;
418       while(SDL_PollEvent(&event))
419         switch(event.type)
420           {
421           case SDL_KEYDOWN:
422             switch(event.key.keysym.sym)
423               {
424               case SDLK_UP:
425                 speed -= SPEED;
426                 break;
427               case SDLK_DOWN:
428                 speed += SPEED;
429                 break;
430               case SDLK_SPACE:
431               case SDLK_RETURN:
432                 if(speed >= 0)
433                   scroll += SCROLL;
434                 break;
435               case SDLK_ESCAPE:
436                 done = 1;
437                 break;
438               default:
439                 break;
440               }
441             break;
442           case SDL_QUIT:
443             done = 1;
444             break;
445           default:
446             break;
447           }
448
449       if(speed > MAX_VEL)
450         speed = MAX_VEL;
451       else if(speed < -MAX_VEL)
452         speed = -MAX_VEL;
453
454       /* draw the credits */
455
456       draw_background();
457
458       white_big_text->drawf("- Credits -", 0, screen->h-scroll, A_HMIDDLE, A_TOP, 2);
459
460       y = 0;
461       for(int i = 0; i < length; i++)
462         {
463         switch(names.item[i][0])
464           {
465           case ' ':
466             white_small_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
467             y += white_small_text->h+ITEMS_SPACE;
468             break;
469           case '        ':
470             white_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
471             y += white_text->h+ITEMS_SPACE;
472             break;
473           case '-':
474             white_big_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 3);
475             y += white_big_text->h+ITEMS_SPACE;
476             break;
477           default:
478             blue_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
479             y += blue_text->h+ITEMS_SPACE;
480             break;
481           }
482         }
483
484       flipscreen();
485
486       if(60+screen->h+y-scroll < 0 && 20+60+screen->h+y-scroll < 0)
487         done = 1;
488
489       scroll += speed;
490       if(scroll < 0)
491         scroll = 0;
492
493       SDL_Delay(35);
494
495       if(timer.get_left() < 0)
496         {
497           frame++;
498           timer.start(50);
499         }
500     }
501   string_list_free(&names);
502
503   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
504   Menu::set_current(main_menu);
505 }