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