- bullet tweaks
[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       // Calculate the movement-factor
257       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
258       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
259         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
260       /* Lower the frame_ratio that Tux doesn't jump to hectically throught the demo. */
261       frame_ratio /= 2;
262
263       SDL_Event event;
264       while (SDL_PollEvent(&event))
265         {
266           if (Menu::current())
267             {
268               Menu::current()->event(event);
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       /* Draw the background: */
276       draw_background();
277       draw_demo(&session, frame_ratio);
278       
279       if (Menu::current() == main_menu)
280         logo->draw( 160, 30);
281
282       white_small_text->draw(" SuperTux " VERSION "\n"
283                              "Copyright (c) 2003 SuperTux Devel Team\n"
284                              "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"
285                              "are welcome to redistribute it under certain conditions; see the file COPYING\n"
286                              "for details.\n",
287                              0, 420, 0);
288
289       /* Don't draw menu, if quit is true */
290       Menu* menu = Menu::current();
291       if(menu)
292         {
293           menu->draw();
294           menu->action();
295         
296           if(menu == main_menu)
297             {
298               switch (main_menu->check())
299                 {
300                 case MNID_STARTGAME:
301                   // Start Game, ie. goto the slots menu
302                   update_load_save_game_menu(load_game_menu);
303                   break;
304                 case MNID_CONTRIB:
305                   // Contrib Menu
306                   puts("Entering contrib menu");
307                   generate_contrib_menu();
308                   break;
309                 case MNID_LEVELEDITOR:
310                   leveleditor(1);
311                   Menu::set_current(main_menu);
312                   break;
313                 case MNID_CREDITS:
314                   display_text_file("CREDITS");
315                   Menu::set_current(main_menu);
316                   break;
317                 case MNID_QUITMAINMENU:
318                   Menu::set_current(0);
319                   break;
320                 }
321             }
322           else if(menu == options_menu)
323             {
324               process_options_menu();
325             }
326           else if(menu == load_game_menu)
327             {
328               if (process_load_game_menu())
329                 {
330                   // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
331                   // reset tux
332                   scroll_x = 0;
333                   //titletux.level_begin();
334                   update_time = st_get_ticks();
335                 }
336             }
337           else if(menu == contrib_menu)
338             {
339               check_contrib_menu();
340             }
341           else if (menu == contrib_subset_menu)
342             {
343               check_contrib_subset_menu();
344             }
345         }
346
347       mouse_cursor->draw();
348       
349       flipscreen();
350
351       /* Set the time of the last update and the time of the current update */
352       last_update_time = update_time;
353       update_time = st_get_ticks();
354
355       /* Pause: */
356       frame++;
357       SDL_Delay(25);
358     }
359   /* Free surfaces: */
360
361   delete bkg_title;
362   delete logo;
363 }
364
365 #define MAX_VEL 10
366 #define SPEED   1
367 #define SCROLL  60
368 #define ITEMS_SPACE 4
369
370 void display_text_file(char *file)
371 {
372   int done;
373   int scroll, speed;
374   int y;
375   Timer timer;
376   int length;
377   FILE* fi;
378   char temp[1024];
379   string_list_type names;
380   char filename[1024];
381   string_list_init(&names);
382   sprintf(filename,"%s/%s", datadir.c_str(), file);
383   if((fi = fopen(filename,"r")) != NULL)
384     {
385       while(fgets(temp, sizeof(temp), fi) != NULL)
386         {
387           temp[strlen(temp)-1]='\0';
388           string_list_add_item(&names,temp);
389         }
390       fclose(fi);
391     }
392   else
393     {
394       string_list_add_item(&names,"Credits were not found!");
395       string_list_add_item(&names,"Shame on the guy, who");
396       string_list_add_item(&names,"forgot to include them");
397       string_list_add_item(&names,"in your SuperTux distribution.");
398     }
399
400
401   timer.init(SDL_GetTicks());
402   timer.start(50);
403
404   scroll = 0;
405   speed = 2;
406   done = 0;
407
408   length = names.num_items;
409
410   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
411
412   while(done == 0)
413     {
414       /* in case of input, exit */
415       SDL_Event event;
416       while(SDL_PollEvent(&event))
417         switch(event.type)
418           {
419           case SDL_KEYDOWN:
420             switch(event.key.keysym.sym)
421               {
422               case SDLK_UP:
423                 speed -= SPEED;
424                 break;
425               case SDLK_DOWN:
426                 speed += SPEED;
427                 break;
428               case SDLK_SPACE:
429               case SDLK_RETURN:
430                 if(speed >= 0)
431                   scroll += SCROLL;
432                 break;
433               case SDLK_ESCAPE:
434                 done = 1;
435                 break;
436               default:
437                 break;
438               }
439             break;
440           case SDL_QUIT:
441             done = 1;
442             break;
443           default:
444             break;
445           }
446
447       if(speed > MAX_VEL)
448         speed = MAX_VEL;
449       else if(speed < -MAX_VEL)
450         speed = -MAX_VEL;
451
452       /* draw the credits */
453
454       draw_background();
455
456       if (strcmp(file, "CREDITS") == 0)
457         white_big_text->drawf("- SuperTux " VERSION " -", 
458                               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 }