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