Move all Menus into separate classes
[supertux.git] / src / supertux / title_screen.cpp
1 //  SuperTux
2 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
3 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include <version.h>
19
20 #include "supertux/title_screen.hpp"
21
22 #include <algorithm>
23 #include <physfs.h>
24
25 #include "addon/addon_manager.hpp"
26 #include "audio/sound_manager.hpp"
27 #include "gui/menu.hpp"
28 #include "gui/menu_manager.hpp"
29 #include "gui/menu_item.hpp"
30 #include "lisp/parser.hpp"
31 #include "object/camera.hpp"
32 #include "object/player.hpp"
33 #include "supertux/fadeout.hpp"
34 #include "supertux/gameconfig.hpp"
35 #include "supertux/globals.hpp"
36 #include "supertux/mainloop.hpp"
37 #include "supertux/menu/menu_storage.hpp"
38 #include "supertux/menu/addon_menu.hpp"
39 #include "supertux/menu/contrib_world_menu.hpp"
40 #include "supertux/menu/contrib_menu.hpp"
41 #include "supertux/menu/main_menu.hpp"
42 #include "supertux/menu/options_menu.hpp"
43 #include "supertux/resources.hpp"
44 #include "supertux/sector.hpp"
45 #include "supertux/textscroller.hpp"
46 #include "supertux/world.hpp"
47 #include "util/file_system.hpp"
48 #include "util/gettext.hpp"
49 #include "util/reader.hpp"
50 #include "video/drawing_context.hpp"
51
52 TitleScreen::TitleScreen() :
53   main_menu(),
54   contrib_menu(),
55   contrib_world_menu(),
56   main_world(),
57   contrib_worlds(),
58   addons_menu(),
59   addons(),
60   current_world(),
61   frame(),
62   controller(),
63   titlesession()
64 {
65   controller.reset(new CodeController());
66   titlesession.reset(new GameSession("levels/misc/menu.stl"));
67
68   Player* player = titlesession->get_current_sector()->player;
69   player->set_controller(controller.get());
70   player->set_speedlimit(230); //MAX_WALK_XM
71
72   generate_main_menu();
73
74   frame = std::auto_ptr<Surface>(new Surface("images/engine/menu/frame.png"));
75 }
76
77 void
78 TitleScreen::update_load_game_menu()
79 {
80 }
81
82 void
83 TitleScreen::free_contrib_menu()
84 {
85   for(std::vector<World*>::iterator i = contrib_worlds.begin();
86       i != contrib_worlds.end(); ++i)
87     delete *i;
88
89   contrib_worlds.clear();
90 }
91
92 void
93 TitleScreen::generate_contrib_menu()
94 {
95   /** Generating contrib levels list by making use of Level Subset  */
96   std::vector<std::string> level_worlds;
97   char** files = PHYSFS_enumerateFiles("levels/");
98   for(const char* const* filename = files; *filename != 0; ++filename) {
99     std::string filepath = std::string("levels/") + *filename;
100     if(PHYSFS_isDirectory(filepath.c_str()))
101       level_worlds.push_back(filepath);
102   }
103   PHYSFS_freeList(files);
104
105   free_contrib_menu();
106   contrib_menu.reset(new ContribMenu(level_worlds, 
107                                      contrib_worlds));
108 }
109
110 std::string
111 TitleScreen::get_level_name(const std::string& filename)
112 {
113   try {
114     lisp::Parser parser;
115     const lisp::Lisp* root = parser.parse(filename);
116
117     const lisp::Lisp* level = root->get_lisp("supertux-level");
118     if(!level)
119       return "";
120
121     std::string name;
122     level->get("name", name);
123     return name;
124   } catch(std::exception& e) {
125     log_warning << "Problem getting name of '" << filename << "': "
126                 << e.what() << std::endl;
127     return "";
128   }
129 }
130
131 void
132 TitleScreen::check_levels_contrib_menu()
133 {
134   int index = contrib_menu->check();
135   if (index == -1)
136     return;
137
138   current_world = contrib_worlds[index];
139
140   if(!current_world->is_levelset) {
141     start_game();
142   } else {
143     contrib_world_menu.reset(new ContribWorldMenu(*current_world));
144     MenuManager::push_current(contrib_world_menu.get());
145   }
146 }
147
148 void
149 TitleScreen::check_contrib_world_menu()
150 {
151   int index = contrib_world_menu->check();
152   if (index != -1) {
153     if (contrib_world_menu->get_item_by_id(index).kind == MN_ACTION) {
154       sound_manager->stop_music();
155       GameSession* session =
156         new GameSession(current_world->get_level_filename(index));
157       g_main_loop->push_screen(session);
158     }
159   }
160 }
161
162 namespace {
163 bool generate_addons_menu_sorter(const Addon* a1, const Addon* a2)
164 {
165   return a1->title < a2->title;
166 }
167 }
168
169 void
170 TitleScreen::generate_addons_menu()
171 {
172   AddonManager& adm = AddonManager::get_instance();
173
174   // refresh list of addons
175   addons = adm.get_addons();
176   
177   // sort list
178   std::sort(addons.begin(), addons.end(), generate_addons_menu_sorter);
179
180   // (re)generate menu
181   free_addons_menu();
182   addons_menu.reset(new AddonMenu(addons));
183 }
184
185 void
186 TitleScreen::check_addons_menu()
187 {
188   int index = addons_menu->check();
189   if (index == -1) return;
190
191   // check if "Check Online" was chosen
192   if (index == 0) {
193     try {
194       AddonManager::get_instance().check_online();
195       generate_addons_menu();
196       MenuManager::set_current(addons_menu.get());
197       addons_menu->set_active_item(index);
198     } 
199     catch (std::runtime_error e) {
200       log_warning << "Check for available Add-ons failed: " << e.what() << std::endl;
201     }
202     return;
203   }
204
205   // if one of the Addons listed was chosen, take appropriate action
206   if ((index >= ADDON_LIST_START_ID) && (index < ADDON_LIST_START_ID) + addons.size()) {
207     Addon& addon = *addons[index - ADDON_LIST_START_ID];
208     if (!addon.installed) {
209       try {
210         AddonManager::get_instance().install(&addon);
211       } 
212       catch (std::runtime_error e) {
213         log_warning << "Installing Add-on failed: " << e.what() << std::endl;
214       }
215       addons_menu->set_toggled(index, addon.loaded);
216     } else if (!addon.loaded) {
217       try {
218         AddonManager::get_instance().enable(&addon);
219       } 
220       catch (std::runtime_error e) {
221         log_warning << "Enabling Add-on failed: " << e.what() << std::endl;
222       }
223       addons_menu->set_toggled(index, addon.loaded);
224     } else {
225       try {
226         AddonManager::get_instance().disable(&addon);
227       } 
228       catch (std::runtime_error e) {
229         log_warning << "Disabling Add-on failed: " << e.what() << std::endl;
230       }
231       addons_menu->set_toggled(index, addon.loaded);
232     }
233   }
234 }
235
236 void
237 TitleScreen::free_addons_menu()
238 {
239 }
240
241 void
242 TitleScreen::make_tux_jump()
243 {
244   static bool jumpWasReleased = true;
245   Sector* sector  = titlesession->get_current_sector();
246   Player* tux = sector->player;
247
248   controller->update();
249   controller->press(Controller::RIGHT);
250
251   // Check if we should press the jump button
252   Rect lookahead = tux->get_bbox();
253   lookahead.p2.x += 96;
254   bool pathBlocked = !sector->is_free_of_statics(lookahead);
255   if ((pathBlocked && jumpWasReleased) || !tux->on_ground()) {
256     controller->press(Controller::JUMP);
257     jumpWasReleased = false;
258   } else {
259     jumpWasReleased = true;
260   }
261
262   // Wrap around at the end of the level back to the beginning
263   if(sector->get_width() - 320 < tux->get_pos().x) {
264     sector->activate("main");
265     sector->camera->reset(tux->get_pos());
266   }
267 }
268
269 void
270 TitleScreen::generate_main_menu()
271 {
272   main_menu.reset(new MainMenu());
273 }
274
275 TitleScreen::~TitleScreen()
276 {
277 }
278
279 void
280 TitleScreen::setup()
281 {
282   player_status->reset();
283
284   Sector* sector = titlesession->get_current_sector();
285   if(Sector::current() != sector) {
286     sector->play_music(LEVEL_MUSIC);
287     sector->activate(sector->player->get_pos());
288   }
289
290   MenuManager::set_current(main_menu.get());
291 }
292
293 void
294 TitleScreen::leave()
295 {
296   Sector* sector = titlesession->get_current_sector();
297   sector->deactivate();
298   MenuManager::set_current(NULL);
299 }
300
301 void
302 TitleScreen::draw(DrawingContext& context)
303 {
304   Sector* sector  = titlesession->get_current_sector();
305   sector->draw(context);
306
307   // FIXME: Add something to scale the frame to the resolution of the screen
308   context.draw_surface(frame.get(), Vector(0,0),LAYER_FOREGROUND1);
309
310   context.draw_text(Resources::small_font, "SuperTux " PACKAGE_VERSION "\n",
311                     Vector(5, SCREEN_HEIGHT - 50), ALIGN_LEFT, LAYER_FOREGROUND1);
312   context.draw_text(Resources::small_font,
313                     _(
314                       "Copyright (c) 2007 SuperTux Devel Team\n"
315                       "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to\n"
316                       "redistribute it under certain conditions; see the file COPYING for details.\n"
317                       ),
318                     Vector(5, SCREEN_HEIGHT - 50 + Resources::small_font->get_height() + 5),
319                     ALIGN_LEFT, LAYER_FOREGROUND1);
320 }
321
322 void
323 TitleScreen::update(float elapsed_time)
324 {
325   g_main_loop->set_speed(0.6f);
326   Sector* sector  = titlesession->get_current_sector();
327   sector->update(elapsed_time);
328
329   make_tux_jump();
330
331   Menu* menu = MenuManager::current();
332   if(menu) {
333     if(menu == main_menu.get()) {
334       switch (main_menu->check()) {
335         case MNID_STARTGAME:
336           // Start Game, ie. goto the slots menu
337           if(main_world.get() == NULL) {
338             main_world.reset(new World());
339             main_world->load("levels/world1/info");
340           }
341           current_world = main_world.get();
342           start_game();
343           break;
344
345         case MNID_LEVELS_CONTRIB:
346           // Contrib Menu
347           generate_contrib_menu();
348           MenuManager::push_current(contrib_menu.get());
349           break;
350
351         case MNID_ADDONS:
352           // Add-ons Menu
353           generate_addons_menu();
354           MenuManager::push_current(addons_menu.get());
355           break;
356
357         case MNID_CREDITS:
358           MenuManager::set_current(NULL);
359           g_main_loop->push_screen(new TextScroller("credits.txt"),
360                                    new FadeOut(0.5));
361           break;
362
363         case MNID_QUITMAINMENU:
364           g_main_loop->quit(new FadeOut(0.25));
365           sound_manager->stop_music(0.25);
366           break;
367       }
368     } else if(menu == contrib_menu.get()) {
369       check_levels_contrib_menu();
370     } else if(menu == addons_menu.get()) {
371       check_addons_menu();
372     } else if (menu == contrib_world_menu.get()) {
373       check_contrib_world_menu();
374     }
375   }
376
377   // reopen menu if user closed it (so that the app doesn't close when user
378   // accidently hit ESC)
379   if(MenuManager::current() == 0 && g_main_loop->has_no_pending_fadeout()) {
380     generate_main_menu();
381     MenuManager::set_current(main_menu.get());
382   }
383 }
384
385 void
386 TitleScreen::start_game()
387 {
388   MenuManager::set_current(NULL);
389   std::string basename = current_world->get_basedir();
390   basename = basename.substr(0, basename.length()-1);
391   std::string worlddirname = FileSystem::basename(basename);
392   std::ostringstream stream;
393   stream << "profile" << g_config->profile << "/" << worlddirname << ".stsg";
394   std::string slotfile = stream.str();
395
396   try {
397     current_world->set_savegame_filename(slotfile);
398     current_world->run();
399   } catch(std::exception& e) {
400     log_fatal << "Couldn't start world: " << e.what() << std::endl;
401   }
402 }
403
404 /* EOF */