- moved joystick mappings into a struct
[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   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          // FIXME: QUIT signal should be handled more generic, not locally
269           if (event.type == SDL_QUIT)
270             Menu::set_current(0);
271         }
272
273       /* Draw the background: */
274       draw_background();
275       draw_demo(&session, frame_ratio);
276       
277       if (Menu::current() == main_menu)
278         logo->draw( 160, 30);
279
280       white_small_text->draw(" SuperTux " VERSION "\n"
281                              "Copyright (c) 2003 SuperTux Devel Team\n"
282                              "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"
283                              "are welcome to redistribute it under certain conditions; see the file COPYING\n"
284                              "for details.\n",
285                              0, 420, 0);
286
287       /* Don't draw menu, if quit is true */
288       Menu* menu = Menu::current();
289       if(menu)
290         {
291           menu->draw();
292           menu->action();
293         
294           if(menu == main_menu)
295             {
296               switch (main_menu->check())
297                 {
298                 case 0:
299                   // Start Game, ie. goto the slots menu
300                   update_load_save_game_menu(load_game_menu);
301                   break;
302                 case 1:
303                   // Contrib Menu
304                   puts("Entering contrib menu");
305                   generate_contrib_menu();
306                   break;
307                 case 3:
308                   leveleditor(1);
309                   Menu::set_current(main_menu);
310                   break;
311                 case 4:
312                   display_credits();
313                   Menu::set_current(main_menu);
314                   break;
315                 case 6:
316                   Menu::set_current(0);
317                   break;
318                 }
319             }
320           else if(menu == options_menu)
321             {
322               process_options_menu();
323             }
324           else if(menu == load_game_menu)
325             {
326               if (process_load_game_menu())
327                 {
328                   // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
329                   // reset tux
330                   scroll_x = 0;
331                   //titletux.level_begin();
332                   update_time = st_get_ticks();
333                 }
334             }
335           else if(menu == contrib_menu)
336             {
337               check_contrib_menu();
338             }
339           else if (menu == contrib_subset_menu)
340             {
341               check_contrib_subset_menu();
342             }
343         }
344
345       mouse_cursor->draw();
346       
347       flipscreen();
348
349       /* Set the time of the last update and the time of the current update */
350       last_update_time = update_time;
351       update_time = st_get_ticks();
352
353       /* Pause: */
354       frame++;
355       SDL_Delay(25);
356
357     }
358   /* Free surfaces: */
359
360   delete bkg_title;
361   delete logo;
362 }
363
364 #define MAX_VEL 10
365 #define SPEED   1
366 #define SCROLL  60
367 #define ITEMS_SPACE 4
368
369 void display_credits()
370 {
371   int done;
372   int scroll, speed;
373   int y;
374   Timer timer;
375   int length;
376   FILE* fi;
377   char temp[1024];
378   string_list_type names;
379   char filename[1024];
380   string_list_init(&names);
381   sprintf(filename,"%s/CREDITS", datadir.c_str());
382   if((fi = fopen(filename,"r")) != NULL)
383     {
384       while(fgets(temp, sizeof(temp), fi) != NULL)
385         {
386           temp[strlen(temp)-1]='\0';
387           string_list_add_item(&names,temp);
388         }
389       fclose(fi);
390     }
391   else
392     {
393       string_list_add_item(&names,"Credits were not found!");
394       string_list_add_item(&names,"Shame on the guy, who");
395       string_list_add_item(&names,"forgot to include them");
396       string_list_add_item(&names,"in your SuperTux distribution.");
397     }
398
399
400   timer.init(SDL_GetTicks());
401   timer.start(50);
402
403   scroll = 0;
404   speed = 2;
405   done = 0;
406
407   length = names.num_items;
408
409   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
410
411   while(done == 0)
412     {
413       /* in case of input, exit */
414       SDL_Event event;
415       while(SDL_PollEvent(&event))
416         switch(event.type)
417           {
418           case SDL_KEYDOWN:
419             switch(event.key.keysym.sym)
420               {
421               case SDLK_UP:
422                 speed -= SPEED;
423                 break;
424               case SDLK_DOWN:
425                 speed += SPEED;
426                 break;
427               case SDLK_SPACE:
428               case SDLK_RETURN:
429                 if(speed >= 0)
430                   scroll += SCROLL;
431                 break;
432               case SDLK_ESCAPE:
433                 done = 1;
434                 break;
435               default:
436                 break;
437               }
438             break;
439           case SDL_QUIT:
440             done = 1;
441             break;
442           default:
443             break;
444           }
445
446       if(speed > MAX_VEL)
447         speed = MAX_VEL;
448       else if(speed < -MAX_VEL)
449         speed = -MAX_VEL;
450
451       /* draw the credits */
452
453       draw_background();
454
455       white_big_text->drawf("- Credits -", 0, screen->h-scroll, A_HMIDDLE, A_TOP, 2);
456
457       y = 0;
458       for(int i = 0; i < length; i++)
459         {
460         switch(names.item[i][0])
461           {
462           case ' ':
463             white_small_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
464             y += white_small_text->h+ITEMS_SPACE;
465             break;
466           case '        ':
467             white_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
468             y += white_text->h+ITEMS_SPACE;
469             break;
470           case '-':
471             white_big_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 3);
472             y += white_big_text->h+ITEMS_SPACE;
473             break;
474           default:
475             blue_text->drawf(names.item[i], 0, 60+screen->h+y-scroll, A_HMIDDLE, A_TOP, 1);
476             y += blue_text->h+ITEMS_SPACE;
477             break;
478           }
479         }
480
481       flipscreen();
482
483       if(60+screen->h+y-scroll < 0 && 20+60+screen->h+y-scroll < 0)
484         done = 1;
485
486       scroll += speed;
487       if(scroll < 0)
488         scroll = 0;
489
490       SDL_Delay(35);
491
492       if(timer.get_left() < 0)
493         {
494           frame++;
495           timer.start(50);
496         }
497     }
498   string_list_free(&names);
499
500   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
501   Menu::set_current(main_menu);
502 }