1 //
2 // SuperTuxKart - a fun racing game with go-kart
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 "race/race_manager.hpp"
20
21 #include <iostream>
22 #include <algorithm>
23
24 #include "challenges/unlock_manager.hpp"
25 #include "config/player_manager.hpp"
26 #include "config/saved_grand_prix.hpp"
27 #include "config/stk_config.hpp"
28 #include "config/user_config.hpp"
29 #include "graphics/irr_driver.hpp"
30 #include "guiengine/message_queue.hpp"
31 #include "input/device_manager.hpp"
32 #include "input/input_manager.hpp"
33 #include "karts/abstract_kart.hpp"
34 #include "karts/controller/controller.hpp"
35 #include "karts/kart_properties_manager.hpp"
36 #include "main_loop.hpp"
37 #include "modes/capture_the_flag.hpp"
38 #include "modes/cutscene_world.hpp"
39 #include "modes/demo_world.hpp"
40 #include "modes/easter_egg_hunt.hpp"
41 #include "modes/follow_the_leader.hpp"
42 #include "modes/free_for_all.hpp"
43 #include "modes/overworld.hpp"
44 #include "modes/standard_race.hpp"
45 #include "modes/tutorial_world.hpp"
46 #include "modes/world.hpp"
47 #include "modes/three_strikes_battle.hpp"
48 #include "modes/soccer_world.hpp"
49 #include "network/protocol_manager.hpp"
50 #include "network/network_config.hpp"
51 #include "network/network_string.hpp"
52 #include "replay/replay_play.hpp"
53 #include "scriptengine/property_animator.hpp"
54 #include "states_screens/grand_prix_cutscene.hpp"
55 #include "states_screens/grand_prix_lose.hpp"
56 #include "states_screens/grand_prix_win.hpp"
57 #include "states_screens/kart_selection.hpp"
58 #include "states_screens/main_menu_screen.hpp"
59 #include "states_screens/state_manager.hpp"
60 #include "tracks/track_manager.hpp"
61 #include "utils/ptr_vector.hpp"
62 #include "utils/stk_process.hpp"
63 #include "utils/string_utils.hpp"
64 #include "utils/translation.hpp"
65
66 //=============================================================================================
67 RaceManager* g_race_manager[PT_COUNT];
68 //---------------------------------------------------------------------------------------------
get()69 RaceManager* RaceManager::get()
70 {
71 ProcessType type = STKProcess::getType();
72 return g_race_manager[type];
73 } // get
74
75 //---------------------------------------------------------------------------------------------
create()76 void RaceManager::create()
77 {
78 ProcessType type = STKProcess::getType();
79 g_race_manager[type] = new RaceManager();
80 } // create
81
82 //---------------------------------------------------------------------------------------------
destroy()83 void RaceManager::destroy()
84 {
85 ProcessType type = STKProcess::getType();
86 delete g_race_manager[type];
87 g_race_manager[type] = NULL;
88 } // destroy
89
90 //---------------------------------------------------------------------------------------------
clear()91 void RaceManager::clear()
92 {
93 memset(g_race_manager, 0, sizeof(g_race_manager));
94 } // clear
95
96 //---------------------------------------------------------------------------------------------
97 /** Constructs the race manager.
98 */
RaceManager()99 RaceManager::RaceManager()
100 {
101 // Several code depends on this, e.g. kart_properties
102 assert(DIFFICULTY_FIRST == 0);
103 m_num_karts = UserConfigParams::m_default_num_karts;
104 m_num_ghost_karts = 0;
105 m_difficulty = DIFFICULTY_HARD;
106 m_major_mode = MAJOR_MODE_SINGLE;
107 m_minor_mode = MINOR_MODE_NORMAL_RACE;
108 m_ai_superpower = SUPERPOWER_NONE;
109 m_track_number = 0;
110 m_coin_target = 0;
111 m_started_from_overworld = false;
112 m_have_kart_last_position_on_overworld = false;
113 m_num_local_players = 0;
114 m_hit_capture_limit = 0;
115 m_flag_return_ticks = stk_config->time2Ticks(20.0f);
116 m_flag_deactivated_ticks = stk_config->time2Ticks(3.0f);
117 setMaxGoal(0);
118 setTimeTarget(0.0f);
119 setReverseTrack(false);
120 setRecordRace(false);
121 setRaceGhostKarts(false);
122 setWatchingReplay(false);
123 setTrack("jungle");
124 m_default_ai_list.clear();
125 setNumPlayers(0);
126 setSpareTireKartNum(0);
127 } // RaceManager
128
129 //---------------------------------------------------------------------------------------------
130 /** Destructor for the race manager.
131 */
~RaceManager()132 RaceManager::~RaceManager()
133 {
134 } // ~RaceManager
135
136 //---------------------------------------------------------------------------------------------
137 /** Resets the race manager in preparation for a new race. It sets the
138 * counter of finished karts to zero. It is called by world when
139 * restarting a race.
140 */
reset()141 void RaceManager::reset()
142 {
143 m_num_finished_karts = 0;
144 m_num_finished_players = 0;
145 } // reset
146
147 // ----------------------------------------------------------------------------
148 /** Sets the default list of AI karts to use.
149 * \param ai_kart_list List of the identifier of the karts to use.
150 */
setDefaultAIKartList(const std::vector<std::string> & ai_list)151 void RaceManager::setDefaultAIKartList(const std::vector<std::string>& ai_list)
152 {
153 for(unsigned int i=0; i<ai_list.size(); i++)
154 {
155 const std::string &name=ai_list[i];
156 const KartProperties *kp = kart_properties_manager->getKart(name);
157 if(!kp)
158 {
159 Log::warn("RaceManager", "Kart '%s' is unknown and therefore ignored.",
160 name.c_str());
161 continue;
162 }
163 // This doesn't work anymore, since this is called when
164 // handling the command line options, at which time the
165 // player (and therefore the current slot) is not defined yet.
166 //if(unlock_manager->getCurrentSlot()->isLocked(name))
167 //{
168 // Log::info("RaceManager", "Kart '%s' is locked and therefore ignored.",
169 // name.c_str());
170 // continue;
171 //}
172 m_default_ai_list.push_back(name);
173 }
174 } // setDefaultAIKartList
175
176 //---------------------------------------------------------------------------------------------
177 /** \brief Sets a player kart (local and non-local).
178 * \param player_id Id of the player.
179 * \param ki Kart info structure for this player.
180 */
setPlayerKart(unsigned int player_id,const RemoteKartInfo & ki)181 void RaceManager::setPlayerKart(unsigned int player_id, const RemoteKartInfo& ki)
182 {
183 m_player_karts[player_id] = ki;
184 } // setPlayerKart
185
186 // ----------------------------------------------------------------------------
setPlayerKart(unsigned int player_id,const std::string & kart_name)187 void RaceManager::setPlayerKart(unsigned int player_id,
188 const std::string &kart_name)
189 {
190 const PlayerProfile* profile =
191 StateManager::get()->getActivePlayerProfile(player_id);
192 RemoteKartInfo rki(player_id, kart_name, profile->getName(), 0, false);
193 m_player_karts[player_id] = rki;
194 } // setPlayerKart
195
196 //---------------------------------------------------------------------------------------------
197 /** Sets additional information for a player to indicate which soccer team it
198 * belongs to.
199 */
setKartTeam(unsigned int player_id,KartTeam team)200 void RaceManager::setKartTeam(unsigned int player_id, KartTeam team)
201 {
202 assert(player_id < m_player_karts.size());
203
204 m_player_karts[player_id].setKartTeam(team);
205 } // setKartTeam
206
207 //---------------------------------------------------------------------------------------------
208 /** Sets the handicap for a player.
209 */
setPlayerHandicap(unsigned int player_id,HandicapLevel handicap)210 void RaceManager::setPlayerHandicap(unsigned int player_id, HandicapLevel handicap)
211 {
212 assert(player_id < m_player_karts.size());
213
214 m_player_karts[player_id].setHandicap(handicap);
215 } // setPlayerHandicap
216
217 //---------------------------------------------------------------------------------------------
218 /** Returns a pointer to the kart which has a given GP rank.
219 * \param n The rank (1 to number of karts) to look for.
220 */
getKartWithGPRank(unsigned int n)221 const AbstractKart *RaceManager::getKartWithGPRank(unsigned int n)
222 {
223 for(unsigned int i=0; i<m_kart_status.size(); i++)
224 if(m_kart_status[i].m_gp_rank == (int)n)
225 return World::getWorld()->getKart(i);
226 return NULL;
227 } // getKLartWithGPRank
228
229 //---------------------------------------------------------------------------------------------
230 /** Returns the GP rank (between 1 and number of karts) of a local player.
231 * \param player_id Local id of the player.
232 */
getLocalPlayerGPRank(const int player_id) const233 int RaceManager::getLocalPlayerGPRank(const int player_id) const
234 {
235 const int amount = (int)m_kart_status.size();
236 for (int n=0; n<amount; n++)
237 {
238 if (m_kart_status[n].m_local_player_id == player_id)
239 {
240 return m_kart_status[n].m_gp_rank;
241 }
242 }
243 return -1;
244 } // getLocalPlayerGPRank
245
246 //---------------------------------------------------------------------------------------------
247 /** Sets the number of players and optional the number of local players.
248 * \param num Number of players.
249 * \param local_players Number of local players, only used from networking.
250 */
setNumPlayers(int players,int local_players)251 void RaceManager::setNumPlayers(int players, int local_players)
252 {
253 // Clear all previous game info from network (like country code atm)
254 // The rest info need to be store for overworld, see #3980
255 for (RemoteKartInfo& rki : m_player_karts)
256 rki.setCountryCode("");
257 m_player_karts.resize(players);
258 if(local_players>-1)
259 m_num_local_players = local_players;
260 else
261 m_num_local_players = players;
262 } // setNumPlayers
263
264 // ----------------------------------------------------------------------------
265 /** Converst the difficulty given as a string into a Difficult enum. Defaults
266 * to HARD.
267 * \param difficulty The difficulty as string.
268 */
269 RaceManager::Difficulty
convertDifficulty(const std::string & difficulty)270 RaceManager::convertDifficulty(const std::string &difficulty)
271 {
272 if (difficulty == "novice")
273 return DIFFICULTY_EASY;
274 else if (difficulty == "intermediate")
275 return DIFFICULTY_MEDIUM;
276 else if (difficulty == "expert")
277 return DIFFICULTY_HARD;
278 else if (difficulty == "best")
279 return DIFFICULTY_BEST;
280 else
281 return DIFFICULTY_HARD;
282 } // convertDifficulty
283
284 //---------------------------------------------------------------------------------------------
285 /** Sets the difficulty to use.
286 * \param diff The difficulty to use.
287 */
setDifficulty(Difficulty diff)288 void RaceManager::setDifficulty(Difficulty diff)
289 {
290 m_difficulty = diff;
291 } // setDifficulty
292
293 //---------------------------------------------------------------------------------------------
294 /** Sets a single track to be used in the next race.
295 * \param track The identifier of the track to use.
296 */
setTrack(const std::string & track)297 void RaceManager::setTrack(const std::string& track)
298 {
299 m_tracks.clear();
300 m_tracks.push_back(track);
301
302 m_coin_target = 0;
303 } // setTrack
304
305 //---------------------------------------------------------------------------------------------
306 /** \brief Computes the list of random karts to be used for the AI.
307 * If a command line option specifies karts, they will be used first
308 */
computeRandomKartList()309 void RaceManager::computeRandomKartList()
310 {
311 int n = m_num_karts - (int)m_player_karts.size();
312 if(UserConfigParams::logMisc())
313 Log::info("RaceManager", "AI karts count = %d for m_num_karts = %d and "
314 "m_player_karts.size() = %d", n, m_num_karts, m_player_karts.size());
315
316 // If less kart selected than there are player karts, adjust the number of
317 // karts to the minimum
318 if(n<0)
319 {
320 m_num_karts -= n;
321 n = 0;
322 }
323
324 m_ai_kart_list.clear();
325
326 //Use the command line options AI list.
327 unsigned int m = std::min( (unsigned) m_num_karts, (unsigned)m_default_ai_list.size());
328
329 for(unsigned int i=0; i<m; i++)
330 {
331 m_ai_kart_list.push_back(m_default_ai_list[i]);
332 n--;
333 }
334
335 if(n>0)
336 kart_properties_manager->getRandomKartList(n, &m_player_karts,
337 &m_ai_kart_list );
338
339 if (m_ai_kart_override != "")
340 {
341 for (unsigned int n = 0; n < m_ai_kart_list.size(); n++)
342 {
343 m_ai_kart_list[n] = m_ai_kart_override;
344 }
345 }
346
347 } // computeRandomKartList
348
349 //---------------------------------------------------------------------------------------------
350 /** \brief Starts a new race or GP (or other mode).
351 * It sets up the list of player karts, AI karts, GP tracks if relevant
352 * etc.
353 * \pre The list of AI karts to use must be set up first. This is
354 * usually being done by a call to computeRandomKartList() from
355 * NetworkManager::setupPlayerKartInfo, but could be done differently
356 * (e.g. depending on user command line options to test certain AIs)
357 * \param from_overworld True if the race/GP is started from overworld
358 * (used to return to overworld at end of race/GP).
359 */
startNew(bool from_overworld)360 void RaceManager::startNew(bool from_overworld)
361 {
362 m_num_ghost_karts = 0;
363 if (m_has_ghost_karts)
364 m_num_ghost_karts = ReplayPlay::get()->getNumGhostKart();
365
366 m_started_from_overworld = from_overworld;
367 if (m_started_from_overworld) m_continue_saved_gp = false;
368 m_saved_gp = NULL; // There will be checks for this being NULL done later
369
370 if (m_major_mode==MAJOR_MODE_GRAND_PRIX)
371 {
372 // GP: get tracks, laps and reverse info from grand prix
373 m_tracks = m_grand_prix.getTrackNames();
374 m_num_laps = m_grand_prix.getLaps();
375 m_reverse_track = m_grand_prix.getReverse();
376
377 if (!NetworkConfig::get()->isNetworking())
378 {
379 // We look if Player 1 has a saved version of this GP.
380 m_saved_gp = SavedGrandPrix::getSavedGP(
381 StateManager::get()
382 ->getActivePlayerProfile(0)
383 ->getUniqueID(),
384 m_grand_prix.getId(),
385 m_minor_mode,
386 (unsigned int)m_player_karts.size());
387
388 // Saved GP only in offline mode
389 if (m_continue_saved_gp)
390 {
391 if (m_saved_gp == NULL)
392 {
393 Log::error("Race Manager", "Can not continue Grand Prix '%s'"
394 "because it could not be loaded",
395 m_grand_prix.getId().c_str());
396 m_continue_saved_gp = false; // simple and working
397 }
398 else
399 {
400 setNumKarts(m_saved_gp->getTotalKarts());
401 setupPlayerKartInfo();
402 m_grand_prix.changeReverse((GrandPrixData::GPReverseType)
403 m_saved_gp->getReverseType());
404 m_reverse_track = m_grand_prix.getReverse();
405 } // if m_saved_gp==NULL
406 } // if m_continue_saved_gp
407 } // if !network_world
408 } // if grand prix
409
410 // command line parameters: negative numbers=all karts
411 if(m_num_karts < 0 ) m_num_karts = stk_config->m_max_karts;
412 if((size_t)m_num_karts < m_player_karts.size())
413 m_num_karts = (int)m_player_karts.size();
414
415 // Create the kart status data structure to keep track of scores, times, ...
416 // ==========================================================================
417 m_kart_status.clear();
418 if (m_num_ghost_karts > 0)
419 m_num_karts += m_num_ghost_karts;
420
421 Log::verbose("RaceManager", "Nb of karts=%u, ghost karts:%u ai:%lu players:%lu\n",
422 (unsigned int) m_num_karts, m_num_ghost_karts, m_ai_kart_list.size(), m_player_karts.size());
423
424 assert((unsigned int)m_num_karts == m_num_ghost_karts+m_ai_kart_list.size()+m_player_karts.size());
425
426 // First add the ghost karts (if any)
427 // ----------------------------------------
428 // GP ranks start with -1 for the leader.
429 int init_gp_rank = getMinorMode()==MINOR_MODE_FOLLOW_LEADER ? -1 : 0;
430 if (m_num_ghost_karts > 0)
431 {
432 for(unsigned int i = 0; i < m_num_ghost_karts; i++)
433 {
434 m_kart_status.push_back(KartStatus(ReplayPlay::get()->getGhostKartName(i),
435 i, -1, -1, init_gp_rank, KT_GHOST, HANDICAP_NONE));
436 init_gp_rank ++;
437 }
438 }
439
440 // Then add the AI karts (randomly chosen)
441 // ----------------------------------------
442 const unsigned int ai_kart_count = (unsigned int)m_ai_kart_list.size();
443 for(unsigned int i = 0; i < ai_kart_count; i++)
444 {
445 m_kart_status.push_back(KartStatus(m_ai_kart_list[i], i, -1, -1,
446 init_gp_rank, KT_AI, HANDICAP_NONE));
447 init_gp_rank ++;
448 if(UserConfigParams::m_ftl_debug)
449 {
450 Log::debug("RaceManager", "[ftl] rank %d ai-kart %s", init_gp_rank,
451 m_ai_kart_list[i].c_str());
452 }
453 }
454
455 // Finally add the players, which start behind the AI karts
456 // -----------------------------------------------------
457 for(unsigned int i = 0; i < m_player_karts.size(); i++)
458 {
459 KartType kt= m_player_karts[i].isNetworkPlayer() ? KT_NETWORK_PLAYER
460 : KT_PLAYER;
461 m_kart_status.push_back(KartStatus(m_player_karts[i].getKartName(), i,
462 m_player_karts[i].getLocalPlayerId(),
463 m_player_karts[i].getGlobalPlayerId(),
464 init_gp_rank, kt,
465 m_player_karts[i].getHandicap()));
466 if(UserConfigParams::m_ftl_debug)
467 {
468 Log::debug("RaceManager", "[ftl] rank %d kart %s", init_gp_rank,
469 m_player_karts[i].getKartName().c_str());
470 }
471 init_gp_rank ++;
472 }
473
474 m_track_number = 0;
475 if (m_major_mode == MAJOR_MODE_GRAND_PRIX)
476 {
477 if (m_continue_saved_gp)
478 {
479 int next_track = m_saved_gp->getNextTrack();
480 if (next_track < (int)m_tracks.size())
481 m_track_number = next_track;
482 m_saved_gp->loadKarts(m_kart_status);
483 }
484 else
485 {
486 while (m_saved_gp != NULL)
487 {
488 m_saved_gp->remove();
489 m_saved_gp = SavedGrandPrix::getSavedGP(
490 StateManager::get()
491 ->getActivePlayerProfile(0)
492 ->getUniqueID(),
493 m_grand_prix.getId(),
494 m_minor_mode,
495 (unsigned int)m_player_karts.size());
496 } // while m_saved_gp
497 } // if m_continue_saved_gp
498 } // if grand prix
499
500 startNextRace();
501 } // startNew
502
503 //---------------------------------------------------------------------------------------------
504 /** \brief Starts the next (or first) race.
505 * It sorts the kart status data structure
506 * according to the number of points, and then creates the world().
507 */
startNextRace()508 void RaceManager::startNextRace()
509 {
510 ProcessType type = STKProcess::getType();
511 main_loop->renderGUI(0);
512 // Uncomment to debug audio leaks
513 // sfx_manager->dump();
514
515 if (type == PT_MAIN)
516 {
517 IrrlichtDevice* device = irr_driver->getDevice();
518 GUIEngine::clearLoadingTips();
519 GUIEngine::renderLoading(true/*clearIcons*/, false/*launching*/, false/*update_tips*/);
520 device->getVideoDriver()->endScene();
521 device->getVideoDriver()->beginScene(true, true,
522 video::SColor(255,100,101,140));
523 }
524
525 m_num_finished_karts = 0;
526 m_num_finished_players = 0;
527
528 // if subsequent race, sort kart status structure
529 // ==============================================
530 if (m_track_number > 0)
531 {
532 // In follow the leader mode do not change the first kart,
533 // since it's always the leader.
534 int offset = (m_minor_mode==MINOR_MODE_FOLLOW_LEADER) ? 1 : 0;
535
536 // Keep players at the end if needed
537 int player_last_offset = 0;
538 if (UserConfigParams::m_gp_player_last)
539 {
540 // Doing this is enough to keep player karts at
541 // the end because of the simple reason that they
542 // are at the end when getting added. Keep them out
543 // of the later sorting and they will stay there.
544 player_last_offset = (int)m_player_karts.size();
545 }
546
547 std::sort(m_kart_status.begin()+offset,
548 m_kart_status.end() - player_last_offset);
549 // reverse kart order if flagged in user's config
550 if (UserConfigParams::m_gp_most_points_first)
551 {
552 std::reverse(m_kart_status.begin()+offset,
553 m_kart_status.end() - player_last_offset);
554 }
555 } // not first race
556
557 // set boosted AI status for AI karts
558 int boosted_ai_count = std::min<int>((int)m_ai_kart_list.size(),
559 ((int)(m_kart_status.size())-2)/4 + 1);
560 if (boosted_ai_count > 4) boosted_ai_count = 4;
561 int ai_count = (int)m_ai_kart_list.size();
562
563 for (unsigned int i=0;i<m_kart_status.size();i++)
564 {
565 if (m_kart_status[i].m_kart_type == KT_AI)
566 {
567 if (boosted_ai_count > 0 &&
568 (UserConfigParams::m_gp_most_points_first ||
569 ai_count == boosted_ai_count))
570 {
571 m_kart_status[i].m_boosted_ai = true;
572 boosted_ai_count--;
573 }
574 else
575 {
576 m_kart_status[i].m_boosted_ai = false;
577 }
578 ai_count--;
579 }
580 }
581
582 main_loop->renderGUI(100);
583
584 // the constructor assigns this object to the global
585 // variable world. Admittedly a bit ugly, but simplifies
586 // handling of objects which get created in the constructor
587 // and need world to be defined.
588 if(DemoWorld::isDemoMode())
589 World::setWorld(new DemoWorld());
590 else if(ProfileWorld::isProfileMode())
591 World::setWorld(new ProfileWorld());
592 else if(m_minor_mode==MINOR_MODE_FOLLOW_LEADER)
593 World::setWorld(new FollowTheLeaderRace());
594 else if(m_minor_mode==MINOR_MODE_NORMAL_RACE ||
595 m_minor_mode==MINOR_MODE_TIME_TRIAL)
596 World::setWorld(new StandardRace());
597 else if(m_minor_mode==MINOR_MODE_TUTORIAL)
598 World::setWorld(new TutorialWorld());
599 else if (isBattleMode())
600 {
601 if (m_minor_mode == MINOR_MODE_3_STRIKES)
602 World::setWorld(new ThreeStrikesBattle());
603 else if (m_minor_mode == MINOR_MODE_FREE_FOR_ALL)
604 World::setWorld(new FreeForAll());
605 else if (m_minor_mode == MINOR_MODE_CAPTURE_THE_FLAG)
606 World::setWorld(new CaptureTheFlag());
607 }
608 else if(m_minor_mode==MINOR_MODE_SOCCER)
609 World::setWorld(new SoccerWorld());
610 else if(m_minor_mode==MINOR_MODE_OVERWORLD)
611 World::setWorld(new OverWorld());
612 else if(m_minor_mode==MINOR_MODE_CUTSCENE)
613 World::setWorld(new CutsceneWorld());
614 else if(m_minor_mode==MINOR_MODE_EASTER_EGG)
615 World::setWorld(new EasterEggHunt());
616 else
617 {
618 Log::error("RaceManager", "Could not create given race mode.");
619 assert(0);
620 }
621 main_loop->renderGUI(200);
622
623 // A second constructor phase is necessary in order to be able to
624 // call functions which are overwritten (otherwise polymorphism
625 // will fail and the results will be incorrect). Also in init() functions
626 // can be called that use World::getWorld().
627 World::getWorld()->init();
628 main_loop->renderGUI(8000);
629 // Now initialise all values that need to be reset from race to race
630 // Calling this here reduces code duplication in init and restartRace()
631 // functions.
632 World::getWorld()->reset();
633
634 if (NetworkConfig::get()->isNetworking())
635 {
636 for (unsigned i = 0; i < getNumPlayers(); i++)
637 {
638 // Eliminate all reserved players in the begining
639 const RemoteKartInfo& rki = getKartInfo(i);
640 if (rki.isReserved())
641 {
642 AbstractKart* k = World::getWorld()->getKart(i);
643 World::getWorld()->eliminateKart(i,
644 false/*notify_of_elimination*/);
645 k->setPosition(
646 World::getWorld()->getCurrentNumKarts() + 1);
647 k->finishedRace(World::getWorld()->getTime(),
648 true/*from_server*/);
649 }
650 }
651 }
652
653 if (type == PT_MAIN)
654 irr_driver->onLoadWorld();
655 main_loop->renderGUI(8100);
656
657 // Save the current score and set last time to zero. This is necessary
658 // if someone presses esc after finishing a gp, and selects restart:
659 // The race is rerun, and the points and scores get reset ... but if
660 // a kart hasn't finished the race at this stage, last_score and time
661 // would be undefined.
662 for(int i=0; i<m_num_karts; i++)
663 {
664 m_kart_status[i].m_last_score = m_kart_status[i].m_score;
665 m_kart_status[i].m_last_time = 0;
666 }
667 main_loop->renderGUI(8200);
668 } // startNextRace
669
670 //---------------------------------------------------------------------------------------------
671 /** \brief Start the next race or go back to the start screen
672 * If there are more races to do, starts the next race, otherwise
673 * calls exitRace to finish the race.
674 */
next()675 void RaceManager::next()
676 {
677 if (STKProcess::getType() == PT_MAIN)
678 PropertyAnimator::get()->clear();
679 World::deleteWorld();
680 m_num_finished_karts = 0;
681 m_num_finished_players = 0;
682 m_track_number++;
683 if(m_track_number<(int)m_tracks.size())
684 {
685 if (m_major_mode == MAJOR_MODE_GRAND_PRIX &&
686 !NetworkConfig::get()->isNetworking())
687 {
688 // Saving GP state
689 saveGP();
690 }
691 startNextRace();
692 }
693 else
694 {
695 exitRace();
696 }
697 } // next
698
699 //---------------------------------------------------------------------------------------------
700 /** Saves the current GP to the config.
701 */
saveGP()702 void RaceManager::saveGP()
703 {
704 // If Player 1 has already saved a GP, we adapt it
705 if (m_saved_gp != NULL)
706 {
707 m_saved_gp->setKarts(m_kart_status);
708 m_saved_gp->setNextTrack(m_track_number);
709 }
710 else if(!m_grand_prix.isRandomGP())
711 {
712 m_saved_gp = new SavedGrandPrix(
713 StateManager::get()->getActivePlayerProfile(0)->getUniqueID(),
714 m_grand_prix.getId(),
715 m_minor_mode,
716 m_difficulty,
717 (int)m_player_karts.size(),
718 m_track_number,
719 m_grand_prix.getReverseType(),
720 m_kart_status);
721
722 // If a new GP is saved, delete any other saved data for this
723 // GP at the same difficulty (even if #karts is different, otherwise
724 // the user has to remember the number of AI karts, with no indication
725 // on which ones are saved).
726 for (unsigned int i = 0;
727 i < UserConfigParams::m_saved_grand_prix_list.size();)
728 {
729 // Delete other save files (and random GP, which should never
730 // have been saved in the first place)
731 const SavedGrandPrix &sgp =
732 UserConfigParams::m_saved_grand_prix_list[i];
733 if (sgp.getGPID() == "random" ||
734 (sgp.getGPID() == m_saved_gp->getGPID() &&
735 sgp.getDifficulty() == m_saved_gp->getDifficulty()) )
736 {
737 UserConfigParams::m_saved_grand_prix_list.erase(i);
738 }
739 else i++;
740 }
741 UserConfigParams::m_saved_grand_prix_list.push_back(m_saved_gp);
742 }
743
744 user_config->saveConfig();
745 } // saveGP
746
747 //---------------------------------------------------------------------------------------------
748
749 /** This class is only used in computeGPRanks, but the C++ standard
750 * forbids the usage of local data type in templates, so we have to
751 * declare it outside of the function. This class is used to store
752 * and compare data for determining the GP rank.
753 */
754 namespace computeGPRanksData
755 {
756 class SortData
757 {
758 public:
759 int m_score;
760 int m_position;
761 float m_race_time;
operator <(const SortData & a)762 bool operator<(const SortData &a)
763 {
764 return ( (m_score > a.m_score) ||
765 (m_score == a.m_score && m_race_time < a.m_race_time) );
766 }
767
768 }; // SortData
769 } // namespace
770
771 // ----------------------------------------------------------------------------
772 /** Sort karts and update the m_gp_rank KartStatus member, in preparation
773 * for future calls to RaceManager::getKartGPRank or
774 * RaceManager::getKartWithGPRank
775 */
computeGPRanks()776 void RaceManager::computeGPRanks()
777 {
778 // calculate the rank of each kart
779 const unsigned int NUM_KARTS = getNumberOfKarts();
780 PtrVector<computeGPRanksData::SortData> sort_data;
781
782 // Ignore the first kart if it's a follow-the-leader race.
783 int start=(getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER);
784 if (start)
785 {
786 // fill values for leader
787 computeGPRanksData::SortData *sd = new computeGPRanksData::SortData();
788
789 sd->m_position = -1;
790 sd->m_score = -1;
791 sd->m_race_time = -1;
792 sort_data.push_back(sd);
793 m_kart_status[0].m_gp_rank = -1;
794 if(UserConfigParams::m_ftl_debug)
795 {
796 Log::debug("Race Manager","[ftl] kart '%s' has position %d.",
797 World::getWorld()->getKart(0)->getIdent().c_str(),
798 sd->m_position);
799 }
800 }
801 for (unsigned int kart_id = start; kart_id < NUM_KARTS; ++kart_id)
802 {
803 computeGPRanksData::SortData *sd = new computeGPRanksData::SortData();
804 sd->m_position = kart_id;
805 sd->m_score = getKartScore(kart_id);
806 sd->m_race_time = getOverallTime(kart_id);
807 sort_data.push_back(sd);
808 if(UserConfigParams::m_ftl_debug)
809 {
810 Log::debug("Race Manager",
811 "[ftl] kart '%s' has position %d score %d.",
812 World::getWorld()->getKart(kart_id)->getIdent().c_str(),
813 sd->m_position, sd->m_score);
814 }
815 }
816
817 sort_data.insertionSort(start);
818 for (unsigned int i=start; i < NUM_KARTS; ++i)
819 {
820 if(UserConfigParams::m_ftl_debug)
821 {
822 const AbstractKart *kart =
823 World::getWorld()->getKart(sort_data[i].m_position);
824 Log::debug("Race Manager","[ftl] kart '%s' has now position %d.",
825 kart->getIdent().c_str(),
826 i-start);
827 }
828
829 m_kart_status[sort_data[i].m_position].m_gp_rank = i - start;
830 }
831 } // computeGPRanks
832
833 //---------------------------------------------------------------------------------------------
834 /** \brief Exit a race (and don't start the next one)
835 * \note In GP, displays the GP result screen first
836 * \param delete_world If set deletes the world.
837 */
exitRace(bool delete_world)838 void RaceManager::exitRace(bool delete_world)
839 {
840 // Only display the grand prix result screen if all tracks
841 // were finished, and not when a race is aborted.
842 MessageQueue::discardStatic();
843 ProcessType type = STKProcess::getType();
844
845 if ( m_major_mode==MAJOR_MODE_GRAND_PRIX &&
846 m_track_number==(int)m_tracks.size() )
847 {
848 PlayerManager::getCurrentPlayer()->grandPrixFinished();
849 if (m_major_mode == MAJOR_MODE_GRAND_PRIX &&
850 !NetworkConfig::get()->isNetworking())
851 {
852 if(m_saved_gp != NULL)
853 m_saved_gp->remove();
854 }
855 StateManager::get()->resetAndGoToScreen( MainMenuScreen::getInstance() );
856
857 bool some_human_player_well_ranked = false;
858 bool some_human_player_won = false;
859 const unsigned int kart_status_count = (unsigned int)m_kart_status.size();
860
861 const int loserThreshold = 3;
862
863 std::pair<std::string, float> winners[3];
864 // because we don't care about AIs that lost
865 std::vector<std::pair<std::string, float> > humanLosers;
866 for (unsigned int i=0; i < kart_status_count; ++i)
867 {
868 if(UserConfigParams::logMisc())
869 {
870 Log::info("RaceManager", "%s has GP final rank %d",
871 m_kart_status[i].m_ident.c_str(), m_kart_status[i].m_gp_rank);
872 }
873
874 const int rank = m_kart_status[i].m_gp_rank;
875 if (rank >= 0 && rank < loserThreshold)
876 {
877 winners[rank].first = m_kart_status[i].m_ident;
878 winners[rank].second = m_kart_status[i].m_color;
879 if (m_kart_status[i].m_kart_type == KT_PLAYER ||
880 m_kart_status[i].m_kart_type == KT_NETWORK_PLAYER)
881 {
882 some_human_player_well_ranked = true;
883 if (rank == 0)
884 some_human_player_won = true;
885 }
886 }
887 else if (rank >= loserThreshold)
888 {
889 if (m_kart_status[i].m_kart_type == KT_PLAYER ||
890 m_kart_status[i].m_kart_type == KT_NETWORK_PLAYER)
891 {
892 humanLosers.emplace_back(m_kart_status[i].m_ident, m_kart_status[i].m_color);
893 }
894 }
895 }
896
897 if (delete_world)
898 {
899 if (type == PT_MAIN)
900 PropertyAnimator::get()->clear();
901 World::deleteWorld();
902 }
903 delete_world = false;
904
905 StateManager::get()->enterGameState();
906 setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
907 setNumKarts(0);
908 setNumPlayers(0);
909
910 if (some_human_player_well_ranked)
911 {
912 startSingleRace("gpwin", 999,
913 raceWasStartedFromOverworld());
914 GrandPrixWin* scene = GrandPrixWin::getInstance();
915 scene->push();
916 scene->setKarts(winners);
917 scene->setPlayerWon(some_human_player_won);
918 }
919 else
920 {
921 startSingleRace("gplose", 999,
922 raceWasStartedFromOverworld());
923 GrandPrixLose* scene = GrandPrixLose::getInstance();
924 scene->push();
925
926 if (humanLosers.size() >= 1)
927 {
928 scene->setKarts(humanLosers);
929 }
930 else
931 {
932 Log::error("RaceManager", "There are no winners and no losers."
933 "This should have never happened\n");
934 std::vector<std::pair<std::string, float> > karts;
935 karts.emplace_back(UserConfigParams::m_default_kart, 0.0f);
936 scene->setKarts(karts);
937 }
938 }
939 }
940
941 if (delete_world)
942 {
943 if (type == PT_MAIN)
944 PropertyAnimator::get()->clear();
945 World::deleteWorld();
946 }
947
948 m_saved_gp = NULL;
949 m_track_number = 0;
950 } // exitRace
951
952 //---------------------------------------------------------------------------------------------
953 /** A kart has finished the race at the specified time (which can be
954 * different from World::getWorld()->getClock() in case of setting
955 * extrapolated arrival times). This function is only called from
956 * kart::finishedRace()
957 * \param kart The kart that finished the race.
958 * \param time Time at which the kart finished the race.
959 */
kartFinishedRace(const AbstractKart * kart,float time)960 void RaceManager::kartFinishedRace(const AbstractKart *kart, float time)
961 {
962 unsigned int id = kart->getWorldKartId();
963 int pos = kart->getPosition();
964
965 assert(pos-1 >= 0);
966 assert(pos-1 < (int)m_kart_status.size());
967
968 m_kart_status[id].m_last_score = m_kart_status[id].m_score;
969
970 // In follow the leader mode, the winner is actually the kart with
971 // position 2, so adjust the points (#points for leader do not matter)
972 WorldWithRank *wwr = dynamic_cast<WorldWithRank*>(World::getWorld());
973 if (wwr)
974 m_kart_status[id].m_score += wwr->getScoreForPosition(pos);
975 else
976 {
977 Log::error("RaceManager",
978 "World with scores that is not a WorldWithRank??");
979 }
980
981 m_kart_status[id].m_overall_time += time;
982 m_kart_status[id].m_last_time = time;
983 m_num_finished_karts ++;
984 if(kart->getController()->isPlayerController())
985 m_num_finished_players++;
986 } // kartFinishedRace
987
988 //---------------------------------------------------------------------------------------------
989 /** \brief Rerun the same race again
990 * This is called after a race is finished, and it will adjust
991 * the number of points and the overall time before restarting the race.
992 */
rerunRace()993 void RaceManager::rerunRace()
994 {
995 // Subtract last score from all karts:
996 for(int i=0; i<m_num_karts; i++)
997 {
998 m_kart_status[i].m_score = m_kart_status[i].m_last_score;
999 m_kart_status[i].m_overall_time -= m_kart_status[i].m_last_time;
1000 }
1001 World::getWorld()->reset(true /* restart */);
1002 } // rerunRace
1003
1004 //---------------------------------------------------------------------------------------------
1005 /** \brief Higher-level method to start a GP without having to care about
1006 * the exact startup sequence
1007 */
startGP(const GrandPrixData & gp,bool from_overworld,bool continue_saved_gp)1008 void RaceManager::startGP(const GrandPrixData &gp, bool from_overworld,
1009 bool continue_saved_gp)
1010 {
1011 StateManager::get()->enterGameState();
1012 setGrandPrix(gp);
1013 setupPlayerKartInfo();
1014 m_continue_saved_gp = continue_saved_gp;
1015
1016 setMajorMode(RaceManager::MAJOR_MODE_GRAND_PRIX);
1017 startNew(from_overworld);
1018 }
1019
1020 //---------------------------------------------------------------------------------------------
1021 /** \brief Higher-level method to start a GP without having to care about
1022 * the exact startup sequence.
1023 * \param trackIdent Internal name of the track to race on
1024 * \param num_laps Number of laps to race, or -1 if number of laps is
1025 * not relevant in current mode
1026 */
startSingleRace(const std::string & track_ident,const int num_laps,bool from_overworld)1027 void RaceManager::startSingleRace(const std::string &track_ident,
1028 const int num_laps,
1029 bool from_overworld)
1030 {
1031 assert(!m_watching_replay);
1032 StateManager::get()->enterGameState();
1033
1034 // In networking, make sure that the tracks screen is shown. This will
1035 // allow for a 'randomly pick track' animation to be shown while
1036 // world is loaded.
1037 // Disable until render gui during loading is bug free
1038 /*if (NetworkConfig::get()->isNetworking() &&
1039 NetworkConfig::get()->isClient() )
1040 {
1041 // TODO: The enterGameState() call above deleted all GUIs, which
1042 // means even if the tracks screen is shown, it need to be recreated.
1043 // And we have to make sure that it is recreated as network version.
1044 TracksScreen *ts = TracksScreen::getInstance();
1045 if (GUIEngine::getCurrentScreen() != ts)
1046 {
1047 ts->setNetworkTracks();
1048 ts->push();
1049 }
1050 }*/
1051
1052
1053 setTrack(track_ident);
1054
1055 if (num_laps != -1) setNumLaps( num_laps );
1056
1057 setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
1058
1059 setCoinTarget( 0 ); // Might still be set from a previous challenge
1060
1061 // if not in a network world, setup player karts
1062 if (!NetworkConfig::get()->isNetworking())
1063 setupPlayerKartInfo(); // do this setup player kart
1064
1065 startNew(from_overworld);
1066 } // startSingleRace
1067
1068 //---------------------------------------------------------------------------------------------
1069 /** Fills up the remaining kart slots with AI karts.
1070 */
setupPlayerKartInfo()1071 void RaceManager::setupPlayerKartInfo()
1072 {
1073 computeRandomKartList();
1074 } // setupPlayerKartInfo
1075
1076 //---------------------------------------------------------------------------------------------
1077 /** \brief Function to start the race with only ghost kart(s) and watch.
1078 * \param trackIdent Internal name of the track to race on
1079 * \param num_laps Number of laps to race, or -1 if number of laps is
1080 * not relevant in current mode
1081 */
startWatchingReplay(const std::string & track_ident,const int num_laps)1082 void RaceManager::startWatchingReplay(const std::string &track_ident,
1083 const int num_laps)
1084 {
1085 assert(m_watching_replay && m_has_ghost_karts && !m_is_recording_race);
1086 StateManager::get()->enterGameState();
1087 setTrack(track_ident);
1088 setNumLaps(num_laps);
1089 setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
1090 setCoinTarget(0);
1091 m_num_karts = ReplayPlay::get()->getNumGhostKart();
1092 m_kart_status.clear();
1093
1094 Log::verbose("RaceManager", "%u ghost kart(s) for watching replay only\n",
1095 (unsigned int)m_num_karts);
1096
1097 int init_gp_rank = 0;
1098
1099 for(int i = 0; i < m_num_karts; i++)
1100 {
1101 m_kart_status.push_back(KartStatus(ReplayPlay::get()->getGhostKartName(i),
1102 i, -1, -1, init_gp_rank, KT_GHOST, HANDICAP_NONE));
1103 init_gp_rank ++;
1104 }
1105
1106 m_track_number = 0;
1107 startNextRace();
1108 } // startWatchingReplay
1109
1110 //---------------------------------------------------------------------------------------------
configGrandPrixResultFromNetwork(NetworkString & ns)1111 void RaceManager::configGrandPrixResultFromNetwork(NetworkString& ns)
1112 {
1113 setMajorMode(MAJOR_MODE_GRAND_PRIX);
1114 class NetworkGrandPrixData : public GrandPrixData
1115 {
1116 public:
1117 NetworkGrandPrixData() : GrandPrixData()
1118 { setGroup(GrandPrixData::GP_STANDARD); }
1119 virtual std::vector<std::string>
1120 getTrackNames(const bool includeLocked=false) const
1121 { return m_tracks; }
1122 virtual unsigned int
1123 getNumberOfTracks(const bool includeLocked=false) const
1124 { return (unsigned int)m_tracks.size(); }
1125 void addNetworkTrack(const std::string& t) { m_tracks.push_back(t); }
1126 };
1127
1128 NetworkGrandPrixData ngpd;
1129 unsigned int track_size = ns.getUInt8();
1130 unsigned int all_track_size = ns.getUInt8();
1131 assert(all_track_size > 0);
1132 m_track_number = all_track_size -1;
1133 for (unsigned i = 0; i < all_track_size; i++)
1134 {
1135 std::string t;
1136 ns.decodeString(&t);
1137 ngpd.addNetworkTrack(t);
1138 }
1139 while (all_track_size < track_size)
1140 {
1141 ngpd.addNetworkTrack("");
1142 all_track_size++;
1143 }
1144
1145 m_tracks = ngpd.getTrackNames();
1146 // For result screen we only need current lap and reserve
1147 m_num_laps.resize(track_size, m_num_laps[0]);
1148 m_reverse_track.resize(track_size, m_reverse_track[0]);
1149
1150 m_grand_prix = ngpd;
1151 unsigned int player_size = ns.getUInt8();
1152 assert(player_size == m_kart_status.size());
1153 for (unsigned i = 0; i < player_size; i++)
1154 {
1155 int last_score = ns.getUInt32();
1156 int cur_score = ns.getUInt32();
1157 float overall_time = ns.getFloat();
1158 m_kart_status[i].m_last_score = last_score;
1159 m_kart_status[i].m_score = cur_score;
1160 m_kart_status[i].m_overall_time = overall_time;
1161 m_kart_status[i].m_gp_rank = i;
1162 }
1163 } // configGrandPrixResultFromNetwork
1164
1165 //---------------------------------------------------------------------------------------------
clearNetworkGrandPrixResult()1166 void RaceManager::clearNetworkGrandPrixResult()
1167 {
1168 if (m_major_mode != MAJOR_MODE_GRAND_PRIX)
1169 return;
1170 setMajorMode(MAJOR_MODE_SINGLE);
1171 m_grand_prix = GrandPrixData();
1172 m_track_number = 0;
1173 m_tracks.clear();
1174 m_num_laps.clear();
1175 m_reverse_track.clear();
1176
1177 } // clearNetworkGrandPrixResult
1178
1179 //---------------------------------------------------------------------------------------------
1180 /** Returns a (translated) name of a minor race mode.
1181 * \param mode Minor race mode.
1182 */
getNameOf(const MinorRaceModeType mode)1183 const core::stringw RaceManager::getNameOf(const MinorRaceModeType mode)
1184 {
1185 switch (mode)
1186 {
1187 //I18N: Game mode
1188 case MINOR_MODE_NORMAL_RACE: return _("Normal Race");
1189 //I18N: Game mode
1190 case MINOR_MODE_TIME_TRIAL: return _("Time Trial");
1191 //I18N: Game mode
1192 case MINOR_MODE_FOLLOW_LEADER: return _("Follow the Leader");
1193 //I18N: Game mode
1194 case MINOR_MODE_3_STRIKES: return _("3 Strikes Battle");
1195 //I18N: Game mode
1196 case MINOR_MODE_FREE_FOR_ALL: return _("Free-For-All");
1197 //I18N: Game mode
1198 case MINOR_MODE_CAPTURE_THE_FLAG: return _("Capture The Flag");
1199 //I18N: Game mode
1200 case MINOR_MODE_EASTER_EGG: return _("Egg Hunt");
1201 //I18N: Game mode
1202 case MINOR_MODE_SOCCER: return _("Soccer");
1203 default: assert(false); return L"";
1204 }
1205 } // getNameOf
1206
1207 //---------------------------------------------------------------------------------------------
1208 /** Returns the specified difficulty as a string. */
getDifficultyName(Difficulty diff) const1209 core::stringw RaceManager::getDifficultyName(Difficulty diff) const
1210 {
1211 switch (diff)
1212 {
1213 case RaceManager::DIFFICULTY_EASY: return _("Novice"); break;
1214 case RaceManager::DIFFICULTY_MEDIUM: return _("Intermediate"); break;
1215 case RaceManager::DIFFICULTY_HARD: return _("Expert"); break;
1216 case RaceManager::DIFFICULTY_BEST: return _("SuperTux"); break;
1217 default: assert(false);
1218 }
1219 return "";
1220 } // getDifficultyName
1221