1 // SuperTuxKart - a fun racing game with go-kart
2 //
3 // Copyright (C) 2006-2015 SuperTuxKart-Team
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (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, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "states_screens/kart_selection.hpp"
20
21 #include "audio/sfx_manager.hpp"
22 #include "challenges/unlock_manager.hpp"
23 #include "config/player_manager.hpp"
24 #include "config/user_config.hpp"
25 #include "graphics/irr_driver.hpp"
26 #include "graphics/render_info.hpp"
27 #include "guiengine/message_queue.hpp"
28 #include "guiengine/widgets/bubble_widget.hpp"
29 #include "guiengine/widgets/kart_stats_widget.hpp"
30 #include "guiengine/widgets/model_view_widget.hpp"
31 #include "guiengine/widgets/player_name_spinner.hpp"
32 #include "input/input_device.hpp"
33 #include "input/input_manager.hpp"
34 #include "input/device_manager.hpp"
35 #include "items/item_manager.hpp"
36 #include "karts/abstract_characteristic.hpp"
37 #include "karts/kart_model.hpp"
38 #include "karts/kart_properties.hpp"
39 #include "karts/kart_properties_manager.hpp"
40 #include "modes/overworld.hpp"
41 #include "network/network_config.hpp"
42 #include "states_screens/race_setup_screen.hpp"
43 #include "utils/log.hpp"
44 #include "utils/random_generator.hpp"
45 #include "utils/string_utils.hpp"
46 #include "utils/translation.hpp"
47
48 #include <IGUIEnvironment.h>
49 #include <IGUIButton.h>
50
51 using namespace GUIEngine;
52 using irr::core::stringw;
53
54
55 static const char RANDOM_KART_ID[] = "randomkart";
56 static const char ID_DONT_USE[] = "x";
57 // Use '/' as special character to avoid that someone creates
58 // a kart called 'locked'
59 static const char ID_LOCKED[] = "locked/";
60
61 KartSelectionScreen* KartSelectionScreen::m_instance_ptr = NULL;
62
63 int g_root_id;
64
65 /** Currently, navigation for multiple players at the same time is implemented
66 in a somewhat clunky way. An invisible "dispatcher" widget is added above
67 kart icons. When a player moves up, he focuses the dispatcher, which in
68 turn moves the selection to the appropriate spinner. "tabbing roots" are
69 used to make navigation back down possible. (FIXME: maybe find a cleaner
70 way?) */
71
72 // ------------------------------------------------------------------------
FocusDispatcher(KartSelectionScreen * parent)73 FocusDispatcher::FocusDispatcher(KartSelectionScreen* parent) : Widget(WTYPE_BUTTON)
74 {
75 m_parent = parent;
76 m_supports_multiplayer = true;
77 m_is_initialised = false;
78
79 Widget* kartsAreaWidget = parent->getWidget("playerskarts");
80 assert(kartsAreaWidget);
81
82 m_x = 0;
83 m_y = kartsAreaWidget->m_y;
84 m_w = 1;
85 m_h = 1;
86
87 m_reserved_id = Widget::getNewNoFocusID();
88 } // FocusDispatcher
89 // ------------------------------------------------------------------------
setRootID(const int reservedID)90 void FocusDispatcher::setRootID(const int reservedID)
91 {
92 assert(reservedID != -1);
93
94 m_reserved_id = reservedID;
95
96 if (m_element != NULL)
97 {
98 m_element->setID(m_reserved_id);
99 }
100
101 m_is_initialised = true;
102 } // setRootID
103
104 // ------------------------------------------------------------------------
add()105 void FocusDispatcher::add()
106 {
107 core::rect<s32> widget_size(m_x, m_y, m_x + m_w, m_y + m_h);
108
109 m_element = GUIEngine::getGUIEnv()->addButton(widget_size, NULL,
110 m_reserved_id,
111 L"Dispatcher", L"");
112
113 m_id = m_element->getID();
114 m_element->setTabStop(true);
115 m_element->setTabGroup(false);
116 m_element->setTabOrder(m_id);
117 }
118
focused(const int player_id)119 EventPropagation FocusDispatcher::focused(const int player_id)
120 {
121 if (!m_is_initialised) return EVENT_LET;
122
123 if(UserConfigParams::logGUI())
124 Log::info("[KartSelectionScreen]", "FocusDispatcher focused by player %u",
125 player_id);
126
127 // since this screen is multiplayer, redirect focus to the right widget
128 const int amount = m_parent->m_kart_widgets.size();
129 for (int n=0; n<amount; n++)
130 {
131 if (m_parent->m_kart_widgets[n].getPlayerID() == player_id)
132 {
133 // If player is done, don't do anything with focus
134 if (m_parent->m_kart_widgets[n].isReady())
135 return GUIEngine::EVENT_BLOCK;
136
137 //std::cout << "--> Redirecting focus for player " << player_id
138 // << " from FocusDispatcher " <<
139 // " (ID " << m_element->getID() <<
140 // ") to spinner " << n << " (ID " <<
141 // m_parent->m_kart_widgets[n].m_player_ident_spinner
142 // ->getIrrlichtElement()->getID() <<
143 // ")" << std::endl;
144
145 m_parent->m_kart_widgets[n].m_player_ident_spinner
146 ->setFocusForPlayer(player_id);
147
148
149 return GUIEngine::EVENT_BLOCK;
150 }
151 }
152
153 //Log::fatal("KartSelectionScreen", "The focus dispatcher can't"
154 // "find the widget for player %d!", player_id);
155 return GUIEngine::EVENT_LET;
156 } // focused
157
158 #if 0
159 #pragma mark -
160 #pragma mark KartHoverListener
161 #endif
162 // ============================================================================
163
KartHoverListener(KartSelectionScreen * parent)164 KartHoverListener::KartHoverListener(KartSelectionScreen* parent)
165 {
166 m_magic_number = 0xCAFEC001;
167 m_parent = parent;
168 } // KartHoverListener
169
170 // ------------------------------------------------------------------------
~KartHoverListener()171 KartHoverListener::~KartHoverListener()
172 {
173 assert(m_magic_number == 0xCAFEC001);
174 m_magic_number = 0xDEADBEEF;
175 } // ~KartHoverListener
176
177 // ------------------------------------------------------------------------
onSelectionChanged(DynamicRibbonWidget * theWidget,const std::string & selectionID,const irr::core::stringw & selectionText,const int player_id)178 void KartHoverListener::onSelectionChanged(DynamicRibbonWidget* theWidget,
179 const std::string& selectionID,
180 const irr::core::stringw& selectionText,
181 const int player_id)
182 {
183 assert(m_magic_number == 0xCAFEC001);
184
185 // Check if this player has a kart
186 if (m_parent->m_kart_widgets.size() <= unsigned(player_id))
187 {
188 GUIEngine::focusNothingForPlayer(player_id);
189 return;
190 }
191
192 // Don't allow changing the selection after confirming it
193 if (m_parent->m_kart_widgets[player_id].isReady())
194 {
195 // discard events sent when putting back to the right kart
196 if (selectionID ==
197 m_parent->m_kart_widgets[player_id].m_kart_internal_name) return;
198
199 DynamicRibbonWidget* w =
200 m_parent->getWidget<DynamicRibbonWidget>("karts");
201 assert(w != NULL);
202
203 w->setSelection(m_parent->m_kart_widgets[player_id]
204 .m_kart_internal_name, player_id, true);
205 return;
206 }
207
208 if (m_parent->m_kart_widgets[player_id].getKartInternalName() == selectionID)
209 return; // already selected
210
211 m_parent->updateKartWidgetModel(player_id, selectionID, selectionText,
212 m_parent->m_kart_widgets[player_id].getAssociatedPlayer()->getProfile()
213 ->getDefaultKartColor());
214 m_parent->m_kart_widgets[player_id].setKartInternalName(selectionID);
215 m_parent->updateKartStats(player_id, selectionID);
216 m_parent->validateKartChoices();
217 } // onSelectionChanged
218
219 #if 0
220 #pragma mark -
221 #pragma mark KartSelectionScreen
222 #endif
223
224 // ============================================================================
225
226 /** Small utility function that returns whether the two given players chose
227 * the same kart. The advantage of this function is that it can handle
228 * "random kart" selection. */
sameKart(const PlayerKartWidget & player1,const PlayerKartWidget & player2)229 bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2)
230 {
231 return player1.getKartInternalName() == player2.getKartInternalName() &&
232 player1.getKartInternalName() != RANDOM_KART_ID;
233 }
234
235 // ============================================================================
236
KartSelectionScreen(const char * filename)237 KartSelectionScreen::KartSelectionScreen(const char* filename) : Screen(filename)
238 {
239 m_dispatcher = NULL;
240 m_removed_widget = NULL;
241 m_multiplayer_message = NULL;
242 m_from_overworld = false;
243 m_go_to_overworld_next = false;
244 } // KartSelectionScreen
245
246 // ============================================================================
247
getRunningInstance()248 KartSelectionScreen* KartSelectionScreen::getRunningInstance()
249 {
250 return m_instance_ptr;
251 }
252
253 // ----------------------------------------------------------------------------
254
loadedFromFile()255 void KartSelectionScreen::loadedFromFile()
256 {
257 m_dispatcher = new FocusDispatcher(this);
258 m_first_widget = m_dispatcher;
259 m_game_master_confirmed = false;
260 m_multiplayer_message = NULL;
261 // Dynamically add tabs
262 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
263 assert( tabs != NULL );
264
265 m_last_widget = tabs;
266 } // loadedFromFile
267
268 // ----------------------------------------------------------------------------
269
beforeAddingWidget()270 void KartSelectionScreen::beforeAddingWidget()
271 {
272 if (useContinueButton())
273 {
274 getWidget("kartlist")->m_properties[GUIEngine::PROP_WIDTH] = "85%";
275 getWidget("continue")->setVisible(true);
276 }
277 else
278 {
279 getWidget("kartlist")->m_properties[GUIEngine::PROP_WIDTH] = "100%";
280 getWidget("continue")->setVisible(false);
281 }
282 // Remove dispatcher from m_widgets before calculateLayout otherwise a
283 // dummy button is shown in kart screen
284 bool removed_dispatcher = false;
285 if (m_widgets.contains(m_dispatcher))
286 {
287 m_widgets.remove(m_dispatcher);
288 removed_dispatcher = true;
289 }
290 calculateLayout();
291 if (removed_dispatcher)
292 m_widgets.push_back(m_dispatcher);
293
294 // Dynamically add tabs
295 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
296 assert( tabs != NULL );
297
298 m_last_widget = tabs;
299 tabs->clearAllChildren();
300
301 const std::vector<std::string>& groups =
302 kart_properties_manager->getAllGroups();
303 const int group_amount = (int)groups.size();
304
305 // add all group first
306 if (group_amount > 1)
307 {
308 //I18N: name of the tab that will show tracks from all groups
309 tabs->addTextChild( _("All") , ALL_KART_GROUPS_ID);
310 }
311
312 // Make group names being picked up by gettext
313 #define FOR_GETTEXT_ONLY(x)
314 //I18N: kart group name
315 FOR_GETTEXT_ONLY( _("standard") )
316 //I18N: kart group name
317 FOR_GETTEXT_ONLY( _("Add-Ons") )
318
319
320 // add others after
321 for (int n=0; n<group_amount; n++)
322 {
323 // try to translate group names
324 tabs->addTextChild( _(groups[n].c_str()) , groups[n]);
325 } // for n<group_amount
326
327
328 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
329 assert( w != NULL );
330
331 w->setItemCountHint( kart_properties_manager->getNumberOfKarts() );
332 } // beforeAddingWidget
333
334 // ----------------------------------------------------------------------------
335
init()336 void KartSelectionScreen::init()
337 {
338 m_instance_ptr = this;
339 Screen::init();
340 m_must_delete_on_back = false;
341
342 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
343 assert( tabs != NULL );
344 tabs->select(UserConfigParams::m_last_used_kart_group,
345 PLAYER_ID_GAME_MASTER);
346
347 Widget* placeholder = getWidget("playerskarts");
348 assert(placeholder != NULL);
349
350 m_dispatcher->setRootID(placeholder->m_reserved_id);
351
352 g_root_id = placeholder->m_reserved_id;
353 if (!m_widgets.contains(m_dispatcher))
354 {
355 m_widgets.push_back(m_dispatcher);
356
357 // this is only needed if the dispatcher wasn't already in
358 // the list of widgets. If it already was, it was added along
359 // other widgets.
360 m_dispatcher->add();
361 }
362
363 m_game_master_confirmed = false;
364
365 tabs->setActive(true);
366
367 m_kart_widgets.clearAndDeleteAll();
368
369 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
370 assert( w != NULL );
371 // Only allow keyboard and gamepad to choose kart without continue button in
372 // multitouch GUI, so mouse (touch) clicking can be used as previewing karts
373 w->setEventCallbackActive(Input::IT_MOUSEBUTTON, !useContinueButton());
374
375 KartHoverListener* karthoverListener = new KartHoverListener(this);
376 w->registerHoverListener(karthoverListener);
377
378
379 // Build kart list (it is built everytime, to account for .g. locking)
380 setKartsFromCurrentGroup();
381
382 /*
383
384 TODO: Ultimately, it'd be nice to *not* clear m_kart_widgets so that
385 when players return to the kart selection screen, it will appear as
386 it did when they left (at least when returning from the track menu).
387 Rebuilding the screen is a little tricky.
388
389 */
390
391 /*
392 if (m_kart_widgets.size() > 0)
393 {
394 // trying to rebuild the screen
395 for (int n = 0; n < m_kart_widgets.size(); n++)
396 {
397 PlayerKartWidget *pkw;
398 pkw = m_kart_widgets.get(n);
399 manualAddWidget(pkw);
400 pkw->add();
401 }
402
403 }
404 else */
405 // For now this is what will happen
406 input_manager->getDeviceManager()->setAssignMode(DETECT_NEW);
407 // This flag will cause that a 'fire' event will be mapped to 'select' (if
408 // 'fire' is not assigned to a GUI event). This is done to support the old
409 // way of player joining by pressing 'fire' instead of 'select'.
410 input_manager->getDeviceManager()->mapFireToSelect(true);
411
412 if (!NetworkConfig::get()->isNetworking())
413 {
414 StateManager::get()->resetActivePlayers();
415 if (!m_multiplayer)
416 {
417 joinPlayer(input_manager->getDeviceManager()->getLatestUsedDevice(),
418 NULL/*player profile*/);
419 w->updateItemDisplay();
420
421 // Player 0 select default kart
422 if (!w->setSelection(UserConfigParams::m_default_kart, 0, true))
423 {
424 // if kart from config not found, select the first instead
425 w->setSelection(0, 0, true);
426 }
427 }
428 else
429 {
430 // Add multiplayer message
431 addMultiplayerMessage();
432 #ifdef MOBILE_STK
433 MessageQueue::addStatic(MessageQueue::MT_GENERIC,
434 _("Connect a keyboard or gamepad to play splitscreen multiplayer"));
435 #endif
436 }
437 }
438 } // init
439
440 // ----------------------------------------------------------------------------
441
tearDown()442 void KartSelectionScreen::tearDown()
443 {
444 #ifdef MOBILE_STK
445 if (m_multiplayer)
446 MessageQueue::discardStatic();
447 #endif
448 // Reset the 'map fire to select' option of the device manager
449 input_manager->getDeviceManager()->mapFireToSelect(false);
450
451 // if a removed widget is currently shrinking down, remove it upon leaving
452 // the screen
453 if (m_removed_widget != NULL)
454 {
455 manualRemoveWidget(m_removed_widget);
456 delete m_removed_widget;
457 m_removed_widget = NULL;
458 }
459
460 removeMultiplayerMessage();
461
462 Screen::tearDown();
463 m_kart_widgets.clearAndDeleteAll();
464
465 if (m_must_delete_on_back)
466 GUIEngine::removeScreen(this);
467
468 } // tearDown
469
470 // ----------------------------------------------------------------------------
471
unloaded()472 void KartSelectionScreen::unloaded()
473 {
474 // This pointer is no longer valid (has been deleted along other widgets)
475 m_dispatcher = NULL;
476 }
477
478 // ----------------------------------------------------------------------------
479 // Return true if event was handled successfully
joinPlayer(InputDevice * device,PlayerProfile * p)480 bool KartSelectionScreen::joinPlayer(InputDevice* device, PlayerProfile* p)
481 {
482 #ifdef MOBILE_STK
483 if (m_multiplayer)
484 MessageQueue::discardStatic();
485 #endif
486 bool first_player = m_kart_widgets.size() == 0;
487
488 if (UserConfigParams::logGUI())
489 Log::info("KartSelectionScreen", "joinPlayer() invoked");
490 if (!m_multiplayer && !first_player) return false;
491
492 assert (m_dispatcher != NULL);
493
494 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
495 if (w == NULL)
496 {
497 Log::error("KartSelectionScreen", "joinPlayer(): Called outside of "
498 "kart selection screen.");
499 return false;
500 }
501 else if (device == NULL)
502 {
503 if (!NetworkConfig::get()->isNetworkAIInstance())
504 {
505 Log::error("KartSelectionScreen", "joinPlayer(): Received null "
506 "device pointer");
507 }
508 return false;
509 }
510
511 if (StateManager::get()->activePlayerCount() >= MAX_PLAYER_COUNT)
512 {
513 Log::error("KartSelectionScreen", "Maximum number of players "
514 "reached");
515 SFXManager::get()->quickSound( "anvil" );
516 return false;
517 }
518
519 // ---- Create new active player
520 PlayerProfile* profile_to_use = p == NULL ?
521 PlayerManager::getCurrentPlayer() : p;
522
523 // Make sure enough guest character exists. At this stage this player has
524 // not been added, so the number of guests requested for the first player
525 // is 0 --> forcing at least one real player.
526 if (p == NULL)
527 {
528 PlayerManager::get()->createGuestPlayers(
529 StateManager::get()->activePlayerCount());
530 }
531 if (!first_player && p == NULL)
532 {
533 // Give each player a different start profile
534 const int num_active_players = StateManager::get()->activePlayerCount();
535 profile_to_use = PlayerManager::get()->getPlayer(num_active_players);
536
537 removeMultiplayerMessage();
538 }
539
540 const int new_player_id =
541 StateManager::get()->createActivePlayer(profile_to_use, device);
542 StateManager::ActivePlayer* aplayer =
543 StateManager::get()->getActivePlayer(new_player_id);
544
545 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
546 assert(tabs != NULL);
547
548 std::string selected_kart_group =
549 tabs->getSelectionIDString(PLAYER_ID_GAME_MASTER);
550
551 // ---- Get available area for karts
552 // make a copy of the area, ands move it to be outside the screen
553 Widget* kartsAreaWidget = getWidget("playerskarts");
554 // start at the rightmost of the screen
555 const int shift = irr_driver->getFrameSize().Width;
556 core::recti kartsArea(kartsAreaWidget->m_x + shift,
557 kartsAreaWidget->m_y,
558 kartsAreaWidget->m_x + shift + kartsAreaWidget->m_w,
559 kartsAreaWidget->m_y + kartsAreaWidget->m_h);
560
561 // ---- Create player/kart widget
562 PlayerKartWidget* newPlayerWidget =
563 new PlayerKartWidget(this, aplayer, kartsArea, m_kart_widgets.size(),
564 selected_kart_group);
565
566 manualAddWidget(newPlayerWidget);
567 m_kart_widgets.push_back(newPlayerWidget);
568
569 newPlayerWidget->add();
570 // From network kart selection, the player name is already defined
571 if (p != NULL)
572 {
573 newPlayerWidget->getPlayerNameSpinner()->setActive(false);
574 newPlayerWidget->getPlayerNameSpinner()->setCustomText(p->getName());
575 }
576
577 // ---- Divide screen space among all karts
578 const int amount = m_kart_widgets.size();
579 Widget* fullarea = getWidget("playerskarts");
580
581 // in this special case, leave room for a message on the right
582 if (m_multiplayer && first_player)
583 {
584 if (p == NULL)
585 addMultiplayerMessage();
586 const int splitWidth = fullarea->m_w / 2;
587 m_kart_widgets[0].move( fullarea->m_x, fullarea->m_y, splitWidth,
588 fullarea->m_h );
589 }
590 else
591 {
592 const int splitWidth = fullarea->m_w / amount;
593
594 for (int n=0; n<amount; n++)
595 {
596 m_kart_widgets[n].move( fullarea->m_x + splitWidth * n,
597 fullarea->m_y, splitWidth, fullarea->m_h);
598 }
599 }
600
601 // select something (anything) in the ribbon; by default, only the
602 // game master has something selected. Thus, when a new player joins,
603 // we need to select something for them
604 w->setSelection(new_player_id, new_player_id, true);
605
606 //newPlayerWidget->m_player_ident_spinner
607 // ->setFocusForPlayer(new_player_id);
608
609 if (!m_multiplayer)
610 {
611 input_manager->getDeviceManager()->setSinglePlayer(StateManager::get()
612 ->getActivePlayer(0));
613 }
614
615 return true;
616 } // joinPlayer
617
618 // -----------------------------------------------------------------------------
619
playerQuit(StateManager::ActivePlayer * player)620 bool KartSelectionScreen::playerQuit(StateManager::ActivePlayer* player)
621 {
622 int player_id = -1;
623
624 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
625 if (w == NULL)
626 {
627 Log::error("KartSelectionScreen", "playerQuit() called "
628 "outside of kart selection screen, "
629 "or the XML file for this screen was changed without "
630 "adapting the code accordingly");
631 return false;
632 }
633
634 // If last player quits, return to main menu
635 if (m_kart_widgets.size() <= 1)
636 {
637 StateManager::get()->escapePressed();
638 return true;
639 }
640
641 std::map<PlayerKartWidget*, std::string> selections;
642
643 // Find the player ID associated to this player
644 for (unsigned int n=0; n<m_kart_widgets.size(); n++)
645 {
646 if (m_kart_widgets[n].getAssociatedPlayer() == player)
647 {
648 // Check that this player has not already confirmed,
649 // then they can't back out
650 if (m_kart_widgets[n].isReady())
651 {
652 SFXManager::get()->quickSound( "anvil" );
653 return true;
654 }
655
656 player_id = n;
657 }
658 else
659 {
660 selections[m_kart_widgets.get(n)] =
661 m_kart_widgets[n].getKartInternalName();
662 }
663 }
664 if (player_id == -1)
665 {
666 Log::warn("KartSelectionScreen", "playerQuit cannot find "
667 "passed player");
668 return false;
669 }
670 if(UserConfigParams::logGUI())
671 Log::info("KartSelectionScreen", "playerQuit(%d)", player_id);
672
673 // Just a cheap way to check if there is any discrepancy
674 // between m_kart_widgets and the active player array
675 assert( m_kart_widgets.size() == StateManager::get()->activePlayerCount());
676
677 // unset selection of this player
678 GUIEngine::focusNothingForPlayer(player_id);
679
680 // delete a previous removed widget that didn't have time to fully shrink
681 // yet.
682 // TODO: handle multiple shrinking widgets gracefully?
683 if (m_removed_widget != NULL)
684 {
685 manualRemoveWidget(m_removed_widget);
686 delete m_removed_widget;
687 m_removed_widget = NULL;
688 }
689
690 // keep the removed kart a while, for the 'disappear' animation
691 // to take place
692 m_removed_widget = m_kart_widgets.remove(player_id);
693
694 // Tell the StateManager to remove this player
695 StateManager::get()->removeActivePlayer(player_id);
696
697 addMultiplayerMessage();
698
699 // Karts count changed, maybe order too, so renumber them.
700 renumberKarts();
701
702 // Tell the removed widget to perform the shrinking animation (which will
703 // be updated in onUpdate, and will stop when the widget has disappeared)
704 Widget* fullarea = getWidget("playerskarts");
705 m_removed_widget->move(m_removed_widget->m_x + m_removed_widget->m_w/2,
706 fullarea->m_y + fullarea->m_h, 0, 0);
707
708 // update selections
709
710 const unsigned int amount = m_kart_widgets.size();
711 for (unsigned int n=0; n<amount; n++)
712 {
713 const std::string& selectedKart = selections[m_kart_widgets.get(n)];
714 if (selectedKart.size() > 0)
715 {
716 //std::cout << m_kart_widgets[n].getAssociatedPlayer()
717 // ->getProfile()->getName() << " selected "
718 // << selectedKart.c_str() << "\n";
719 const bool success = w->setSelection(selectedKart, n, true);
720 if (!success)
721 {
722 Log::warn("KartSelectionScreen", "Failed to select kart %s"
723 " for player %u, what's going on??", selectedKart.c_str(),n);
724 }
725 }
726 }
727
728
729 // check if all players are ready
730 bool allPlayersReady = true;
731 for (unsigned int n=0; n<amount; n++)
732 {
733 if (!m_kart_widgets[n].isReady())
734 {
735 allPlayersReady = false;
736 break;
737 }
738 }
739 if (allPlayersReady && (!m_multiplayer || amount > 1)) allPlayersDone();
740
741 return true;
742 } // playerQuit
743
744 // ----------------------------------------------------------------------------
745
onUpdate(float delta)746 void KartSelectionScreen::onUpdate(float delta)
747 {
748 // Dispatch the onUpdate event to each kart, so they can perform their
749 // animation if any
750 const int amount = m_kart_widgets.size();
751 for (int n=0; n<amount; n++)
752 {
753 m_kart_widgets[n].onUpdate(delta);
754 }
755
756 // When a kart widget is removed, it's a kept a while, for the disappear
757 // animation to take place
758 if (m_removed_widget != NULL)
759 {
760 m_removed_widget->onUpdate(delta);
761
762 if (m_removed_widget->m_w == 0 || m_removed_widget->m_h == 0)
763 {
764 // destruct when too small (for "disappear" effects)
765 manualRemoveWidget(m_removed_widget);
766 delete m_removed_widget;
767 m_removed_widget = NULL;
768 }
769 }
770 } // onUpdate
771
772 // ----------------------------------------------------------------------------
773
playerConfirm(const int player_id)774 void KartSelectionScreen::playerConfirm(const int player_id)
775 {
776 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
777 assert(w != NULL);
778 const std::string selection = w->getSelectionIDString(player_id);
779 if (StringUtils::startsWith(selection, ID_LOCKED) && !m_multiplayer)
780 {
781 unlock_manager->playLockSound();
782 return;
783 }
784
785 if (m_kart_widgets[player_id].getKartInternalName().size() == 0 ||
786 m_kart_widgets[player_id].getKartInternalName() == RibbonWidget::NO_ITEM_ID)
787 {
788 SFXManager::get()->quickSound( "anvil" );
789 return;
790 }
791
792 const int amount = m_kart_widgets.size();
793
794 // Check if we have enough karts for everybody. If there are more players
795 // than karts then just allow duplicates
796 const int available_kart_count = (int) w->getItems().size();
797 const bool will_need_duplicates = (amount > available_kart_count);
798
799 // make sure no other player selected the same identity or kart
800 for (int n=0; n<amount; n++)
801 {
802 if (n == player_id) continue; // don't check a kart against itself
803
804 const bool player_ready = m_kart_widgets[n].isReady();
805 const bool ident_conflict =
806 !m_kart_widgets[n].getAssociatedPlayer()->getProfile()
807 ->isGuestAccount() &&
808 m_kart_widgets[n].getAssociatedPlayer()->getProfile() ==
809 m_kart_widgets[player_id].getAssociatedPlayer()->getProfile();
810 const bool kart_conflict = sameKart(m_kart_widgets[n],
811 m_kart_widgets[player_id]);
812
813 if (player_ready && (ident_conflict || kart_conflict) &&
814 !will_need_duplicates)
815 {
816 if (UserConfigParams::logGUI())
817 Log::warn("KartSelectionScreen", "You can't select this identity "
818 "or kart, someone already took it!!");
819
820 SFXManager::get()->quickSound( "anvil" );
821 return;
822 }
823
824 // If two PlayerKart entries are associated to the same ActivePlayer,
825 // something went wrong
826 assert(m_kart_widgets[n].getAssociatedPlayer() !=
827 m_kart_widgets[player_id].getAssociatedPlayer());
828 }
829
830 // Mark this player as ready to start
831 m_kart_widgets[player_id].markAsReady();
832
833 if (player_id == PLAYER_ID_GAME_MASTER)
834 {
835 m_game_master_confirmed = true;
836 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
837 assert( tabs != NULL );
838 tabs->setActive(false);
839 }
840
841 // validate choices to notify player of duplicates
842 const bool names_ok = validateIdentChoices();
843 const bool karts_ok = validateKartChoices();
844
845 if (!names_ok || !karts_ok) return;
846
847 // check if all players are ready
848 bool allPlayersReady = true;
849 for (int n=0; n<amount; n++)
850 {
851 if (!m_kart_widgets[n].isReady())
852 {
853 allPlayersReady = false;
854 break;
855 }
856 }
857
858 if (allPlayersReady && (!m_multiplayer || amount > 1)) allPlayersDone();
859 } // playerConfirm
860
861 // ----------------------------------------------------------------------------
862
updateKartStats(uint8_t widget_id,const std::string & selection)863 void KartSelectionScreen::updateKartStats(uint8_t widget_id,
864 const std::string& selection)
865 {
866 KartStatsWidget* w = m_kart_widgets[widget_id].m_kart_stats;
867 assert(w != NULL);
868
869 const KartProperties *kp =
870 kart_properties_manager->getKart(selection);
871 // Adjust for online addon karts
872 if (kp && kp->isAddon() && NetworkConfig::get()->isNetworking() &&
873 NetworkConfig::get()->useTuxHitboxAddon())
874 kp = kart_properties_manager->getKart("tux");
875 if (kp != NULL)
876 {
877 w->setValues(kp, m_kart_widgets[widget_id].getHandicap());
878 w->update(0);
879 }
880 else
881 {
882 w->hideAll();
883 w->update(0);
884 }
885 }
886
887 // ----------------------------------------------------------------------------
updateKartWidgetModel(int widget_id,const std::string & selection,const irr::core::stringw & selectionText,float kart_color)888 void KartSelectionScreen::updateKartWidgetModel(int widget_id,
889 const std::string& selection,
890 const irr::core::stringw& selectionText, float kart_color)
891 {
892 // Update the displayed model
893 ModelViewWidget* w3 = m_kart_widgets[widget_id].m_model_view;
894 assert( w3 != NULL );
895
896 // set the color of the name label
897 m_kart_widgets[widget_id].m_kart_name->setColor(GUIEngine::getSkin()->getColor("text::neutral"));
898
899 if (selection == RANDOM_KART_ID)
900 {
901 // Random kart
902 scene::IMesh* model =
903 ItemManager::getItemModel(Item::ITEM_BONUS_BOX);
904
905 w3->clearModels();
906 core::matrix4 model_location;
907 model_location.setTranslation(core::vector3df(0.0f, -12.0f, 0.0f));
908 model_location.setScale(core::vector3df(35.0f, 35.0f, 35.0f));
909 w3->addModel(model, model_location);
910 w3->update(0);
911 m_kart_widgets[widget_id].m_kart_name
912 ->setText( _("Random Kart"), false );
913 }
914 // selection contains the name of the kart, so check only for substr
915 else if (StringUtils::startsWith(selection, ID_LOCKED) && !m_multiplayer)
916 {
917 w3->clearModels();
918 core::matrix4 model_location;
919 model_location.setScale(core::vector3df(15.0f, 15.0f, 15.0f));
920 file_manager->pushTextureSearchPath
921 (file_manager->getAsset(FileManager::MODEL,""), "models");
922 w3->addModel(irr_driver->getAnimatedMesh(
923 file_manager->getAsset(FileManager::MODEL, "chest.spm"))
924 ->getMesh(20), model_location);
925 file_manager->popTextureSearchPath();
926 w3->update(0);
927
928 if (m_multiplayer)
929 {
930 m_kart_widgets[widget_id].m_kart_name
931 ->setText(_("Locked"), false );
932 }
933 else
934 {
935 m_kart_widgets[widget_id].m_kart_name
936 ->setText(_("Locked : solve active challenges to gain access to more!"), false );
937 }
938 }
939 else
940 {
941 const KartProperties *kp =
942 kart_properties_manager->getKart(selection);
943 if (kp != NULL)
944 {
945 const KartModel &kart_model = kp->getMasterKartModel();
946
947 float scale = 35.0f;
948 if (kart_model.getLength() > 1.45f)
949 {
950 // if kart is too long, size it down a bit so that it fits
951 scale = 30.0f;
952 }
953
954 core::matrix4 model_location;
955 model_location.setScale(core::vector3df(scale, scale, scale));
956 w3->clearModels();
957 const bool has_win_anime =
958 UserConfigParams::m_animated_characters &&
959 (((kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 ||
960 kart_model.getFrame(KartModel::AF_WIN_START) > -1) &&
961 kart_model.getFrame(KartModel::AF_WIN_END) > -1) ||
962 (kart_model.getFrame(KartModel::AF_SELECTION_START) > -1 &&
963 kart_model.getFrame(KartModel::AF_SELECTION_END) > -1));
964 w3->addModel( kart_model.getModel(), model_location,
965 has_win_anime ?
966 kart_model.getFrame(KartModel::AF_SELECTION_START) > -1 ?
967 kart_model.getFrame(KartModel::AF_SELECTION_START) :
968 kart_model.getFrame(KartModel::AF_WIN_LOOP_START) > -1 ?
969 kart_model.getFrame(KartModel::AF_WIN_LOOP_START) :
970 kart_model.getFrame(KartModel::AF_WIN_START) :
971 kart_model.getBaseFrame(),
972 has_win_anime ?
973 kart_model.getFrame(KartModel::AF_SELECTION_END) > -1 ?
974 kart_model.getFrame(KartModel::AF_SELECTION_END) :
975 kart_model.getFrame(KartModel::AF_WIN_END) :
976 kart_model.getBaseFrame(),
977 kart_model.getAnimationSpeed());
978
979 w3->getModelViewRenderInfo()->setHue(kart_color);
980 model_location.setScale(core::vector3df(1.0f, 1.0f, 1.0f));
981 for (unsigned i = 0; i < 4; i++)
982 {
983 model_location.setTranslation(kart_model
984 .getWheelGraphicsPosition(i).toIrrVector());
985 w3->addModel(kart_model.getWheelModel(i), model_location);
986 }
987
988 for (unsigned i = 0;
989 i < kart_model.getSpeedWeightedObjectsCount(); i++)
990 {
991 const SpeedWeightedObject& obj =
992 kart_model.getSpeedWeightedObject(i);
993 core::matrix4 swol = obj.m_location;
994 if (!obj.m_bone_name.empty())
995 {
996 core::matrix4 inv =
997 kart_model.getInverseBoneMatrix(obj.m_bone_name);
998 swol = inv * obj.m_location;
999 }
1000 w3->addModel(obj.m_model, swol, -1, -1, 0.0f, obj.m_bone_name);
1001 }
1002 //w3->update(0);
1003
1004 m_kart_widgets[widget_id].m_kart_name
1005 ->setText( selectionText.c_str(), false );
1006 }
1007 else
1008 Log::warn("KartSelectionScreen", "could not "
1009 "find a kart named '%s'",
1010 selection.c_str());
1011 }
1012 }
1013
1014 // ----------------------------------------------------------------------------
1015 /**
1016 * Adds a message to the screen which indicates that players must press fire to join.
1017 */
addMultiplayerMessage()1018 void KartSelectionScreen::addMultiplayerMessage()
1019 {
1020 Widget* fullarea = getWidget("playerskarts");
1021 const int splitWidth = fullarea->m_w / 2;
1022 int message_x = 0;
1023 if (m_kart_widgets.size() == 1)
1024 message_x = (int) (fullarea->m_x + splitWidth + splitWidth * 0.2f);
1025 else
1026 message_x = (int) (fullarea->m_x + splitWidth / 2 + splitWidth * 0.2f);
1027
1028 if (m_kart_widgets.size() < 2 && m_multiplayer_message == NULL)
1029 {
1030 m_multiplayer_message = new BubbleWidget();
1031 m_multiplayer_message->m_properties[PROP_TEXT_ALIGN] = "center";
1032 m_multiplayer_message->setText(_("Everyone:\n"
1033 "Press the 'Select' button to join the game"));
1034 m_multiplayer_message->m_x = message_x;
1035 m_multiplayer_message->m_y = (int) (fullarea->m_y + fullarea->m_h * 0.3f);
1036 m_multiplayer_message->m_w = (int) (splitWidth * 0.6f);
1037 m_multiplayer_message->m_h = (int) (fullarea->m_h * 0.6f);
1038 m_multiplayer_message->setFocusable(false);
1039 m_multiplayer_message->add();
1040 manualAddWidget(m_multiplayer_message);
1041 }
1042 else if(m_multiplayer_message != NULL)
1043 {
1044 m_multiplayer_message->move(message_x, (int) (fullarea->m_y + fullarea->m_h * 0.3f),
1045 (int) (splitWidth * 0.6f), (int) (fullarea->m_h * 0.6f));
1046 }
1047 } // addMultiplayerMessage
1048
1049 // ----------------------------------------------------------------------------
1050 /**
1051 * Remove the multiplayer message.
1052 */
removeMultiplayerMessage()1053 void KartSelectionScreen::removeMultiplayerMessage()
1054 {
1055 if (m_multiplayer_message != NULL)
1056 {
1057 manualRemoveWidget(m_multiplayer_message);
1058 m_multiplayer_message->getIrrlichtElement()->remove();
1059 m_multiplayer_message->elementRemoved();
1060 delete m_multiplayer_message;
1061 m_multiplayer_message = NULL;
1062 }
1063 } // removeMultiplayerMessage
1064
1065 // ----------------------------------------------------------------------------
1066 /**
1067 * Callback handling events from the kart selection menu
1068 */
eventCallback(Widget * widget,const std::string & name,const int player_id)1069 void KartSelectionScreen::eventCallback(Widget* widget,
1070 const std::string& name,
1071 const int player_id)
1072 {
1073 // don't allow changing group after someone confirmed
1074 if (name == "kartgroups" && !m_game_master_confirmed)
1075 {
1076 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
1077 assert(tabs != NULL);
1078 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
1079 assert(w != NULL);
1080
1081 setKartsFromCurrentGroup();
1082
1083 const std::string &selected_kart_group =
1084 tabs->getSelectionIDString(PLAYER_ID_GAME_MASTER);
1085
1086 UserConfigParams::m_last_used_kart_group = selected_kart_group;
1087
1088 RandomGenerator random;
1089
1090 const int num_players = m_kart_widgets.size();
1091 for (int n=0; n<num_players; n++)
1092 {
1093 // The game master is the one that can change the groups, leave
1094 // his focus on the tabs for others, remove focus from kart that
1095 // might no more exist in this tab.
1096 if (n != PLAYER_ID_GAME_MASTER)
1097 GUIEngine::focusNothingForPlayer(n);
1098
1099 if (!m_kart_widgets[n].isReady())
1100 {
1101 // try to preserve the same kart for each player (except for
1102 // game master, since it's the one that can change the
1103 // groups, so focus for this player must remain on the tabs)
1104 const std::string& selected_kart =
1105 m_kart_widgets[n].getKartInternalName();
1106 if (!w->setSelection( selected_kart, n,
1107 n != PLAYER_ID_GAME_MASTER))
1108 {
1109 // if we get here, it means one player "lost" his kart in
1110 // the tab switch
1111 if (UserConfigParams::logGUI())
1112 Log::info("KartSelectionScreen", "Player %u"
1113 " lost their selection when switching tabs!!!",n);
1114
1115 // Select a random kart in this case
1116 const int count = (int) w->getItems().size();
1117 if (count > 0)
1118 {
1119 // FIXME: two players may be given the same kart by
1120 // the use of random
1121 const int random_id = random.get( count );
1122
1123 // select kart for players > 0 (player 0 is the one
1124 // that can change the groups, so focus for player 0
1125 // must remain on the tabs)
1126 const bool success =
1127 w->setSelection( random_id, n,
1128 n != PLAYER_ID_GAME_MASTER );
1129 if (!success)
1130 Log::warn("KartSelectionScreen",
1131 "setting kart of player %u failed");
1132 }
1133 else
1134 {
1135 Log::warn("KartSelectionScreen", " 0 items "
1136 "in the ribbon");
1137 }
1138 }
1139 }
1140 } // end for
1141 }
1142 else if (name == "karts")
1143 {
1144 if (m_kart_widgets.size() > unsigned(player_id))
1145 playerConfirm(player_id);
1146 }
1147 else if (name == "continue")
1148 {
1149 if (m_kart_widgets.size() > unsigned(player_id))
1150 playerConfirm(player_id);
1151 }
1152 else if (name == "back")
1153 {
1154 StateManager::get()->escapePressed();
1155 }
1156 else
1157 {
1158 // Transmit to all subwidgets, maybe *they* care about this event
1159 const int amount = m_kart_widgets.size();
1160 for (int n=0; n<amount; n++)
1161 {
1162 m_kart_widgets[n].transmitEvent(widget, name, player_id);
1163 }
1164
1165 // those events may mean that a player selection changed, so
1166 // validate again
1167 validateIdentChoices();
1168 validateKartChoices();
1169 }
1170 } // eventCallback
1171
1172 // ----------------------------------------------------------------------------
1173
setMultiplayer(bool multiplayer)1174 void KartSelectionScreen::setMultiplayer(bool multiplayer)
1175 {
1176 m_multiplayer = multiplayer;
1177 } // setMultiplayer
1178
1179 // ----------------------------------------------------------------------------
1180
onEscapePressed()1181 bool KartSelectionScreen::onEscapePressed()
1182 {
1183 m_go_to_overworld_next = false; // valid once
1184 m_must_delete_on_back = true; // delete the screen
1185 if (m_from_overworld)
1186 {
1187 m_from_overworld = false; // valid once
1188 OverWorld::enterOverWorld();
1189 return false;
1190 }
1191 else
1192 {
1193 return true;
1194 }
1195 }
1196
1197 // ----------------------------------------------------------------------------
1198
1199 #if 0
1200 #pragma mark -
1201 #pragma mark KartSelectionScreen (private)
1202 #endif
1203
allPlayersDone()1204 void KartSelectionScreen::allPlayersDone()
1205 {
1206 input_manager->setMasterPlayerOnly(true);
1207
1208 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
1209 assert(tabs != NULL);
1210
1211 std::string selected_kart_group =
1212 tabs->getSelectionIDString(PLAYER_ID_GAME_MASTER);
1213
1214 UserConfigParams::m_last_used_kart_group = selected_kart_group;
1215
1216 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
1217 assert( w != NULL );
1218
1219 const PtrVector< StateManager::ActivePlayer, HOLD >& players =
1220 StateManager::get()->getActivePlayers();
1221
1222 // ---- Print selection (for debugging purposes)
1223 if(UserConfigParams::logGUI())
1224 {
1225 Log::info("KartSelectionScreen", "players : %d",players.size());
1226
1227 for (unsigned int n=0; n<players.size(); n++)
1228 {
1229 Log::info("KartSelectionScreen", " Player %u is %s on %s",n,
1230 core::stringc(
1231 players[n].getConstProfile()->getName().c_str()).c_str(),
1232 players[n].getDevice()->getName().c_str());
1233 }
1234 }
1235
1236 for (unsigned int n=0; n<players.size(); n++)
1237 {
1238 StateManager::get()->getActivePlayer(n)->getProfile()
1239 ->incrementUseFrequency();
1240 }
1241 // ---- Give player info to race manager
1242 RaceManager::get()->setNumPlayers(players.size());
1243
1244 // ---- Manage 'random kart' selection(s)
1245 RandomGenerator random;
1246
1247 std::vector<ItemDescription> items = w->getItems();
1248
1249 // remove the 'random' item itself
1250 const int item_count = (int) items.size();
1251 for (int n=0; n<item_count; n++)
1252 {
1253 if (items[n].m_code_name == RANDOM_KART_ID)
1254 {
1255 items[n].m_code_name = ID_DONT_USE;
1256 break;
1257 }
1258 }
1259
1260 // pick random karts
1261 const int kart_count = m_kart_widgets.size();
1262 for (int n = 0; n < kart_count; n++)
1263 {
1264 std::string selected_kart = m_kart_widgets[n].m_kart_internal_name;
1265
1266 if (selected_kart == RANDOM_KART_ID)
1267 {
1268 // don't select an already selected kart
1269 // to prevent infinite loop in case they are all locked
1270 int count = 0;
1271 bool done = false;
1272 do
1273 {
1274 int random_id = random.get(item_count);
1275 // valid kart if it can bt used, and is either not locked,
1276 // or it's a multiplayer race.
1277 if (items[random_id].m_code_name != ID_DONT_USE &&
1278 (!StringUtils::startsWith(items[random_id].m_code_name, ID_LOCKED)
1279 || m_multiplayer) )
1280 {
1281 selected_kart = items[random_id].m_code_name;
1282 done = true;
1283 }
1284 items[random_id].m_code_name = ID_DONT_USE;
1285 count++;
1286 if (count > 100) return;
1287 }
1288 while (!done);
1289 }
1290 else
1291 {
1292 // mark the item as taken
1293 for (int i=0; i<item_count; i++)
1294 {
1295 if (items[i].m_code_name ==
1296 m_kart_widgets[n].m_kart_internal_name)
1297 {
1298 items[i].m_code_name = ID_DONT_USE;
1299 break;
1300 }
1301 }
1302 }
1303
1304 if (n == PLAYER_ID_GAME_MASTER)
1305 {
1306 UserConfigParams::m_default_kart = selected_kart;
1307 }
1308
1309 RaceManager::get()->setPlayerKart(n, selected_kart);
1310
1311 // Set handicap if needed
1312 if (m_multiplayer && UserConfigParams::m_per_player_difficulty)
1313 RaceManager::get()->setPlayerHandicap(n, m_kart_widgets[n].getHandicap());
1314 }
1315
1316 // ---- Switch to assign mode
1317 input_manager->getDeviceManager()->setAssignMode(ASSIGN);
1318
1319 StateManager::ActivePlayer *ap = m_multiplayer
1320 ? NULL
1321 : StateManager::get()->getActivePlayer(0);
1322 input_manager->getDeviceManager()->setSinglePlayer(ap);
1323
1324 // ---- Go to next screen or return to overworld
1325 if (m_from_overworld || m_go_to_overworld_next)
1326 {
1327 m_from_overworld = false; // valid once
1328 m_go_to_overworld_next = false;
1329 OverWorld::enterOverWorld();
1330 }
1331 else
1332 {
1333 RaceSetupScreen::getInstance()->push();
1334 }
1335 } // allPlayersDone
1336
1337 // ----------------------------------------------------------------------------
1338
validateIdentChoices()1339 bool KartSelectionScreen::validateIdentChoices()
1340 {
1341 bool ok = true;
1342
1343 const int amount = m_kart_widgets.size();
1344
1345 // reset all marks, we'll re-add them next if errors are still there
1346 for (int n=0; n<amount; n++)
1347 {
1348 // first check if the player name widget is still there, it won't
1349 // be for those that confirmed
1350 if (m_kart_widgets[n].m_player_ident_spinner != NULL)
1351 {
1352 m_kart_widgets[n].m_player_ident_spinner->markAsCorrect();
1353
1354 // verify internal consistency in debug mode
1355 if (m_multiplayer)
1356 {
1357 int spinner_value = m_kart_widgets[n].m_player_ident_spinner->getValue();
1358 if (UserConfigParams::m_per_player_difficulty)
1359 spinner_value /= 2;
1360 assert(m_kart_widgets[n].getAssociatedPlayer()->getProfile() ==
1361 PlayerManager::get()->getPlayer(spinner_value));
1362 }
1363 }
1364 }
1365
1366 // perform actual checking
1367 for (int n=0; n<amount; n++)
1368 {
1369 // skip players that took a guest account, they can be many on the
1370 // same identity in this case
1371 if (m_kart_widgets[n].getAssociatedPlayer()->getProfile()
1372 ->isGuestAccount())
1373 {
1374 continue;
1375 }
1376
1377 // check if another kart took the same identity as the current one
1378 for (int m=n+1; m<amount; m++)
1379 {
1380
1381 // check if 2 players took the same name
1382 if (m_kart_widgets[n].getAssociatedPlayer()->getProfile() ==
1383 m_kart_widgets[m].getAssociatedPlayer()->getProfile())
1384 {
1385 // two players took the same name. check if one is ready
1386 if (!m_kart_widgets[n].isReady() &&
1387 m_kart_widgets[m].isReady())
1388 {
1389 // player m is ready, so player n should not choose
1390 // this name
1391 m_kart_widgets[n].m_player_ident_spinner
1392 ->markAsIncorrect();
1393 }
1394 else if (m_kart_widgets[n].isReady() &&
1395 !m_kart_widgets[m].isReady())
1396 {
1397 // player n is ready, so player m should not
1398 // choose this name
1399 m_kart_widgets[m].m_player_ident_spinner
1400 ->markAsIncorrect();
1401 }
1402 else if (m_kart_widgets[n].isReady() &&
1403 m_kart_widgets[m].isReady())
1404 {
1405 // it should be impossible for two players to confirm
1406 // they're ready with the same name
1407 assert(false);
1408 }
1409
1410 ok = false;
1411 }
1412 } // end for
1413 }
1414
1415 return ok;
1416 } // validateIdentChoices
1417
1418 // -----------------------------------------------------------------------------
1419
validateKartChoices()1420 bool KartSelectionScreen::validateKartChoices()
1421 {
1422 bool ok = true;
1423
1424 const unsigned int amount = m_kart_widgets.size();
1425
1426 // reset all marks, we'll re-add them next if errors are still there
1427 for (unsigned int n=0; n<amount; n++)
1428 {
1429 m_kart_widgets[n].m_model_view->unsetBadge(BAD_BADGE);
1430 }
1431
1432 // Check if we have enough karts for everybody. If there are more
1433 // players than karts then just allow duplicates
1434 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
1435 assert( w != NULL );
1436 const unsigned int availableKartCount = (unsigned int)w->getItems().size();
1437 if (amount > availableKartCount) return true;
1438
1439 // Check everyone for duplicates
1440 for (unsigned int n=0; n<amount; n++)
1441 {
1442 for (unsigned int m=n+1; m<amount; m++)
1443 {
1444 // check if 2 players took the same name
1445 if (sameKart(m_kart_widgets[n], m_kart_widgets[m]))
1446 {
1447 if (UserConfigParams::logGUI())
1448 {
1449 Log::warn("KartSelectionScreen", "Kart conflict!!");
1450 Log::warn("KartSelectionScreen", " Player %u chose %s",n,
1451 m_kart_widgets[n].getKartInternalName().c_str());
1452 Log::warn("KartSelectionScreen", " Player %u chose %s",m,
1453 m_kart_widgets[m].getKartInternalName().c_str());
1454 }
1455
1456 // two players took the same kart. check if one is ready
1457 if (!m_kart_widgets[n].isReady() &&
1458 m_kart_widgets[m].isReady())
1459 {
1460 if (UserConfigParams::logGUI())
1461 Log::info("KartSelectionScreen", " --> Setting red badge on player %u", n);
1462
1463 // player m is ready, so player n should not choose
1464 // this name
1465 m_kart_widgets[n].m_model_view->setBadge(BAD_BADGE);
1466 }
1467 else if (m_kart_widgets[n].isReady() &&
1468 !m_kart_widgets[m].isReady())
1469 {
1470 if (UserConfigParams::logGUI())
1471 Log::info("KartSelectionScreen", " --> Setting red badge on player %u",m);
1472
1473 // player n is ready, so player m should not
1474 // choose this name
1475 m_kart_widgets[m].m_model_view->setBadge(BAD_BADGE);
1476 }
1477 else if (m_kart_widgets[n].isReady() &&
1478 m_kart_widgets[m].isReady())
1479 {
1480 // it should be impossible for two players to confirm
1481 // they're ready with the same kart
1482 assert(false);
1483 }
1484
1485 // we know it's not ok (but don't stop right now, all bad
1486 // ones need red badges)
1487 ok = false;
1488 }
1489 } // end for
1490 }
1491
1492 return ok;
1493
1494 } // validateKartChoices
1495
1496 // ----------------------------------------------------------------------------
1497
renumberKarts()1498 void KartSelectionScreen::renumberKarts()
1499 {
1500 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
1501 assert( w != NULL );
1502 Widget* fullarea = getWidget("playerskarts");
1503 int splitWidth = fullarea->m_w / m_kart_widgets.size();
1504 if (m_kart_widgets.size() == 1)
1505 splitWidth /= 2;
1506
1507 for (unsigned int n=0; n < m_kart_widgets.size(); n++)
1508 {
1509 m_kart_widgets[n].setPlayerID(n);
1510 m_kart_widgets[n].move( fullarea->m_x + splitWidth*n, fullarea->m_y,
1511 splitWidth, fullarea->m_h );
1512 }
1513
1514 w->updateItemDisplay();
1515 } // renumberKarts
1516
1517 // ----------------------------------------------------------------------------
getUsableKarts(const std::string & selected_kart_group)1518 PtrVector<const KartProperties, REF> KartSelectionScreen::getUsableKarts(
1519 const std::string& selected_kart_group)
1520 {
1521 PtrVector<const KartProperties, REF> karts;
1522 for(unsigned int i=0; i<kart_properties_manager->getNumberOfKarts(); i++)
1523 {
1524 const KartProperties* prop = kart_properties_manager->getKartById(i);
1525 // Ignore karts that are not in the selected group
1526 if((selected_kart_group != ALL_KART_GROUPS_ID &&
1527 !prop->isInGroup(selected_kart_group)) || isIgnored(prop->getIdent()))
1528 continue;
1529 karts.push_back(prop);
1530 }
1531 karts.insertionSort();
1532 return karts;
1533 } // getUsableKarts
1534
1535 // ----------------------------------------------------------------------------
1536
setKartsFromCurrentGroup()1537 void KartSelectionScreen::setKartsFromCurrentGroup()
1538 {
1539 RibbonWidget* tabs = getWidget<RibbonWidget>("kartgroups");
1540 assert(tabs != NULL);
1541
1542 std::string selected_kart_group =
1543 tabs->getSelectionIDString(PLAYER_ID_GAME_MASTER);
1544
1545 UserConfigParams::m_last_used_kart_group = selected_kart_group;
1546
1547 // This can happen if addons are removed so that also the previously
1548 // selected kart group is removed. In this case, select the
1549 // 'standard' group
1550 if (selected_kart_group != ALL_KART_GROUPS_ID &&
1551 !kart_properties_manager->getKartsInGroup(selected_kart_group).size())
1552 {
1553 selected_kart_group = DEFAULT_GROUP_NAME;
1554 }
1555
1556 DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
1557 w->clearItems();
1558
1559 int usable_kart_count = 0;
1560 PtrVector<const KartProperties, REF> karts = getUsableKarts(selected_kart_group);
1561
1562 if (karts.empty())
1563 {
1564 // In network this will happen if no addons kart on server
1565 PtrVector<const KartProperties, REF> new_karts =
1566 getUsableKarts(DEFAULT_GROUP_NAME);
1567 std::swap(karts.m_contents_vector, new_karts.m_contents_vector);
1568 tabs->select(DEFAULT_GROUP_NAME, PLAYER_ID_GAME_MASTER);
1569 }
1570
1571 for(unsigned int i=0; i<karts.size(); i++)
1572 {
1573 const KartProperties* prop = karts.get(i);
1574 if (PlayerManager::getCurrentPlayer()->isLocked(prop->getIdent()) &&
1575 !m_multiplayer && !NetworkConfig::get()->isNetworking())
1576 {
1577 w->addItem(_("Locked : solve active challenges to gain access to more!"),
1578 ID_LOCKED + prop->getIdent(),
1579 prop->getAbsoluteIconFile(), LOCKED_BADGE,
1580 IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
1581 }
1582 else
1583 {
1584 w->addItem(prop->getName(),
1585 prop->getIdent(),
1586 prop->getAbsoluteIconFile(), 0,
1587 IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
1588 usable_kart_count++;
1589 }
1590 }
1591
1592 // add random
1593 if (usable_kart_count > 1)
1594 {
1595 w->addItem(_("Random Kart"), RANDOM_KART_ID, "/gui/icons/random_kart.png");
1596 }
1597
1598 w->updateItemDisplay();
1599 }
1600
1601 // ----------------------------------------------------------------------------
useContinueButton() const1602 bool KartSelectionScreen::useContinueButton() const
1603 {
1604 #ifdef MOBILE_STK
1605 if (m_multiplayer)
1606 return false;
1607 bool multitouch_enabled = (UserConfigParams::m_multitouch_active == 1 &&
1608 irr_driver->getDevice()->supportsTouchDevice()) ||
1609 UserConfigParams::m_multitouch_active > 1;
1610 return multitouch_enabled;
1611 #else
1612 return false;
1613 #endif
1614 } // useContinueButton
1615
1616 #if 0
1617 #pragma mark -
1618 #endif
1619
1620