Move some code from TitleScreen to ContribWorldMenu
[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 namespace {
149 bool generate_addons_menu_sorter(const Addon* a1, const Addon* a2)
150 {
151   return a1->title < a2->title;
152 }
153 }
154
155 void
156 TitleScreen::generate_addons_menu()
157 {
158   AddonManager& adm = AddonManager::get_instance();
159
160   // refresh list of addons
161   addons = adm.get_addons();
162   
163   // sort list
164   std::sort(addons.begin(), addons.end(), generate_addons_menu_sorter);
165
166   // (re)generate menu
167   free_addons_menu();
168   addons_menu.reset(new AddonMenu(addons));
169 }
170
171 void
172 TitleScreen::check_addons_menu()
173 {
174   int index = addons_menu->check();
175   if (index == -1) return;
176
177   // check if "Check Online" was chosen
178   if (index == 0) {
179     try {
180       AddonManager::get_instance().check_online();
181       generate_addons_menu();
182       MenuManager::set_current(addons_menu.get());
183       addons_menu->set_active_item(index);
184     } 
185     catch (std::runtime_error e) {
186       log_warning << "Check for available Add-ons failed: " << e.what() << std::endl;
187     }
188     return;
189   }
190
191   // if one of the Addons listed was chosen, take appropriate action
192   if ((index >= ADDON_LIST_START_ID) && (index < ADDON_LIST_START_ID) + addons.size()) {
193     Addon& addon = *addons[index - ADDON_LIST_START_ID];
194     if (!addon.installed) {
195       try {
196         AddonManager::get_instance().install(&addon);
197       } 
198       catch (std::runtime_error e) {
199         log_warning << "Installing Add-on failed: " << e.what() << std::endl;
200       }
201       addons_menu->set_toggled(index, addon.loaded);
202     } else if (!addon.loaded) {
203       try {
204         AddonManager::get_instance().enable(&addon);
205       } 
206       catch (std::runtime_error e) {
207         log_warning << "Enabling Add-on failed: " << e.what() << std::endl;
208       }
209       addons_menu->set_toggled(index, addon.loaded);
210     } else {
211       try {
212         AddonManager::get_instance().disable(&addon);
213       } 
214       catch (std::runtime_error e) {
215         log_warning << "Disabling Add-on failed: " << e.what() << std::endl;
216       }
217       addons_menu->set_toggled(index, addon.loaded);
218     }
219   }
220 }
221
222 void
223 TitleScreen::free_addons_menu()
224 {
225 }
226
227 void
228 TitleScreen::make_tux_jump()
229 {
230   static bool jumpWasReleased = true;
231   Sector* sector  = titlesession->get_current_sector();
232   Player* tux = sector->player;
233
234   controller->update();
235   controller->press(Controller::RIGHT);
236
237   // Check if we should press the jump button
238   Rect lookahead = tux->get_bbox();
239   lookahead.p2.x += 96;
240   bool pathBlocked = !sector->is_free_of_statics(lookahead);
241   if ((pathBlocked && jumpWasReleased) || !tux->on_ground()) {
242     controller->press(Controller::JUMP);
243     jumpWasReleased = false;
244   } else {
245     jumpWasReleased = true;
246   }
247
248   // Wrap around at the end of the level back to the beginning
249   if(sector->get_width() - 320 < tux->get_pos().x) {
250     sector->activate("main");
251     sector->camera->reset(tux->get_pos());
252   }
253 }
254
255 void
256 TitleScreen::generate_main_menu()
257 {
258   main_menu.reset(new MainMenu());
259 }
260
261 TitleScreen::~TitleScreen()
262 {
263 }
264
265 void
266 TitleScreen::setup()
267 {
268   player_status->reset();
269
270   Sector* sector = titlesession->get_current_sector();
271   if(Sector::current() != sector) {
272     sector->play_music(LEVEL_MUSIC);
273     sector->activate(sector->player->get_pos());
274   }
275
276   MenuManager::set_current(main_menu.get());
277 }
278
279 void
280 TitleScreen::leave()
281 {
282   Sector* sector = titlesession->get_current_sector();
283   sector->deactivate();
284   MenuManager::set_current(NULL);
285 }
286
287 void
288 TitleScreen::draw(DrawingContext& context)
289 {
290   Sector* sector  = titlesession->get_current_sector();
291   sector->draw(context);
292
293   // FIXME: Add something to scale the frame to the resolution of the screen
294   context.draw_surface(frame.get(), Vector(0,0),LAYER_FOREGROUND1);
295
296   context.draw_text(Resources::small_font, "SuperTux " PACKAGE_VERSION "\n",
297                     Vector(5, SCREEN_HEIGHT - 50), ALIGN_LEFT, LAYER_FOREGROUND1);
298   context.draw_text(Resources::small_font,
299                     _(
300                       "Copyright (c) 2007 SuperTux Devel Team\n"
301                       "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to\n"
302                       "redistribute it under certain conditions; see the file COPYING for details.\n"
303                       ),
304                     Vector(5, SCREEN_HEIGHT - 50 + Resources::small_font->get_height() + 5),
305                     ALIGN_LEFT, LAYER_FOREGROUND1);
306 }
307
308 void
309 TitleScreen::update(float elapsed_time)
310 {
311   g_main_loop->set_speed(0.6f);
312   Sector* sector  = titlesession->get_current_sector();
313   sector->update(elapsed_time);
314
315   make_tux_jump();
316
317   Menu* menu = MenuManager::current();
318   if(menu) {
319     if(menu == main_menu.get()) {
320       switch (main_menu->check()) {
321         case MNID_STARTGAME:
322           // Start Game, ie. goto the slots menu
323           if(main_world.get() == NULL) {
324             main_world.reset(new World());
325             main_world->load("levels/world1/info");
326           }
327           current_world = main_world.get();
328           start_game();
329           break;
330
331         case MNID_LEVELS_CONTRIB:
332           // Contrib Menu
333           generate_contrib_menu();
334           MenuManager::push_current(contrib_menu.get());
335           break;
336
337         case MNID_ADDONS:
338           // Add-ons Menu
339           generate_addons_menu();
340           MenuManager::push_current(addons_menu.get());
341           break;
342
343         case MNID_CREDITS:
344           MenuManager::set_current(NULL);
345           g_main_loop->push_screen(new TextScroller("credits.txt"),
346                                    new FadeOut(0.5));
347           break;
348
349         case MNID_QUITMAINMENU:
350           g_main_loop->quit(new FadeOut(0.25));
351           sound_manager->stop_music(0.25);
352           break;
353       }
354     } else if(menu == contrib_menu.get()) {
355       check_levels_contrib_menu();
356     } else if(menu == addons_menu.get()) {
357       check_addons_menu();
358     } else if (menu == contrib_world_menu.get()) {
359       contrib_world_menu->check_menu();
360     }
361   }
362
363   // reopen menu if user closed it (so that the app doesn't close when user
364   // accidently hit ESC)
365   if(MenuManager::current() == 0 && g_main_loop->has_no_pending_fadeout()) {
366     generate_main_menu();
367     MenuManager::set_current(main_menu.get());
368   }
369 }
370
371 void
372 TitleScreen::start_game()
373 {
374   MenuManager::set_current(NULL);
375   std::string basename = current_world->get_basedir();
376   basename = basename.substr(0, basename.length()-1);
377   std::string worlddirname = FileSystem::basename(basename);
378   std::ostringstream stream;
379   stream << "profile" << g_config->profile << "/" << worlddirname << ".stsg";
380   std::string slotfile = stream.str();
381
382   try {
383     current_world->set_savegame_filename(slotfile);
384     current_world->run();
385   } catch(std::exception& e) {
386     log_fatal << "Couldn't start world: " << e.what() << std::endl;
387   }
388 }
389
390 /* EOF */