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