1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2009-2015 Marianne Gagnon
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #define DEBUG_MENU_ITEM 0
19
20 #include "states_screens/main_menu_screen.hpp"
21
22 #include "addons/news_manager.hpp"
23 #include "challenges/story_mode_timer.hpp"
24 #include "challenges/unlock_manager.hpp"
25 #include "config/player_manager.hpp"
26 #include "config/user_config.hpp"
27 #include "graphics/irr_driver.hpp"
28 #include "guiengine/dialog_queue.hpp"
29 #include "guiengine/scalable_font.hpp"
30 #include "guiengine/widgets/label_widget.hpp"
31 #include "guiengine/widgets/list_widget.hpp"
32 #include "guiengine/widgets/ribbon_widget.hpp"
33 #include "input/device_manager.hpp"
34 #include "input/input_manager.hpp"
35 #include "input/keyboard_device.hpp"
36 #include "io/file_manager.hpp"
37 #include "karts/kart_properties_manager.hpp"
38 #include "main_loop.hpp"
39 #include "modes/cutscene_world.hpp"
40 #include "modes/overworld.hpp"
41 #include "modes/demo_world.hpp"
42 #include "network/network_config.hpp"
43 #include "online/request_manager.hpp"
44 #include "states_screens/addons_screen.hpp"
45 #include "states_screens/credits.hpp"
46 #include "states_screens/cutscene_general.hpp"
47 #include "states_screens/grand_prix_editor_screen.hpp"
48 #include "states_screens/help_screen_1.hpp"
49 #include "states_screens/offline_kart_selection.hpp"
50 #include "states_screens/online/online_profile_achievements.hpp"
51 #include "states_screens/online/online_profile_servers.hpp"
52 #include "states_screens/online/online_screen.hpp"
53 #include "states_screens/options/options_screen_general.hpp"
54 #include "states_screens/state_manager.hpp"
55 #include "states_screens/options/user_screen.hpp"
56 #if DEBUG_MENU_ITEM
57 #include "states_screens/feature_unlocked.hpp"
58 #include "states_screens/grand_prix_lose.hpp"
59 #include "states_screens/grand_prix_win.hpp"
60 #endif
61 #include "states_screens/dialogs/message_dialog.hpp"
62 #include "tracks/track_manager.hpp"
63 #include "tracks/track.hpp"
64 #include "utils/string_utils.hpp"
65 #include "utils/translation.hpp"
66
67 #include <string>
68
69
70 using namespace GUIEngine;
71 using namespace Online;
72
73 // ----------------------------------------------------------------------------
74
MainMenuScreen()75 MainMenuScreen::MainMenuScreen() : Screen("main_menu.stkgui")
76 {
77 m_resizable = true;
78 } // MainMenuScreen
79
80 // ----------------------------------------------------------------------------
81
loadedFromFile()82 void MainMenuScreen::loadedFromFile()
83 {
84 LabelWidget* w = getWidget<LabelWidget>("info_addons");
85 w->setScrollSpeed(GUIEngine::getFontHeight() / 2);
86
87 RibbonWidget* rw_top = getWidget<RibbonWidget>("menu_toprow");
88 assert(rw_top != NULL);
89
90 if (track_manager->getTrack("overworld") == NULL ||
91 track_manager->getTrack("introcutscene") == NULL ||
92 track_manager->getTrack("introcutscene2") == NULL)
93 {
94 rw_top->removeChildNamed("story");
95 }
96
97 #if DEBUG_MENU_ITEM != 1
98 RibbonWidget* rw = getWidget<RibbonWidget>("menu_bottomrow");
99 rw->removeChildNamed("test_gpwin");
100 rw->removeChildNamed("test_gplose");
101 rw->removeChildNamed("test_unlocked");
102 rw->removeChildNamed("test_unlocked2");
103 rw->removeChildNamed("test_intro");
104 rw->removeChildNamed("test_outro");
105 #endif
106 } // loadedFromFile
107
108 // ----------------------------------------------------------------------------
109
beforeAddingWidget()110 void MainMenuScreen::beforeAddingWidget()
111 {
112 #ifdef IOS_STK
113 // iOS app doesn't like quit button in UI
114 Widget* w = getWidget("quit");
115 if (w)
116 w->setVisible(false);
117 #endif
118 }
119
120 // ----------------------------------------------------------------------------
121 //
init()122 void MainMenuScreen::init()
123 {
124 GUIEngine::getDevice()->setResizable(true);
125 Screen::init();
126
127 m_user_id = getWidget<ButtonWidget>("user-id");
128 assert(m_user_id);
129
130 // reset in case we're coming back from a race
131 NetworkConfig::get()->cleanNetworkPlayers();
132 StateManager::get()->resetActivePlayers();
133 input_manager->getDeviceManager()->setAssignMode(NO_ASSIGN);
134 input_manager->getDeviceManager()->setSinglePlayer( NULL );
135 input_manager->setMasterPlayerOnly(false);
136
137 // Avoid incorrect behaviour in certain race circumstances:
138 // If a multi-player game is played with two keyboards, the 2nd
139 // player selects his kart last, and only the keyboard is used
140 // to select all other settings - then if the next time the kart
141 // selection screen comes up, the default device will still be
142 // the 2nd player. So if the first player presses 'select', it
143 // will instead add a second player (so basically the key
144 // binding for the second player become the default, so pressing
145 // select will add a new player). See bug 3090931
146 // To avoid this, we will clean the last used device, making
147 // the key bindings for the first player the default again.
148 input_manager->getDeviceManager()->clearLatestUsedDevice();
149
150 #ifndef SERVER_ONLY
151 if (addons_manager && addons_manager->isLoading())
152 {
153 IconButtonWidget* w = getWidget<IconButtonWidget>("addons");
154 w->setActive(false);
155 w->resetAllBadges();
156 w->setBadge(LOADING_BADGE);
157 }
158
159 LabelWidget* w = getWidget<LabelWidget>("info_addons");
160 const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
161 w->setText(news_text, true);
162 w->update(0.01f);
163 #endif
164
165 RibbonWidget* r = getWidget<RibbonWidget>("menu_bottomrow");
166 // FIXME: why do I need to do this manually
167 ((IconButtonWidget*)r->getChildren().get(0))->unfocused(PLAYER_ID_GAME_MASTER, NULL);
168 ((IconButtonWidget*)r->getChildren().get(1))->unfocused(PLAYER_ID_GAME_MASTER, NULL);
169 ((IconButtonWidget*)r->getChildren().get(2))->unfocused(PLAYER_ID_GAME_MASTER, NULL);
170
171 r = getWidget<RibbonWidget>("menu_toprow");
172 r->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
173 DemoWorld::resetIdleTime();
174
175 #ifdef IOS_STK
176 // iOS app doesn't like quit button in UI
177 Widget* quit = getWidget("quit");
178 if (quit)
179 quit->setVisible(false);
180 #endif
181 } // init
182
183 // ----------------------------------------------------------------------------
184
onUpdate(float delta)185 void MainMenuScreen::onUpdate(float delta)
186 {
187 #ifndef SERVER_ONLY
188 NewsManager::get()->joinDownloadThreadIfExit();
189
190 IconButtonWidget* addons_icon = getWidget<IconButtonWidget>("addons");
191 if (addons_icon != NULL)
192 {
193 if (addons_manager->wasError())
194 {
195 addons_icon->setActive(true);
196 addons_icon->resetAllBadges();
197 addons_icon->setBadge(BAD_BADGE);
198 }
199 else if (addons_manager->isLoading() && UserConfigParams::m_internet_status
200 == Online::RequestManager::IPERM_ALLOWED)
201 {
202 // Addons manager is still initialising/downloading.
203 addons_icon->setActive(false);
204 addons_icon->resetAllBadges();
205 addons_icon->setBadge(LOADING_BADGE);
206 }
207 else
208 {
209 addons_icon->setActive(true);
210 addons_icon->resetAllBadges();
211 }
212 // maybe add a new badge when not allowed to access the net
213 }
214
215 LabelWidget* w = getWidget<LabelWidget>("info_addons");
216 w->update(delta);
217 if(w->scrolledOff())
218 {
219 const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
220 w->setText(news_text, true);
221 }
222
223 PlayerProfile *player = PlayerManager::getCurrentPlayer();
224 if (!player)
225 return;
226 if(PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_GUEST ||
227 PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN)
228 {
229 m_user_id->setText(player->getLastOnlineName() + "@stk");
230 }
231 else if (PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_OUT)
232 {
233 m_user_id->setText(player->getName());
234 }
235 else
236 {
237 // now must be either logging in or logging out
238 m_user_id->setText(player->getName());
239 }
240
241 // Ask if user want to play tutorial when profile is newly created
242 if (player->getUseFrequency() != 0)
243 return;
244
245 player->incrementUseFrequency();
246 class PlayTutorial :
247 public MessageDialog::IConfirmDialogListener
248 {
249 public:
250 virtual void onConfirm()
251 {
252 GUIEngine::ModalDialog::dismiss();
253 MainMenuScreen::getInstance()->startTutorial();
254 } // onConfirm
255 }; // PlayTutorial
256
257 MessageDialog* dialog =
258 new MessageDialog(_("Would you like to play the tutorial of the game?"),
259 MessageDialog::MESSAGE_DIALOG_YESNO, new PlayTutorial(),
260 true/*delete_listener*/, true/*from_queue*/);
261 GUIEngine::DialogQueue::get()->pushDialog(dialog,
262 false/*closes_any_dialog*/);
263 #endif
264 } // onUpdate
265
266 // ----------------------------------------------------------------------------
startTutorial()267 void MainMenuScreen::startTutorial()
268 {
269 RaceManager::get()->setNumPlayers(1);
270 RaceManager::get()->setMajorMode (RaceManager::MAJOR_MODE_SINGLE);
271 RaceManager::get()->setMinorMode (RaceManager::MINOR_MODE_TUTORIAL);
272 RaceManager::get()->setNumKarts( 1 );
273 RaceManager::get()->setTrack("tutorial");
274 RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_EASY);
275 RaceManager::get()->setReverseTrack(false);
276
277 // Use keyboard 0 by default (FIXME: let player choose?)
278 InputDevice* device = input_manager->getDeviceManager()->getKeyboard(0);
279
280 // Create player and associate player with keyboard
281 StateManager::get()->createActivePlayer(PlayerManager::getCurrentPlayer(),
282 device);
283
284 if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL)
285 {
286 Log::warn("MainMenuScreen", "Cannot find kart '%s', will revert to default",
287 UserConfigParams::m_default_kart.c_str());
288 UserConfigParams::m_default_kart.revertToDefaults();
289 }
290 RaceManager::get()->setPlayerKart(0, UserConfigParams::m_default_kart);
291
292 // ASSIGN should make sure that only input from assigned devices
293 // is read.
294 input_manager->getDeviceManager()->setAssignMode(ASSIGN);
295 input_manager->getDeviceManager()
296 ->setSinglePlayer( StateManager::get()->getActivePlayer(0) );
297
298 StateManager::get()->enterGameState();
299 RaceManager::get()->setupPlayerKartInfo();
300 RaceManager::get()->startNew(false);
301 } // startTutorial
302
303 // ----------------------------------------------------------------------------
304
eventCallback(Widget * widget,const std::string & name,const int playerID)305 void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
306 const int playerID)
307 {
308 #ifndef SERVER_ONLY
309 if(name=="user-id")
310 {
311 UserScreen::getInstance()->push();
312 return;
313 }
314
315 // most interesting stuff is in the ribbons, so start there
316 RibbonWidget* ribbon = dynamic_cast<RibbonWidget*>(widget);
317
318 if (ribbon == NULL) return; // what's that event??
319
320 // ---- A ribbon icon was clicked
321 std::string selection =
322 ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER);
323
324 /*
325 if (selection == "story")
326 {
327 StateManager::get()->enterGameState();
328 RaceManager::get()->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
329 RaceManager::get()->setNumKarts( 0 );
330 RaceManager::get()->setNumPlayers(0);
331 RaceManager::get()->setNumPlayers(0);
332 RaceManager::get()->startSingleRace("endcutscene", 999, false);
333
334 std::vector<std::string> parts;
335 parts.push_back("introcutscene");
336 parts.push_back("introcutscene2");
337 ((CutsceneWorld*)World::getWorld())->setParts(parts);
338 //RaceManager::get()->startSingleRace("introcutscene2", 999, false);
339 return;
340 }
341 */
342
343 #if DEBUG_MENU_ITEM
344 if (selection == "test_gpwin")
345 {
346 StoryModeStatus* sms = PlayerManager::getCurrentPlayer()->getStoryModeStatus();
347 sms->unlockFeature(const_cast<ChallengeStatus*>(sms->getChallengeStatus("gp1")),
348 RaceManager::DIFFICULTY_HARD);
349
350 StateManager::get()->enterGameState();
351 RaceManager::get()->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
352 RaceManager::get()->setNumKarts(0);
353 RaceManager::get()->setNumPlayers(0);
354 RaceManager::get()->setNumPlayers(0);
355 RaceManager::get()->startSingleRace("gpwin", 999, false);
356 GrandPrixWin* scene = GrandPrixWin::getInstance();
357 scene->push();
358 const std::pair<std::string, float> winners[] =
359 {
360 { "kiki", 0.6f },
361 { "nolok", 1.0f },
362 { "pidgin", 0.0f },
363 };
364 scene->setKarts(winners);
365 }
366 else if (selection == "test_gplose")
367 {
368 StateManager::get()->enterGameState();
369 RaceManager::get()->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
370 RaceManager::get()->setNumKarts(0);
371 RaceManager::get()->setNumPlayers(0);
372 RaceManager::get()->setNumPlayers(0);
373 RaceManager::get()->startSingleRace("gplose", 999, false);
374 GrandPrixLose* scene = GrandPrixLose::getInstance();
375 scene->push();
376 std::vector<std::pair<std::string, float> > losers;
377 losers.emplace_back("nolok", 1.0f);
378 losers.emplace_back("kiki", 0.6f);
379 //losers.push_back("wilber");
380 //losers.push_back("tux");
381 scene->setKarts(losers);
382 }
383 else if (selection == "test_unlocked" || selection == "test_unlocked2")
384 {
385 StoryModeStatus* sms = PlayerManager::getCurrentPlayer()->getStoryModeStatus();
386 sms->unlockFeature(const_cast<ChallengeStatus*>(sms->getChallengeStatus("gp1")),
387 RaceManager::DIFFICULTY_HARD);
388
389 StateManager::get()->enterGameState();
390 RaceManager::get()->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
391 RaceManager::get()->setNumKarts(0);
392 RaceManager::get()->setNumPlayers(0);
393 RaceManager::get()->setNumPlayers(0);
394 RaceManager::get()->startSingleRace("featunlocked", 999, false);
395
396 FeatureUnlockedCutScene* scene =
397 FeatureUnlockedCutScene::getInstance();
398
399 std::vector<std::string> parts;
400 parts.push_back("featunlocked");
401 ((CutsceneWorld*)World::getWorld())->setParts(parts);
402
403 scene->addTrophy(RaceManager::DIFFICULTY_EASY, false);
404
405 if (selection == "test_unlocked")
406 {
407 scene->addUnlockedKart(kart_properties_manager->getKart("tux"));
408 scene->addUnlockedTrack(track_manager->getTrack("lighthouse"));
409 scene->push();
410 }
411 else if (selection == "test_unlocked2")
412 {
413 std::vector<video::ITexture*> textures;
414 textures.push_back(irr_driver->getTexture(
415 track_manager->getTrack("lighthouse")
416 ->getScreenshotFile().c_str()));
417 textures.push_back(irr_driver->getTexture(
418 track_manager->getTrack("snowtuxpeak")
419 ->getScreenshotFile().c_str()));
420 textures.push_back(irr_driver->getTexture(
421 track_manager->getTrack("sandtrack")
422 ->getScreenshotFile().c_str()));
423 textures.push_back(irr_driver->getTexture(
424 track_manager->getTrack("snowmountain")
425 ->getScreenshotFile().c_str()));
426
427 scene->addUnlockedPictures(textures, 4.0, 3.0, L"You unlocked <actual text would go here...>");
428
429 scene->push();
430 }
431 }
432 else if (selection == "test_intro")
433 {
434 CutsceneWorld::setUseDuration(true);
435 StateManager::get()->enterGameState();
436 RaceManager::get()->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
437 RaceManager::get()->setNumKarts(0);
438 RaceManager::get()->setNumPlayers(0);
439 RaceManager::get()->setNumPlayers(0);
440 RaceManager::get()->startSingleRace("introcutscene", 999, false);
441
442 std::vector<std::string> parts;
443 parts.push_back("introcutscene");
444 parts.push_back("introcutscene2");
445 ((CutsceneWorld*)World::getWorld())->setParts(parts);
446 //RaceManager::get()->startSingleRace("introcutscene2", 999, false);
447
448 CutSceneGeneral* scene = CutSceneGeneral::getInstance();
449 scene->push();
450 return;
451 }
452 else if (selection == "test_outro")
453 {
454 CutsceneWorld::setUseDuration(true);
455 StateManager::get()->enterGameState();
456 RaceManager::get()->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
457 RaceManager::get()->setNumKarts(0);
458 RaceManager::get()->setNumPlayers(0);
459 RaceManager::get()->setNumPlayers(0);
460 RaceManager::get()->startSingleRace("endcutscene", 999, false);
461
462 std::vector<std::string> parts;
463 parts.push_back("endcutscene");
464 ((CutsceneWorld*)World::getWorld())->setParts(parts);
465
466 CutSceneGeneral* scene = CutSceneGeneral::getInstance();
467 scene->push();
468 }
469 else
470 #endif
471 if (selection == "new")
472 {
473 NetworkConfig::get()->unsetNetworking();
474 KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance();
475 s->setMultiplayer(false);
476 s->setFromOverworld(false);
477 s->push();
478 }
479 else if (selection == "multiplayer")
480 {
481 KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance();
482 NetworkConfig::get()->unsetNetworking();
483 s->setMultiplayer(true);
484 s->setFromOverworld(false);
485 s->push();
486 }
487 else if (selection == "options")
488 {
489 OptionsScreenGeneral::getInstance()->push();
490 }
491 else if (selection == "quit")
492 {
493 StateManager::get()->popMenu();
494 return;
495 }
496 else if (selection == "about")
497 {
498 CreditsScreen::getInstance()->push();
499 }
500 else if (selection == "help")
501 {
502 HelpScreen1::getInstance()->push();
503 }
504 else if (selection == "startTutorial")
505 {
506 startTutorial();
507 }
508 else if (selection == "story")
509 {
510 NetworkConfig::get()->unsetNetworking();
511 PlayerProfile *player = PlayerManager::getCurrentPlayer();
512
513 // Start the story mode (and speedrun) timer
514 story_mode_timer->startTimer();
515
516 if (player->isFirstTime())
517 {
518 CutsceneWorld::setUseDuration(true);
519 StateManager::get()->enterGameState();
520 RaceManager::get()->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
521 RaceManager::get()->setNumKarts( 0 );
522 RaceManager::get()->setNumPlayers(0);
523 RaceManager::get()->startSingleRace("introcutscene", 999, false);
524
525 std::vector<std::string> parts;
526 parts.push_back("introcutscene");
527 parts.push_back("introcutscene2");
528 ((CutsceneWorld*)World::getWorld())->setParts(parts);
529 //RaceManager::get()->startSingleRace("introcutscene2", 999, false);
530
531 CutSceneGeneral* scene = CutSceneGeneral::getInstance();
532 scene->push();
533 return;
534 }
535 else
536 {
537 // Unpause the story mode timer when entering back the story mode
538 story_mode_timer->unpauseTimer(/* exit loading pause */ false);
539
540 const std::string default_kart = UserConfigParams::m_default_kart;
541 if (player->isLocked(default_kart))
542 {
543 KartSelectionScreen *next = OfflineKartSelectionScreen::getInstance();
544 next->setGoToOverworldNext();
545 next->setMultiplayer(false);
546 next->push();
547 return;
548 }
549 OverWorld::enterOverWorld();
550 }
551 }
552 else if (selection == "online")
553 {
554 if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED)
555 {
556 new MessageDialog(_("You can not play online without internet access. "
557 "If you want to play online, go in the options menu, "
558 "and check \"Connect to the Internet\"."));
559 return;
560 }
561 OnlineScreen::getInstance()->push();
562 }
563 else if (selection == "addons")
564 {
565 // Don't go to addons if there is no internet, unless some addons are
566 // already installed (so that you can delete addons without being online).
567 if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED)
568 {
569 if (!addons_manager->anyAddonsInstalled())
570 {
571 new MessageDialog(_("You can not download addons without internet access. "
572 "If you want to download addons, go in the options menu, "
573 "and check \"Connect to the Internet\"."));
574 return;
575 }
576 else
577 {
578 AddonsScreen::getInstance()->push();
579 new MessageDialog(_("You can not download addons without internet access. "
580 "If you want to download addons, go in the options menu, "
581 "and check \"Connect to the Internet\".\n\n"
582 "You can however delete already downloaded addons."));
583 return;
584 }
585 }
586 AddonsScreen::getInstance()->push();
587 }
588 else if (selection == "gpEditor")
589 {
590 GrandPrixEditorScreen::getInstance()->push();
591 }
592 else if (selection == "achievements")
593 {
594 OnlineProfileAchievements::getInstance()->push();
595 }
596 #endif
597 } // eventCallback
598
599 // ----------------------------------------------------------------------------
600
tearDown()601 void MainMenuScreen::tearDown()
602 {
603 GUIEngine::getDevice()->setResizable(false);
604 } // tearDown
605
606 // ----------------------------------------------------------------------------
607
onDisabledItemClicked(const std::string & item)608 void MainMenuScreen::onDisabledItemClicked(const std::string& item)
609 {
610 #ifndef SERVER_ONLY
611 if (item == "addons")
612 {
613 if (UserConfigParams::m_internet_status != RequestManager::IPERM_ALLOWED)
614 {
615 new MessageDialog( _("The add-ons module is currently disabled in "
616 "the Options screen") );
617 }
618 else if (addons_manager->wasError())
619 {
620 new MessageDialog( _("Sorry, an error occurred while contacting "
621 "the add-ons website. Make sure you are "
622 "connected to the Internet and that "
623 "SuperTuxKart is not blocked by a firewall"));
624 }
625 else if (addons_manager->isLoading())
626 {
627 new MessageDialog( _("Please wait while the add-ons are loading"));
628 }
629 }
630 #endif
631 } // onDisabledItemClicked
632
633 // ----------------------------------------------------------------------------
634
onEscapePressed()635 bool MainMenuScreen::onEscapePressed()
636 {
637 return true;
638 } // onEscapePressed
639