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