1 //  $Id: title.cpp 2702 2005-07-08 12:18:16Z wansti $
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 #include "worldmap.h"
54 
55 static Surface* bkg_title;
56 static Surface* logo;
57 static Surface* img_choose_subset;
58 
59 static bool walking;
60 static Timer random_timer;
61 
62 static int frame;
63 static unsigned int last_update_time;
64 static unsigned int update_time;
65 
66 static std::vector<LevelSubset*> contrib_subsets;
67 static std::string current_contrib_subset;
68 
69 static string_list_type worldmap_list;
70 
free_contrib_menu()71 void free_contrib_menu()
72 {
73   for(std::vector<LevelSubset*>::iterator i = contrib_subsets.begin();
74       i != contrib_subsets.end(); ++i)
75     delete *i;
76 
77   contrib_subsets.clear();
78   contrib_menu->clear();
79 }
80 
generate_contrib_menu()81 void generate_contrib_menu()
82 {
83   string_list_type level_subsets = dsubdirs("/levels", "info");
84 
85   free_contrib_menu();
86 
87   contrib_menu->additem(MN_LABEL,"Bonus Levels",0,0);
88   contrib_menu->additem(MN_HL,"",0,0);
89 
90   for (int i = 0; i < level_subsets.num_items; ++i)
91     {
92       LevelSubset* subset = new LevelSubset();
93       subset->load(level_subsets.item[i]);
94       contrib_menu->additem(MN_GOTO, subset->title.c_str(), i,
95           contrib_subset_menu, i);
96       contrib_subsets.push_back(subset);
97     }
98 
99   for(int i = 0; i < worldmap_list.num_items; i++)
100     {
101     WorldMapNS::WorldMap worldmap;
102     worldmap.loadmap(worldmap_list.item[i]);
103     contrib_menu->additem(MN_ACTION, worldmap.get_world_title(),0,0, i + level_subsets.num_items);
104     }
105 
106   contrib_menu->additem(MN_HL,"",0,0);
107   contrib_menu->additem(MN_BACK,"Back",0,0);
108 
109   string_list_free(&level_subsets);
110 }
111 
check_contrib_menu()112 void check_contrib_menu()
113 {
114   int index = contrib_menu->check();
115   if (index == -1)
116     return;
117 
118   if (index < (int)contrib_subsets.size())
119     {
120       // FIXME: This shouln't be busy looping
121       LevelSubset& subset = * (contrib_subsets[index]);
122 
123       current_contrib_subset = subset.name;
124 
125       contrib_subset_menu->clear();
126 
127       contrib_subset_menu->additem(MN_LABEL, subset.title, 0,0);
128       contrib_subset_menu->additem(MN_HL,"",0,0);
129 
130       for (int i = 0; i < subset.levels; ++i)
131         {
132         /** get level's title */
133         Level level;
134         level.load(subset.name, i+1);
135         contrib_subset_menu->additem(MN_ACTION, level.name, 0,0,i+1);
136         }
137 
138       contrib_subset_menu->additem(MN_HL,"",0,0);
139       contrib_subset_menu->additem(MN_BACK, "Back", 0, 0);
140       }
141     else if(index < worldmap_list.num_items + (int)contrib_subsets.size())
142       {
143       // Loading fade
144       fadeout();
145 
146       WorldMapNS::WorldMap worldmap;
147       worldmap.loadmap(worldmap_list.item[index - contrib_subsets.size()]);
148 //      worldmap.set_levels_as_solved();
149       std::string savegame = worldmap_list.item[index - contrib_subsets.size()];
150       // remove .stwm...
151       savegame = savegame.substr(0, savegame.size()-5);
152       savegame = std::string(st_save_dir) + "/" + savegame + ".stsg";
153       std::cout << "SaveGameName: " << savegame << "\n";
154       worldmap.loadgame(savegame.c_str());
155 
156       worldmap.display();
157 
158       Menu::set_current(main_menu);
159       }
160 }
161 
check_contrib_subset_menu()162 void check_contrib_subset_menu()
163 {
164   int index = contrib_subset_menu->check();
165   if (index != -1)
166     {
167       if (contrib_subset_menu->get_item_by_id(index).kind == MN_ACTION)
168         {
169           std::cout << "Starting level: " << index << std::endl;
170           GameSession session(current_contrib_subset, index, ST_GL_PLAY);
171           session.run();
172           player_status.reset();
173           Menu::set_current(main_menu);
174         }
175     }
176 }
177 
draw_background()178 void draw_background()
179 {
180   /* Draw the title background: */
181 
182   bkg_title->draw_bg();
183 }
184 
draw_demo(GameSession * session,double frame_ratio)185 void draw_demo(GameSession* session, double frame_ratio)
186 {
187   World* world  = session->get_world();
188   World::set_current(world);
189   Level* plevel = session->get_level();
190   Player* tux = world->get_tux();
191 
192   world->play_music(LEVEL_MUSIC);
193 
194   global_frame_counter++;
195   tux->key_event((SDLKey) keymap.right,DOWN);
196 
197   if(random_timer.check())
198     {
199       if(walking)
200         tux->key_event((SDLKey) keymap.jump,UP);
201       else
202         tux->key_event((SDLKey) keymap.jump,DOWN);
203     }
204   else
205     {
206       random_timer.start(rand() % 3000 + 3000);
207       walking = !walking;
208     }
209 
210   // Wrap around at the end of the level back to the beginnig
211   if(plevel->width * 32 - 320 < tux->base.x)
212     {
213       tux->level_begin();
214       scroll_x = 0;
215     }
216 
217   tux->can_jump = true;
218   float last_tux_x_pos = tux->base.x;
219   world->action(frame_ratio);
220 
221 
222   // disabled for now, since with the new jump code we easily get deadlocks
223   // Jump if tux stays in the same position for one loop, ie. if he is
224   // stuck behind a wall
225   if (last_tux_x_pos == tux->base.x)
226     {
227       walking = false;
228     }
229 
230   world->draw();
231 }
232 
233 /* --- TITLE SCREEN --- */
title(void)234 void title(void)
235 {
236   random_timer.init(true);
237 
238   walking = true;
239 
240   st_pause_ticks_init();
241 
242   GameSession session(datadir + "/levels/misc/menu.stl", 0, ST_GL_DEMO_GAME);
243 
244   clearscreen(0, 0, 0);
245   updatescreen();
246 
247   /* Load images: */
248   bkg_title = new Surface(datadir + "/images/title/background.jpg", IGNORE_ALPHA);
249   logo = new Surface(datadir + "/images/title/logo.png", USE_ALPHA);
250   img_choose_subset = new Surface(datadir + "/images/status/choose-level-subset.png", USE_ALPHA);
251 
252   /* Generating contrib maps by only using a string_list */
253   // Since there isn't any world dir or anything, add a hardcoded entry for Bonus Island
254   string_list_init(&worldmap_list);
255 
256   string_list_type files = dfiles("levels/worldmaps/", ".stwm", "couldn't list worldmaps");
257   for(int i = 0; i < files.num_items; ++i) {
258     if(strcmp(files.item[i], "world1.stwm") == 0)
259       continue;
260     string_list_add_item(&worldmap_list, files.item[i]);
261   }
262   string_list_free(&files);
263 
264   /* --- Main title loop: --- */
265   frame = 0;
266 
267   /* Draw the title background: */
268   bkg_title->draw_bg();
269 
270   update_time = st_get_ticks();
271   random_timer.start(rand() % 2000 + 2000);
272 
273   Menu::set_current(main_menu);
274   while (Menu::current())
275     {
276       // if we spent to much time on a menu entry
277       if( (update_time - last_update_time) > 1000)
278         update_time = last_update_time = st_get_ticks();
279 
280       // Calculate the movement-factor
281       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
282       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
283         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
284       /* Lower the frame_ratio that Tux doesn't jump to hectically throught the demo. */
285       frame_ratio /= 2;
286 
287       SDL_Event event;
288       while (SDL_PollEvent(&event))
289         {
290           if (Menu::current())
291             {
292               Menu::current()->event(event);
293             }
294          // FIXME: QUIT signal should be handled more generic, not locally
295           if (event.type == SDL_QUIT)
296             Menu::set_current(0);
297         }
298 
299       /* Draw the background: */
300       draw_demo(&session, frame_ratio);
301 
302       if (Menu::current() == main_menu)
303         logo->draw( 160, 30);
304 
305       white_small_text->draw(" SuperTux " VERSION "\n"
306                              "Copyright (c) 2003 SuperTux Devel Team\n"
307                              "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"
308                              "are welcome to redistribute it under certain conditions; see the file COPYING\n"
309                              "for details.\n",
310                              0, 420, 0);
311 
312       /* Don't draw menu, if quit is true */
313       Menu* menu = Menu::current();
314       if(menu)
315         {
316           menu->draw();
317           menu->action();
318 
319           if(menu == main_menu)
320             {
321               MusicManager* music_manager;
322 	      MusicRef menu_song;
323               switch (main_menu->check())
324                 {
325                 case MNID_STARTGAME:
326                   // Start Game, ie. goto the slots menu
327                   update_load_save_game_menu(load_game_menu);
328                   break;
329                 case MNID_CONTRIB:
330                   // Contrib Menu
331                   puts("Entering contrib menu");
332                   generate_contrib_menu();
333                   break;
334                 case MNID_LEVELEDITOR:
335                   leveleditor();
336                   Menu::set_current(main_menu);
337                   break;
338                 case MNID_CREDITS:
339                   music_manager = new MusicManager();
340                   menu_song  = music_manager->load_music(datadir + "/music/credits.ogg");
341                   music_manager->halt_music();
342                   music_manager->play_music(menu_song,0);
343                   display_text_file("CREDITS", bkg_title, SCROLL_SPEED_CREDITS);
344                   music_manager->halt_music();
345                   menu_song = music_manager->load_music(datadir + "/music/theme.mod");
346                   music_manager->play_music(menu_song);
347                   Menu::set_current(main_menu);
348                   break;
349                 case MNID_QUITMAINMENU:
350                   Menu::set_current(0);
351                   break;
352                 }
353             }
354           else if(menu == options_menu)
355             {
356               process_options_menu();
357             }
358           else if(menu == load_game_menu)
359             {
360               if(event.key.keysym.sym == SDLK_DELETE)
361                 {
362                 int slot = menu->get_active_item_id();
363                 char str[1024];
364                 sprintf(str,"Are you sure you want to delete slot %d?", slot);
365 
366                 draw_background();
367 
368                 if(confirm_dialog(str))
369                   {
370                   sprintf(str,"%s/slot%d.stsg", st_save_dir, slot);
371                   printf("Removing: %s\n",str);
372                   remove(str);
373                   }
374 
375                 update_load_save_game_menu(load_game_menu);
376                 update_time = st_get_ticks();
377                 Menu::set_current(main_menu);
378                 }
379               else if (process_load_game_menu())
380                 {
381                   // FIXME: shouldn't be needed if GameSession doesn't relay on global variables
382                   // reset tux
383                   scroll_x = 0;
384                   //titletux.level_begin();
385                   update_time = st_get_ticks();
386                 }
387             }
388           else if(menu == contrib_menu)
389             {
390               check_contrib_menu();
391             }
392           else if (menu == contrib_subset_menu)
393             {
394               check_contrib_subset_menu();
395             }
396         }
397 
398       mouse_cursor->draw();
399 
400       flipscreen();
401 
402       /* Set the time of the last update and the time of the current update */
403       last_update_time = update_time;
404       update_time = st_get_ticks();
405 
406       /* Pause: */
407       frame++;
408       SDL_Delay(25);
409     }
410   /* Free surfaces: */
411 
412   free_contrib_menu();
413   string_list_free(&worldmap_list);
414   delete bkg_title;
415   delete logo;
416   delete img_choose_subset;
417 }
418 
419 // EOF //
420 
421