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 "modes/world.hpp"
20
21 #include "audio/music_manager.hpp"
22 #include "audio/sfx_base.hpp"
23 #include "audio/sfx_manager.hpp"
24 #include "config/player_manager.hpp"
25 #include "challenges/unlock_manager.hpp"
26 #include "config/user_config.hpp"
27 #include "graphics/camera.hpp"
28 #include "graphics/central_settings.hpp"
29 #include "graphics/irr_driver.hpp"
30 #include "graphics/material.hpp"
31 #include "graphics/material_manager.hpp"
32 #include "graphics/render_info.hpp"
33 #include "guiengine/modaldialog.hpp"
34 #include "guiengine/screen_keyboard.hpp"
35 #include "io/file_manager.hpp"
36 #include "input/device_manager.hpp"
37 #include "input/keyboard_device.hpp"
38 #include "items/projectile_manager.hpp"
39 #include "karts/controller/battle_ai.hpp"
40 #include "karts/ghost_kart.hpp"
41 #include "karts/controller/end_controller.hpp"
42 #include "karts/controller/local_player_controller.hpp"
43 #include "karts/controller/skidding_ai.hpp"
44 #include "karts/controller/soccer_ai.hpp"
45 #include "karts/controller/spare_tire_ai.hpp"
46 #include "karts/controller/test_ai.hpp"
47 #include "karts/controller/network_ai_controller.hpp"
48 #include "karts/controller/network_player_controller.hpp"
49 #include "karts/kart.hpp"
50 #include "karts/kart_model.hpp"
51 #include "karts/kart_properties_manager.hpp"
52 #include "karts/kart_rewinder.hpp"
53 #include "main_loop.hpp"
54 #include "modes/overworld.hpp"
55 #include "network/child_loop.hpp"
56 #include "network/protocols/client_lobby.hpp"
57 #include "network/network_config.hpp"
58 #include "network/rewind_manager.hpp"
59 #include "network/stk_host.hpp"
60 #include "physics/btKart.hpp"
61 #include "physics/physics.hpp"
62 #include "physics/triangle_mesh.hpp"
63 #include "race/highscore_manager.hpp"
64 #include "race/history.hpp"
65 #include "race/race_manager.hpp"
66 #include "replay/replay_play.hpp"
67 #include "replay/replay_recorder.hpp"
68 #include "scriptengine/script_engine.hpp"
69 #include "states_screens/dialogs/race_paused_dialog.hpp"
70 #include "states_screens/race_gui_base.hpp"
71 #include "states_screens/main_menu_screen.hpp"
72 #include "states_screens/race_gui.hpp"
73 #include "states_screens/race_result_gui.hpp"
74 #include "states_screens/state_manager.hpp"
75 #include "tracks/check_manager.hpp"
76 #include "tracks/track.hpp"
77 #include "tracks/track_manager.hpp"
78 #include "tracks/track_object.hpp"
79 #include "tracks/track_object_manager.hpp"
80 #include "utils/constants.hpp"
81 #include "utils/profiler.hpp"
82 #include "utils/translation.hpp"
83 #include "utils/string_utils.hpp"
84
85 #include <algorithm>
86 #include <assert.h>
87 #include <ctime>
88 #include <sstream>
89 #include <stdexcept>
90
91
92 World* World::m_world[PT_COUNT];
93
94 /** The main world class is used to handle the track and the karts.
95 * The end of the race is detected in two phases: first the (abstract)
96 * function isRaceOver, which must be implemented by all game modes,
97 * must return true. In which case enterRaceOverState is called. At
98 * this time a winning (or losing) animation can be played. The WorldStatus
99 * class will in its enterRaceOverState switch to DELAY_FINISH_PHASE,
100 * but the remaining AI kart will keep on racing during that time.
101 * After a time period specified in stk_config.xml WorldStatus will
102 * switch to FINISH_PHASE and call terminateRace. Now the finishing status
103 * of all karts is set (i.e. in a normal race the arrival time for karts
104 * will be estimated), highscore is updated, and the race result gui
105 * is being displayed.
106 * Rescuing is handled via the three functions:
107 * getNumberOfRescuePositions() - which returns the number of rescue
108 * positions defined.
109 * getRescuePositionIndex(AbstractKart *kart) - which determines the
110 * index of the rescue position to be used for the given kart.
111 * getRescueTransform(unsigned int index) - which returns the transform
112 * (i.e. position and rotation) for the specified rescue
113 * position.
114 * This allows the world class to do some tests to make sure all rescue
115 * positions are valid (when started with --track-debug). It tries to
116 * place all karts on all rescue positions. If there are any problems
117 * (e.g. a rescue position not over terrain (perhaps because it is too
118 * low); or the rescue position is on a texture which will immediately
119 * trigger another rescue), a warning message will be printed.
120 */
121
122 //-----------------------------------------------------------------------------
123 /** Constructor. Note that in the constructor it is not possible to call any
124 * functions that use World::getWorld(), since this is only defined
125 * after the constructor. Those functions must be called in the init()
126 * function, which is called immediately after the constructor.
127 */
World()128 World::World() : WorldStatus()
129 {
130 if (m_process_type == PT_MAIN)
131 GUIEngine::getDevice()->setResizable(true);
132 RewindManager::setEnable(NetworkConfig::get()->isNetworking());
133 #ifdef DEBUG
134 m_magic_number = 0xB01D6543;
135 #endif
136
137 m_race_gui = NULL;
138 m_saved_race_gui = NULL;
139 m_use_highscores = true;
140 m_schedule_pause = false;
141 m_schedule_unpause = false;
142 m_schedule_exit_race = false;
143 m_schedule_tutorial = false;
144 m_is_network_world = false;
145
146 m_stop_music_when_dialog_open = true;
147
148 WorldStatus::setClockMode(CLOCK_CHRONO);
149
150 } // World
151
152 // ----------------------------------------------------------------------------
153 /** This function is called after instanciating. The code here can't be moved
154 * to the contructor as child classes must be instanciated, otherwise
155 * polymorphism will fail and the results will be incorrect . Also in init()
156 * functions can be called that use World::getWorld().
157 */
init()158 void World::init()
159 {
160 m_ended_early = false;
161 m_faster_music_active = false;
162 m_fastest_kart = 0;
163 m_eliminated_karts = 0;
164 m_eliminated_players = 0;
165 m_num_players = 0;
166 unsigned int gk = 0;
167 m_red_ai = m_blue_ai = 0;
168 if (RaceManager::get()->hasGhostKarts())
169 gk = ReplayPlay::get()->getNumGhostKart();
170
171 // Create the race gui before anything else is attached to the scene node
172 // (which happens when the track is loaded). This allows the race gui to
173 // do any rendering on texture. Note that this function can NOT be called
174 // in the World constuctor, since it might be overwritten by a the game
175 // mode class, which would not have been constructed at the time that this
176 // constructor is called, so the wrong race gui would be created.
177 createRaceGUI();
178 main_loop->renderGUI(1000);
179 RewindManager::create();
180 main_loop->renderGUI(1100);
181 // Grab the track file
182 Track *track = track_manager->getTrack(RaceManager::get()->getTrackName());
183 if (m_process_type == PT_MAIN)
184 {
185 Scripting::ScriptEngine::getInstance<Scripting::ScriptEngine>();
186 if(!track)
187 {
188 std::ostringstream msg;
189 msg << "Track '" << RaceManager::get()->getTrackName()
190 << "' not found.\n";
191 throw std::runtime_error(msg.str());
192 }
193
194 std::string script_path = track->getTrackFile("scripting.as");
195 Scripting::ScriptEngine::getInstance()->loadScript(script_path, true);
196 }
197 main_loop->renderGUI(1200);
198 // Create the physics
199 Physics::create();
200 main_loop->renderGUI(1300);
201 unsigned int num_karts = RaceManager::get()->getNumberOfKarts();
202 //assert(num_karts > 0);
203
204 // Load the track models - this must be done before the karts so that the
205 // karts can be positioned properly on (and not in) the tracks.
206 // This also defines the static Track::getCurrentTrack function.
207 if (m_process_type == PT_MAIN)
208 track->loadTrackModel(RaceManager::get()->getReverseTrack());
209 else
210 {
211 Track* child_track = Track::getCurrentTrack();
212 ChildLoop* child_loop = STKHost::getByType(PT_MAIN)->getChildLoop();
213 while (!child_loop->isAborted() && child_track == NULL)
214 {
215 StkTime::sleep(1);
216 child_track = Track::getCurrentTrack();
217 }
218 if (!child_loop->isAborted())
219 child_track->initChildTrack();
220 }
221
222 // Shuffles the start transforms with playing 3-strikes or free for all battles.
223 if ((RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ||
224 RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FREE_FOR_ALL) &&
225 !NetworkConfig::get()->isNetworking())
226 {
227 track->shuffleStartTransforms();
228 }
229
230 main_loop->renderGUI(6998);
231 if (gk > 0)
232 {
233 ReplayPlay::get()->load();
234 for (unsigned int k = 0; k < gk; k++)
235 m_karts.push_back(ReplayPlay::get()->getGhostKart(k));
236 }
237 main_loop->renderGUI(6999);
238
239 // Assign team of AIs for team mode before createKart
240 if (hasTeam())
241 setAITeam();
242
243 for(unsigned int i=0; i<num_karts; i++)
244 {
245 main_loop->renderGUI(7000, i, num_karts);
246 if (RaceManager::get()->getKartType(i) == RaceManager::KT_GHOST) continue;
247 std::string kart_ident = history->replayHistory()
248 ? history->getKartIdent(i)
249 : RaceManager::get()->getKartIdent(i);
250 int local_player_id = RaceManager::get()->getKartLocalPlayerId(i);
251 int global_player_id = RaceManager::get()->getKartGlobalPlayerId(i);
252 std::shared_ptr<AbstractKart> new_kart;
253 if (hasTeam())
254 {
255 new_kart = createKartWithTeam(kart_ident, i, local_player_id,
256 global_player_id, RaceManager::get()->getKartType(i),
257 RaceManager::get()->getPlayerHandicap(i));
258 }
259 else
260 {
261 new_kart = createKart(kart_ident, i, local_player_id,
262 global_player_id, RaceManager::get()->getKartType(i),
263 RaceManager::get()->getPlayerHandicap(i));
264 }
265 new_kart->setBoostAI(RaceManager::get()->hasBoostedAI(i));
266 m_karts.push_back(new_kart);
267 } // for i
268
269 main_loop->renderGUI(7050);
270 // Load other custom models if needed
271 loadCustomModels();
272 main_loop->renderGUI(7100);
273 // Must be called after all karts are created
274 if (m_race_gui)
275 m_race_gui->init();
276
277 if (m_process_type == PT_MAIN)
278 powerup_manager->computeWeightsForRace(RaceManager::get()->getNumberOfKarts());
279 main_loop->renderGUI(7200);
280 if (m_process_type == PT_MAIN && UserConfigParams::m_particles_effects > 1)
281 {
282 Weather::getInstance<Weather>(); // create Weather instance
283 }
284
285 if (Camera::getNumCameras() == 0)
286 {
287 auto cl = LobbyProtocol::get<ClientLobby>();
288 if ( (NetworkConfig::get()->isServer() &&
289 !GUIEngine::isNoGraphics() ) ||
290 RaceManager::get()->isWatchingReplay() ||
291 (cl && cl->isSpectator()))
292 {
293 // In case that the server is running with gui, watching replay or
294 // spectating the game, create a camera and attach it to the first
295 // kart.
296 Camera::createCamera(World::getWorld()->getKart(0), 0);
297
298 } // if server with graphics of is watching replay
299 } // if getNumCameras()==0
300
301 const unsigned int kart_amount = (unsigned int)m_karts.size();
302 for (unsigned int i = 0; i < kart_amount; i++)
303 initTeamArrows(m_karts[i].get());
304
305 main_loop->renderGUI(7300);
306 } // init
307
308 //-----------------------------------------------------------------------------
initTeamArrows(AbstractKart * k)309 void World::initTeamArrows(AbstractKart* k)
310 {
311 if (!hasTeam() || GUIEngine::isNoGraphics())
312 return;
313 #ifndef SERVER_ONLY
314 //Loading the indicator textures
315 std::string red_path =
316 file_manager->getAsset(FileManager::GUI_ICON, "red_arrow.png");
317 std::string blue_path =
318 file_manager->getAsset(FileManager::GUI_ICON, "blue_arrow.png");
319
320 // Assigning indicators
321 scene::ISceneNode *arrow_node = NULL;
322
323 KartModel* km = k->getKartModel();
324 // Color of karts can be changed using shaders if the model supports
325 if (km->supportColorization() && CVS->isGLSL())
326 return;
327
328 float arrow_pos_height = km->getHeight() + 0.5f;
329 KartTeam team = getKartTeam(k->getWorldKartId());
330
331 arrow_node = irr_driver->addBillboard(
332 core::dimension2d<irr::f32>(0.3f,0.3f),
333 team == KART_TEAM_BLUE ? blue_path : red_path,
334 k->getNode());
335
336 arrow_node->setPosition(core::vector3df(0, arrow_pos_height, 0));
337 #endif
338 } // initTeamArrows
339
340 //-----------------------------------------------------------------------------
341 /** This function is called before a race is started (i.e. either after
342 * calling init() when starting a race for the first time, or after
343 * restarting a race, in which case no init() is called.
344 */
reset(bool restart)345 void World::reset(bool restart)
346 {
347 RewindManager::get()->reset();
348
349 // If m_saved_race_gui is set, it means that the restart was done
350 // when the race result gui was being shown. In this case restore the
351 // race gui (note that the race result gui is cached and so never really
352 // destroyed).
353 bool reset_streak = restart && !m_saved_race_gui;
354
355 if(m_saved_race_gui)
356 {
357 m_race_gui = m_saved_race_gui;
358 m_saved_race_gui = NULL;
359 }
360
361 m_ended_early = false;
362 m_schedule_pause = false;
363 m_schedule_unpause = false;
364
365 WorldStatus::reset(restart);
366 m_faster_music_active = false;
367 m_eliminated_karts = 0;
368 m_eliminated_players = 0;
369 m_is_network_world = false;
370
371 for ( KartList::iterator i = m_karts.begin(); i != m_karts.end() ; ++i )
372 {
373 (*i)->reset();
374 if (m_process_type == PT_MAIN && (*i)->getController()->canGetAchievements())
375 {
376 updateAchievementModeCounters(true /*start*/);
377
378 PlayerManager::resetKartHits(getNumKarts());
379 if (RaceManager::get()->isLinearRaceMode())
380 {
381 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), AchievementsStatus::TR_STARTED);
382 AchievementsStatus::AchievementData diff;
383 diff = (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_EASY) ? AchievementsStatus::EASY_STARTED :
384 (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM) ? AchievementsStatus::MEDIUM_STARTED :
385 (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD) ? AchievementsStatus::HARD_STARTED :
386 AchievementsStatus::BEST_STARTED;
387 PlayerManager::increaseAchievement(diff,1);
388 }
389 else if (RaceManager::get()->isEggHuntMode())
390 {
391 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), AchievementsStatus::TR_EGG_HUNT_STARTED);
392 }
393 if (reset_streak)
394 PlayerManager::onRaceEnd(true /* previous race aborted */);
395 }
396 }
397
398 if (!GUIEngine::isNoGraphics())
399 Camera::resetAllCameras();
400
401 if(RaceManager::get()->hasGhostKarts())
402 ReplayPlay::get()->reset();
403
404 // Remove all (if any) previous game flyables before reset karts, so no
405 // explosion animation will be created
406 ProjectileManager::get()->cleanup();
407 resetAllKarts();
408 // Note: track reset must be called after all karts exist, since check
409 // objects need to allocate data structures depending on the number
410 // of karts.
411 Track::getCurrentTrack()->reset();
412
413 // Reset the race gui.
414 if (m_race_gui)
415 m_race_gui->reset();
416
417 // Start music from beginning
418 music_manager->stopMusic();
419
420 // Enable SFX again
421 SFXManager::get()->resumeAll();
422
423 RewindManager::get()->reset();
424 RaceManager::get()->reset();
425 // Make sure to overwrite the data from the previous race.
426 if(!history->replayHistory()) history->initRecording();
427 if(RaceManager::get()->isRecordingRace())
428 {
429 Log::info("World", "Start Recording race.");
430 ReplayRecorder::get()->init();
431 }
432
433 // Reset all data structures that depend on number of karts.
434 if (m_process_type == PT_MAIN)
435 irr_driver->reset();
436 m_unfair_team = false;
437 } // reset
438
439 //-----------------------------------------------------------------------------
440
createRaceGUI()441 void World::createRaceGUI()
442 {
443 if (!GUIEngine::isNoGraphics())
444 m_race_gui = new RaceGUI();
445 }
446
447 //-----------------------------------------------------------------------------
448 /** Creates a kart, having a certain position, starting location, and local
449 * and global player id (if applicable).
450 * \param kart_ident Identifier of the kart to create.
451 * \param index Index of the kart.
452 * \param local_player_id If the kart is a player kart this is the index of
453 * this player on the local machine.
454 * \param global_player_id If the kart is a player kart this is the index of
455 * this player globally (i.e. including network players).
456 */
createKart(const std::string & kart_ident,int index,int local_player_id,int global_player_id,RaceManager::KartType kart_type,HandicapLevel handicap)457 std::shared_ptr<AbstractKart> World::createKart
458 (const std::string &kart_ident, int index, int local_player_id,
459 int global_player_id, RaceManager::KartType kart_type,
460 HandicapLevel handicap)
461 {
462 unsigned int gk = 0;
463 if (RaceManager::get()->hasGhostKarts())
464 gk = ReplayPlay::get()->getNumGhostKart();
465
466 std::shared_ptr<RenderInfo> ri = std::make_shared<RenderInfo>();
467 core::stringw online_name;
468 if (global_player_id > -1)
469 {
470 ri->setHue(RaceManager::get()->getKartInfo(global_player_id)
471 .getDefaultKartColor());
472 online_name = RaceManager::get()->getKartInfo(global_player_id)
473 .getPlayerName();
474 }
475
476 int position = index+1;
477 btTransform init_pos = getStartTransform(index - gk);
478 std::shared_ptr<AbstractKart> new_kart;
479 if (RewindManager::get()->isEnabled())
480 {
481 auto kr = std::make_shared<KartRewinder>(kart_ident, index, position,
482 init_pos, handicap, ri);
483 kr->rewinderAdd();
484 new_kart = kr;
485 }
486 else
487 {
488 new_kart = std::make_shared<Kart>(kart_ident, index, position,
489 init_pos, handicap, ri);
490 }
491
492 new_kart->init(RaceManager::get()->getKartType(index));
493 Controller *controller = NULL;
494 switch(kart_type)
495 {
496 case RaceManager::KT_PLAYER:
497 {
498 int local_player_count = 99999;
499 if (NetworkConfig::get()->isNetworking() &&
500 NetworkConfig::get()->isClient())
501 {
502 local_player_count =
503 (int)NetworkConfig::get()->getNetworkPlayers().size();
504 }
505 // local_player_id >= local_player_count for fixed AI defined in create
506 // server screen
507 if (NetworkConfig::get()->isNetworkAIInstance() ||
508 local_player_id >= local_player_count)
509 {
510 AIBaseController* ai = NULL;
511 if (RaceManager::get()->isBattleMode())
512 ai = new BattleAI(new_kart.get());
513 else
514 ai = new SkiddingAI(new_kart.get());
515 controller = new NetworkAIController(new_kart.get(),
516 local_player_id, ai);
517 }
518 else
519 {
520 controller = new LocalPlayerController(new_kart.get(),
521 local_player_id, handicap);
522 const PlayerProfile* p = StateManager::get()
523 ->getActivePlayer(local_player_id)->getConstProfile();
524 if (p && p->getDefaultKartColor() > 0.0f)
525 {
526 ri->setHue(p->getDefaultKartColor());
527 }
528 }
529 m_num_players ++;
530 break;
531 }
532 case RaceManager::KT_NETWORK_PLAYER:
533 {
534 controller = new NetworkPlayerController(new_kart.get());
535 m_num_players++;
536 break;
537 }
538 case RaceManager::KT_AI:
539 {
540 controller = loadAIController(new_kart.get());
541 break;
542 }
543 case RaceManager::KT_GHOST:
544 case RaceManager::KT_LEADER:
545 case RaceManager::KT_SPARE_TIRE:
546 break;
547 }
548
549 if (!controller->isLocalPlayerController() && !online_name.empty())
550 new_kart->setOnScreenText(online_name.c_str());
551 new_kart->setController(controller);
552 RaceManager::get()->setKartColor(index, ri->getHue());
553 return new_kart;
554 } // createKart
555
556 //-----------------------------------------------------------------------------
557 /** Returns the start coordinates for a kart with a given index.
558 * \param index Index of kart ranging from 0 to kart_num-1. */
getStartTransform(int index)559 const btTransform &World::getStartTransform(int index)
560 {
561 return Track::getCurrentTrack()->getStartTransform(index);
562 } // getStartTransform
563
564 //-----------------------------------------------------------------------------
565 /** Creates an AI controller for the kart.
566 * \param kart The kart to be controlled by an AI.
567 */
loadAIController(AbstractKart * kart)568 Controller* World::loadAIController(AbstractKart* kart)
569 {
570 Controller *controller;
571 int turn=0;
572
573 if(RaceManager::get()->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES
574 || RaceManager::get()->getMinorMode()==RaceManager::MINOR_MODE_FREE_FOR_ALL)
575 turn=1;
576 else if(RaceManager::get()->getMinorMode()==RaceManager::MINOR_MODE_SOCCER)
577 turn=2;
578 // If different AIs should be used, adjust turn (or switch randomly
579 // or dependent on difficulty)
580 switch(turn)
581 {
582 case 0:
583 // If requested, start the test ai
584 if( (AIBaseController::getTestAI()!=0 ) &&
585 ( (kart->getWorldKartId()+1) % AIBaseController::getTestAI() )==0)
586 controller = new TestAI(kart);
587 else
588 controller = new SkiddingAI(kart);
589 break;
590 case 1:
591 controller = new BattleAI(kart);
592 break;
593 case 2:
594 controller = new SoccerAI(kart);
595 break;
596 default:
597 Log::warn("[World]", "Unknown AI, using default.");
598 controller = new SkiddingAI(kart);
599 break;
600 }
601
602 return controller;
603 } // loadAIController
604
605 //-----------------------------------------------------------------------------
~World()606 World::~World()
607 {
608 if (m_process_type == PT_MAIN)
609 {
610 GUIEngine::getDevice()->setResizable(false);
611 material_manager->unloadAllTextures();
612 }
613
614 RewindManager::destroy();
615
616 if (m_process_type == PT_MAIN)
617 irr_driver->onUnloadWorld();
618
619 ProjectileManager::get()->cleanup();
620
621 // In case that a race is aborted (e.g. track not found) track is 0.
622 if (m_process_type == PT_MAIN)
623 {
624 if(Track::getCurrentTrack())
625 Track::getCurrentTrack()->cleanup();
626 }
627 else
628 Track::cleanChildTrack();
629
630 // Delete the in-race-gui:
631 if(m_saved_race_gui)
632 {
633 // If there is a save race gui, this means that the result gui is
634 // currently being shown. The race result gui is a screen and so
635 // is deleted by the state manager. So we only have to delete
636 // the actual race gui:
637 delete m_saved_race_gui;
638 }
639 else
640 {
641 // No race result gui is shown, so m_race_gui is the in-race
642 // gui and this must be deleted.
643 delete m_race_gui;
644 }
645
646 if (m_process_type == PT_MAIN)
647 Weather::kill();
648
649 m_karts.clear();
650 if(RaceManager::get()->hasGhostKarts() || RaceManager::get()->isRecordingRace())
651 {
652 // Destroy the old replay object, which also stored the ghost
653 // karts, and create a new one (which means that in further
654 // races the usage of ghosts will still be enabled).
655 // It can allow auto recreation of ghost replay file lists
656 // when next time visit the ghost replay selection screen.
657 ReplayPlay::destroy();
658 ReplayPlay::create();
659 }
660 if(RaceManager::get()->isRecordingRace())
661 ReplayRecorder::get()->reset();
662 RaceManager::get()->setRaceGhostKarts(false);
663 RaceManager::get()->setRecordRace(false);
664 RaceManager::get()->setWatchingReplay(false);
665 RaceManager::get()->setTimeTarget(0.0f);
666 RaceManager::get()->setSpareTireKartNum(0);
667
668 if (!GUIEngine::isNoGraphics())
669 Camera::removeAllCameras();
670
671 // In case that the track is not found, Physics was not instantiated,
672 // but kill handles this correctly.
673 Physics::destroy();
674
675 if (m_process_type == PT_MAIN)
676 Scripting::ScriptEngine::kill();
677
678 m_world[m_process_type] = NULL;
679
680 if (m_process_type == PT_MAIN)
681 irr_driver->getSceneManager()->clear();
682
683 #ifdef DEBUG
684 m_magic_number = 0xDEADBEEF;
685 #endif
686
687 } // ~World
688
689 //-----------------------------------------------------------------------------
690 /** Called when 'go' is being displayed for the first time. Here the brakes
691 * of the karts are released.
692 */
onGo()693 void World::onGo()
694 {
695 // Reset the brakes now that the prestart
696 // phase is over (braking prevents the karts
697 // from sliding downhill)
698 for(unsigned int i=0; i<m_karts.size(); i++)
699 {
700 if (m_karts[i]->isGhostKart()) continue;
701 m_karts[i]->getVehicle()->setAllBrakes(0);
702 }
703 // Reset track objects 1 more time to make sure all instances of moveable
704 // fall at the same instant when race start in network
705 if (NetworkConfig::get()->isNetworking())
706 {
707 PtrVector<TrackObject>& objs = Track::getCurrentTrack()
708 ->getTrackObjectManager()->getObjects();
709 for (TrackObject* curr : objs)
710 {
711 if (curr->getPhysicalObject())
712 {
713 curr->reset();
714 curr->resetEnabled();
715 }
716 }
717 }
718 } // onGo
719
720 //-----------------------------------------------------------------------------
721 /** Called at the end of a race. Updates highscores, pauses the game, and
722 * informs the unlock manager about the finished race. This function must
723 * be called after all other stats were updated from the different game
724 * modes.
725 */
terminateRace()726 void World::terminateRace()
727 {
728 // In case the user opened paused dialog in network
729 if (!GUIEngine::isNoGraphics())
730 {
731 GUIEngine::ScreenKeyboard::dismiss();
732 GUIEngine::ModalDialog::dismiss();
733 }
734
735 m_schedule_pause = false;
736 m_schedule_unpause = false;
737
738 // Update the estimated finishing time for all karts that haven't
739 // finished yet.
740 const unsigned int kart_amount = getNumKarts();
741 for(unsigned int i = 0; i < kart_amount ; i++)
742 {
743 if(!m_karts[i]->hasFinishedRace() && !m_karts[i]->isEliminated())
744 {
745 m_karts[i]->finishedRace(
746 estimateFinishTimeForKart(m_karts[i].get()));
747
748 }
749 } // i<kart_amount
750
751 // Update highscores, and retrieve the best highscore if relevant
752 // to show it in the GUI
753 int best_highscore_rank = -1;
754 std::string highscore_who = "";
755 if (!isNetworkWorld())
756 {
757 updateHighscores(&best_highscore_rank);
758 }
759
760 if (m_process_type == PT_MAIN)
761 {
762 updateAchievementDataEndRace();
763 PlayerManager::getCurrentPlayer()->raceFinished();
764 }
765
766 if (m_race_gui) m_race_gui->clearAllMessages();
767 // we can't delete the race gui here, since it is needed in case of
768 // a restart: the constructor of it creates some textures which assume
769 // that no scene nodes exist. In case of a restart there are scene nodes,
770 // so we can't create the race gui again, so we keep it around
771 // and save the pointer.
772 assert(m_saved_race_gui==NULL);
773 m_saved_race_gui = m_race_gui;
774
775 if (!GUIEngine::isNoGraphics())
776 {
777 RaceResultGUI* results = RaceResultGUI::getInstance();
778 m_race_gui = results;
779 if (best_highscore_rank > 0)
780 results->setHighscore(best_highscore_rank);
781 else
782 results->clearHighscores();
783 results->push();
784 }
785
786 WorldStatus::terminateRace();
787 } // terminateRace
788
789 //-----------------------------------------------------------------------------
790 /** Waits till each kart is resting on the ground
791 *
792 * Does simulation steps still all karts reach the ground, i.e. are not
793 * moving anymore
794 */
resetAllKarts()795 void World::resetAllKarts()
796 {
797 // Reset the physics 'remaining' time to 0 so that the number
798 // of timesteps is reproducible if doing a physics-based history run
799 Physics::get()->getPhysicsWorld()->resetLocalTime();
800
801 // If track checking is requested, check all rescue positions if
802 // they are high enough.
803 if(UserConfigParams::m_track_debug)
804 {
805 // Loop over all karts, in case that some karts are dfferent
806 for(unsigned int kart_id=0; kart_id<(unsigned int)m_karts.size(); kart_id++)
807 {
808 if (m_karts[kart_id]->isGhostKart()) continue;
809 for(unsigned int rescue_pos=0;
810 rescue_pos<getNumberOfRescuePositions();
811 rescue_pos++)
812 {
813 btTransform t = getRescueTransform(rescue_pos);
814 // This will print out warnings if there is no terrain under
815 // the kart, or the kart is being dropped on a reset texture
816 moveKartTo(m_karts[kart_id].get(), t);
817
818 } // rescue_pos<getNumberOfRescuePositions
819
820 // Reset the karts back to the original start position.
821 // This call is a bit of an overkill, but setting the correct
822 // transforms, positions, motion state is a bit of a hassle.
823 m_karts[kart_id]->reset();
824 } // for kart_id<m_karts.size()
825
826
827 } // if m_track_debug
828
829 m_schedule_pause = false;
830 m_schedule_unpause = false;
831
832 //Project karts onto track from above. This will lower each kart so
833 //that at least one of its wheel will be on the surface of the track
834 for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
835 {
836 if ((*i)->isGhostKart()) continue;
837 Vec3 xyz = (*i)->getXYZ();
838 //start projection from top of kart
839 Vec3 up_offset = (*i)->getNormal() * (0.5f * ((*i)->getKartHeight()));
840 (*i)->setXYZ(xyz+up_offset);
841
842 bool kart_over_ground = Track::getCurrentTrack()->findGround(i->get());
843
844 if (!kart_over_ground)
845 {
846 Log::error("World",
847 "No valid starting position for kart %d on track %s.",
848 (int)(i - m_karts.begin()),
849 Track::getCurrentTrack()->getIdent().c_str());
850 if (UserConfigParams::m_artist_debug_mode)
851 {
852 Log::warn("World", "Activating fly mode.");
853 (*i)->flyUp();
854 continue;
855 }
856 else
857 {
858 exit(-1);
859 }
860 }
861 }
862
863 // Do a longer initial simulation, which should be long enough for all
864 // karts to be firmly on ground.
865 float g = Track::getCurrentTrack()->getGravity();
866 for (KartList::iterator i = m_karts.begin(); i != m_karts.end(); i++)
867 {
868 if ((*i)->isGhostKart()) continue;
869 (*i)->getBody()->setGravity(
870 (*i)->getMaterial() && (*i)->getMaterial()->hasGravity() ?
871 (*i)->getNormal() * -g : Vec3(0, -g, 0));
872 }
873 for(int i=0; i<stk_config->getPhysicsFPS(); i++)
874 Physics::get()->update(1);
875
876 for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
877 {
878 (*i)->kartIsInRestNow();
879 }
880
881 // Initialise the cameras, now that the correct kart positions are set
882 if (!GUIEngine::isNoGraphics())
883 {
884 for(unsigned int i=0; i<Camera::getNumCameras(); i++)
885 {
886 Camera::getCamera(i)->setInitialTransform();
887 }
888 }
889 } // resetAllKarts
890
891 // ----------------------------------------------------------------------------
892 /** Places a kart that is rescued. It calls getRescuePositionIndex to find
893 * to which rescue position the kart should be moved, then getRescueTransform
894 * to get the position and rotation of this rescue position, and then moves
895 * the kart.
896 * \param kart The kart that is rescued.
897 */
moveKartAfterRescue(AbstractKart * kart)898 void World::moveKartAfterRescue(AbstractKart* kart)
899 {
900 unsigned int index = getRescuePositionIndex(kart);
901 btTransform t = getRescueTransform(index);
902 moveKartTo(kart, t);
903 } // moveKartAfterRescue
904
905 // ----------------------------------------------------------------------------
906 /** Places the kart at a given position and rotation.
907 * \param kart The kart to be moved.
908 * \param transform
909 */
moveKartTo(AbstractKart * kart,const btTransform & transform)910 void World::moveKartTo(AbstractKart* kart, const btTransform &transform)
911 {
912 btTransform pos(transform);
913
914 // Move the kart
915 Vec3 xyz = pos.getOrigin() +
916 pos.getBasis() * Vec3(0, 0.5f*kart->getKartHeight(), 0);
917 pos.setOrigin(xyz);
918 kart->setXYZ(xyz);
919 kart->setRotation(pos.getRotation());
920
921 kart->getBody()->setCenterOfMassTransform(pos);
922 // The raycast to determine the terrain underneath the kart is done from
923 // the centre point of the 4 wheel positions. After a rescue, the wheel
924 // positions need to be updated (otherwise the raycast will be done from
925 // the previous position, which might be the position that triggered
926 // the rescue in the first place).
927 kart->getVehicle()->updateAllWheelPositions();
928
929 // Project kart to surface of track
930 // This will set the physics transform
931 Track::getCurrentTrack()->findGround(kart);
932 Track::getCurrentTrack()->getCheckManager()->resetAfterKartMove(kart);
933
934 } // moveKartTo
935
936 // ----------------------------------------------------------------------------
schedulePause(Phase phase)937 void World::schedulePause(Phase phase)
938 {
939 if (m_schedule_unpause)
940 {
941 m_schedule_unpause = false;
942 }
943 else
944 {
945 m_schedule_pause = true;
946 m_scheduled_pause_phase = phase;
947 }
948 } // schedulePause
949
950 // ----------------------------------------------------------------------------
scheduleUnpause()951 void World::scheduleUnpause()
952 {
953 if (m_schedule_pause)
954 {
955 m_schedule_pause = false;
956 }
957 else
958 {
959 m_schedule_unpause = true;
960 }
961 } // scheduleUnpause
962
963 //-----------------------------------------------------------------------------
964 /** This is the main interface to update the world. This function calls
965 * update(), and checks then for the end of the race. Note that race over
966 * handling can not necessarily be done in update(), since not all
967 * data structures might have been updated (e.g.LinearWorld must
968 * call World::update() first, to get updated kart positions. If race
969 * over would be handled in World::update, LinearWorld had no opportunity
970 * to update its data structures before the race is finished).
971 * \param ticks Number of physics time steps - should be 1.
972 */
updateWorld(int ticks)973 void World::updateWorld(int ticks)
974 {
975 #ifdef DEBUG
976 assert(m_magic_number == 0xB01D6543);
977 #endif
978
979
980 if (m_schedule_pause)
981 {
982 pause(m_scheduled_pause_phase);
983 m_schedule_pause = false;
984 }
985 else if (m_schedule_unpause)
986 {
987 unpause();
988 m_schedule_unpause = false;
989 }
990
991 // Don't update world if a menu is shown or the race is over.
992 if (getPhase() == FINISH_PHASE ||
993 (!NetworkConfig::get()->isNetworking() &&
994 getPhase() == IN_GAME_MENU_PHASE))
995 return;
996
997 try
998 {
999 update(ticks);
1000 }
1001 catch (AbortWorldUpdateException& e)
1002 {
1003 (void)e; // avoid compiler warning
1004 return;
1005 }
1006
1007 #ifdef DEBUG
1008 assert(m_magic_number == 0xB01D6543);
1009 #endif
1010
1011 if( (!isFinishPhase()) && isRaceOver())
1012 {
1013 enterRaceOverState();
1014 }
1015 else
1016 {
1017 if (m_schedule_exit_race)
1018 {
1019 m_schedule_exit_race = false;
1020 RaceManager::get()->exitRace(false);
1021 RaceManager::get()->setAIKartOverride("");
1022
1023 StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
1024
1025 if (m_schedule_tutorial)
1026 {
1027 m_schedule_tutorial = false;
1028 RaceManager::get()->setNumPlayers(1);
1029 RaceManager::get()->setMajorMode (RaceManager::MAJOR_MODE_SINGLE);
1030 RaceManager::get()->setMinorMode (RaceManager::MINOR_MODE_TUTORIAL);
1031 RaceManager::get()->setNumKarts( 1 );
1032 RaceManager::get()->setTrack( "tutorial" );
1033 RaceManager::get()->setDifficulty(RaceManager::DIFFICULTY_EASY);
1034 RaceManager::get()->setReverseTrack(false);
1035
1036 // Use keyboard 0 by default (FIXME: let player choose?)
1037 InputDevice* device = input_manager->getDeviceManager()->getKeyboard(0);
1038
1039 // Create player and associate player with keyboard
1040 StateManager::get()->createActivePlayer(PlayerManager::getCurrentPlayer(),
1041 device);
1042
1043 if (!kart_properties_manager->getKart(UserConfigParams::m_default_kart))
1044 {
1045 Log::warn("[World]",
1046 "Cannot find kart '%s', will revert to default.",
1047 UserConfigParams::m_default_kart.c_str());
1048 UserConfigParams::m_default_kart.revertToDefaults();
1049 }
1050 RaceManager::get()->setPlayerKart(0, UserConfigParams::m_default_kart);
1051
1052 // ASSIGN should make sure that only input from assigned devices
1053 // is read.
1054 input_manager->getDeviceManager()->setAssignMode(ASSIGN);
1055 input_manager->getDeviceManager()
1056 ->setSinglePlayer( StateManager::get()->getActivePlayer(0) );
1057
1058 delete this;
1059
1060 StateManager::get()->enterGameState();
1061 RaceManager::get()->setupPlayerKartInfo();
1062 RaceManager::get()->startNew(true);
1063 }
1064 else
1065 {
1066 delete this;
1067
1068 if (RaceManager::get()->raceWasStartedFromOverworld())
1069 {
1070 OverWorld::enterOverWorld();
1071 }
1072
1073 }
1074 }
1075 }
1076 } // updateWorld
1077
1078 #define MEASURE_FPS 0
1079
1080 //-----------------------------------------------------------------------------
1081
scheduleTutorial()1082 void World::scheduleTutorial()
1083 {
1084 m_schedule_exit_race = true;
1085 m_schedule_tutorial = true;
1086 } // scheduleTutorial
1087
1088 //-----------------------------------------------------------------------------
1089 /** This updates all only graphical elements. It is only called once per
1090 * rendered frame, not once per time step.
1091 * float dt Time since last frame.
1092 */
updateGraphics(float dt)1093 void World::updateGraphics(float dt)
1094 {
1095 if (auto cl = LobbyProtocol::get<ClientLobby>())
1096 {
1097 // Reset all smooth network body of rewinders so the rubber band effect
1098 // of moveable does not exist during firstly live join.
1099 if (cl->hasLiveJoiningRecently())
1100 RewindManager::get()->resetSmoothNetworkBody();
1101 }
1102
1103 PROFILER_PUSH_CPU_MARKER("World::update (weather)", 0x80, 0x7F, 0x00);
1104 if (UserConfigParams::m_particles_effects > 1 && Weather::getInstance())
1105 {
1106 Weather::getInstance()->update(dt);
1107 }
1108 PROFILER_POP_CPU_MARKER();
1109
1110 // Update graphics of karts, e.g. visual suspension, skid marks
1111 const int kart_amount = (int)m_karts.size();
1112 for (int i = 0; i < kart_amount; ++i)
1113 {
1114 // Update all karts that are visible
1115 if (m_karts[i]->isVisible())
1116 {
1117 m_karts[i]->updateGraphics(dt);
1118 }
1119 }
1120
1121 PROFILER_PUSH_CPU_MARKER("World::updateGraphics (camera)", 0x60, 0x7F, 0);
1122 for (unsigned int i = 0; i < Camera::getNumCameras(); i++)
1123 Camera::getCamera(i)->update(dt);
1124 PROFILER_POP_CPU_MARKER();
1125
1126 Scripting::ScriptEngine *script_engine =
1127 Scripting::ScriptEngine::getInstance();
1128 if (script_engine)
1129 script_engine->update(dt);
1130
1131 ProjectileManager::get()->updateGraphics(dt);
1132 Track::getCurrentTrack()->updateGraphics(dt);
1133 } // updateGraphics
1134
1135 //-----------------------------------------------------------------------------
1136 /** Updates the physics, all karts, the track, and projectile manager.
1137 * \param ticks Number of physics time steps - should be 1.
1138 */
update(int ticks)1139 void World::update(int ticks)
1140 {
1141 #ifdef DEBUG
1142 assert(m_magic_number == 0xB01D6543);
1143 #endif
1144
1145 PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00);
1146
1147 #if MEASURE_FPS
1148 static int time = 0.0f;
1149 time += ticks;
1150 if (time > stk_config->time2Ticks(5.0f))
1151 {
1152 time -= stk_config->time2Ticks(5.0f);
1153 printf("%i\n",irr_driver->getVideoDriver()->getFPS());
1154 }
1155 #endif
1156
1157 PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00);
1158 WorldStatus::update(ticks);
1159 PROFILER_POP_CPU_MARKER();
1160 PROFILER_PUSH_CPU_MARKER("World::update (RewindManager)", 0x20, 0x7F, 0x40);
1161 RewindManager::get()->update(ticks);
1162 PROFILER_POP_CPU_MARKER();
1163
1164 PROFILER_PUSH_CPU_MARKER("World::update (Track object manager)", 0x20, 0x7F, 0x40);
1165 Track::getCurrentTrack()->getTrackObjectManager()->update(stk_config->ticks2Time(ticks));
1166 PROFILER_POP_CPU_MARKER();
1167
1168 PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00);
1169
1170 // Update all the karts. This in turn will also update the controller,
1171 // which causes all AI steering commands set. So in the following
1172 // physics update the new steering is taken into account.
1173 const int kart_amount = (int)m_karts.size();
1174 for (int i = 0 ; i < kart_amount; ++i)
1175 {
1176 SpareTireAI* sta =
1177 dynamic_cast<SpareTireAI*>(m_karts[i]->getController());
1178 // Update all karts that are not eliminated
1179 if(!m_karts[i]->isEliminated() || (sta && sta->isMoving()))
1180 m_karts[i]->update(ticks);
1181 if (isStartPhase())
1182 m_karts[i]->makeKartRest();
1183 }
1184 PROFILER_POP_CPU_MARKER();
1185 if(RaceManager::get()->isRecordingRace()) ReplayRecorder::get()->update(ticks);
1186
1187 PROFILER_PUSH_CPU_MARKER("World::update (projectiles)", 0xa0, 0x7F, 0x00);
1188 ProjectileManager::get()->update(ticks);
1189 PROFILER_POP_CPU_MARKER();
1190
1191 PROFILER_PUSH_CPU_MARKER("World::update (physics)", 0xa0, 0x7F, 0x00);
1192 Physics::get()->update(ticks);
1193 PROFILER_POP_CPU_MARKER();
1194
1195 PROFILER_POP_CPU_MARKER();
1196
1197 #ifdef DEBUG
1198 assert(m_magic_number == 0xB01D6543);
1199 #endif
1200 } // update
1201
1202 // ----------------------------------------------------------------------------
1203 /** Only updates the track. The order in which the various parts of STK are
1204 * updated is quite important (i.e. the track can't be updated as part of
1205 * the standard update call):
1206 * the track must be updated after updating the karts (otherwise the
1207 * checklines would be using the previous kart positions to determine
1208 * new laps, but linear world which determines distance along track would
1209 * be using the new kart positions --> the lap counting line will be
1210 * triggered one frame too late, potentially causing strange behaviour of
1211 * the icons.
1212 * Similarly linear world must update the position of all karts after all
1213 * karts have been updated (i.e. World::update() must be called before
1214 * updating the position of the karts). The check manager (which is called
1215 * from Track::update()) needs the updated distance along track, so track
1216 * update has to be called after updating the race position in linear world.
1217 * That's why there is a separate call for trackUpdate here.
1218 */
updateTrack(int ticks)1219 void World::updateTrack(int ticks)
1220 {
1221 Track::getCurrentTrack()->update(ticks);
1222 } // update Track
1223
1224 // ----------------------------------------------------------------------------
getHighscores() const1225 Highscores* World::getHighscores() const
1226 {
1227 if (isNetworkWorld() || !m_use_highscores) return NULL;
1228
1229 const Highscores::HighscoreType type = "HST_" + getIdent();
1230
1231 Highscores * highscores =
1232 highscore_manager->getHighscores(type,
1233 RaceManager::get()->getNumNonGhostKarts(),
1234 RaceManager::get()->getDifficulty(),
1235 RaceManager::get()->getTrackName(),
1236 RaceManager::get()->getNumLaps(),
1237 RaceManager::get()->getReverseTrack());
1238
1239 return highscores;
1240 } // getHighscores
1241
1242 // ----------------------------------------------------------------------------
1243 /** Called at the end of a race. Checks if the current times are worth a new
1244 * score, if so it notifies the HighscoreManager so the new score is added
1245 * and saved.
1246 */
updateHighscores(int * best_highscore_rank)1247 void World::updateHighscores(int* best_highscore_rank)
1248 {
1249 *best_highscore_rank = -1;
1250
1251 if(!m_use_highscores) return;
1252
1253 // Add times to highscore list. First compute the order of karts,
1254 // so that the timing of the fastest kart is added first (otherwise
1255 // someone might get into the highscore list, only to be kicked out
1256 // again by a faster kart in the same race), which might be confusing
1257 // if we ever decide to display a message (e.g. during a race)
1258 unsigned int *index = new unsigned int[m_karts.size()];
1259
1260 const unsigned int kart_amount = (unsigned int) m_karts.size();
1261 for (unsigned int i=0; i<kart_amount; i++ )
1262 {
1263 index[i] = 999; // first reset the contents of the array
1264 }
1265 for (unsigned int i=0; i<kart_amount; i++ )
1266 {
1267 const int pos = m_karts[i]->getPosition()-1;
1268 if(pos < 0 || pos >= (int)kart_amount) continue; // wrong position
1269 index[pos] = i;
1270 }
1271
1272 for (unsigned int pos=0; pos<kart_amount; pos++)
1273 {
1274 if(index[pos] == 999)
1275 {
1276 // no kart claimed to be in this position, most likely means
1277 // the kart location data is wrong
1278
1279 #ifdef DEBUG
1280 Log::error("[World]", "Incorrect kart positions:");
1281 for (unsigned int i=0; i<m_karts.size(); i++ )
1282 {
1283 Log::error("[World]", "i=%d position %d.",i,
1284 m_karts[i]->getPosition());
1285 }
1286 #endif
1287 continue;
1288 }
1289
1290 // Only record times for local player karts and only if
1291 // they finished the race
1292 if(!m_karts[index[pos]]->getController()->isLocalPlayerController())
1293 continue;
1294 if (!m_karts[index[pos]]->hasFinishedRace()) continue;
1295 if (m_karts[index[pos]]->isEliminated()) continue;
1296
1297 assert(index[pos] < m_karts.size());
1298 Kart *k = (Kart*)m_karts[index[pos]].get();
1299
1300 Highscores* highscores = getHighscores();
1301
1302 int highscore_rank = 0;
1303 // The player is a local player, so there is a name:
1304 highscore_rank = highscores->addData(k->getIdent(),
1305 k->getController()->getName(),
1306 k->getFinishTime() );
1307
1308 if (highscore_rank > 0)
1309 {
1310 if (*best_highscore_rank == -1 ||
1311 highscore_rank < *best_highscore_rank)
1312 {
1313 *best_highscore_rank = highscore_rank;
1314 }
1315
1316 highscore_manager->saveHighscores();
1317 }
1318 } // next position
1319 delete []index;
1320
1321 } // updateHighscores
1322
1323 //-----------------------------------------------------------------------------
1324 /** Returns the n-th player kart. Note that this function is O(N), not O(1),
1325 * so it shouldn't be called inside of loops.
1326 * \param n Index of player kart to return.
1327 */
getPlayerKart(unsigned int n) const1328 AbstractKart *World::getPlayerKart(unsigned int n) const
1329 {
1330 unsigned int count = -1;
1331
1332 for(unsigned int i = 0; i < m_karts.size(); i++)
1333 {
1334 if (m_karts[i]->getController()->isPlayerController())
1335 {
1336 count++;
1337 if (count == n)
1338 return m_karts[i].get();
1339 }
1340 }
1341 return NULL;
1342 } // getPlayerKart
1343
1344 //-----------------------------------------------------------------------------
1345 /** Returns the nth local player kart, i.e. a kart that has a camera.
1346 * Note that in profile mode this means a non player kart could be returned
1347 * (since an AI kart will have the camera).
1348 * \param n Index of player kart to return.
1349 */
getLocalPlayerKart(unsigned int n) const1350 AbstractKart *World::getLocalPlayerKart(unsigned int n) const
1351 {
1352 if(n>=Camera::getNumCameras()) return NULL;
1353 return Camera::getCamera(n)->getKart();
1354 } // getLocalPlayerKart
1355
1356 //-----------------------------------------------------------------------------
1357 /** Remove (eliminate) a kart from the race */
eliminateKart(int kart_id,bool notify_of_elimination)1358 void World::eliminateKart(int kart_id, bool notify_of_elimination)
1359 {
1360 assert(kart_id < (int)m_karts.size());
1361 AbstractKart *kart = m_karts[kart_id].get();
1362 if (kart->isGhostKart()) return;
1363
1364 // Display a message about the eliminated kart in the race gui
1365 if (m_race_gui && notify_of_elimination)
1366 {
1367 for(unsigned int i=0; i<Camera::getNumCameras(); i++)
1368 {
1369 Camera *camera = Camera::getCamera(i);
1370 if(camera->getKart()==kart)
1371 m_race_gui->addMessage(_("You have been eliminated!"), kart,
1372 2.0f);
1373 else
1374 {
1375 // Store the temporary string because clang would mess this up
1376 // (remove the stringw before the wchar_t* is used).
1377 const core::stringw &kart_name = kart->getController()->getName();
1378 m_race_gui->addMessage(_("'%s' has been eliminated.",
1379 kart_name),
1380 camera->getKart(),
1381 2.0f);
1382 }
1383 } // for i < number of cameras
1384 } // if notify_of_elimination
1385
1386 if(kart->getController()->isLocalPlayerController())
1387 {
1388 for(unsigned int i=0; i<Camera::getNumCameras(); i++)
1389 {
1390 // Change the camera so that it will be attached to the leader
1391 // and facing backwards.
1392 Camera *camera = Camera::getCamera(i);
1393 if(camera->getKart()==kart)
1394 camera->setMode(Camera::CM_LEADER_MODE);
1395 }
1396 m_eliminated_players++;
1397 }
1398
1399 // The kart can't be really removed from the m_kart array, since otherwise
1400 // a race can't be restarted. So it's only marked to be eliminated (and
1401 // ignored in all loops). Important:world->getCurrentNumKarts() returns
1402 // the number of karts still racing. This value can not be used for loops
1403 // over all karts, use RaceManager::get()->getNumKarts() instead!
1404 kart->eliminate();
1405 m_eliminated_karts++;
1406
1407 } // eliminateKart
1408
1409 //-----------------------------------------------------------------------------
1410 /** Called to determine the default collectibles to give each player at the
1411 * start for this kind of race. Both parameters are of 'out' type.
1412 * \param collectible_type The type of collectible each kart.
1413 * \param amount The number of collectibles.
1414 */
getDefaultCollectibles(int * collectible_type,int * amount)1415 void World::getDefaultCollectibles(int *collectible_type, int *amount )
1416 {
1417 *collectible_type = PowerupManager::POWERUP_NOTHING;
1418 *amount = 0;
1419 } // getDefaultCollectibles
1420
1421 //-----------------------------------------------------------------------------
1422 /** Pauses the music (and then pauses WorldStatus).
1423 */
pause(Phase phase)1424 void World::pause(Phase phase)
1425 {
1426 if (m_stop_music_when_dialog_open)
1427 music_manager->pauseMusic();
1428 SFXManager::get()->pauseAll();
1429
1430 WorldStatus::pause(phase);
1431 } // pause
1432
1433 //-----------------------------------------------------------------------------
unpause()1434 void World::unpause()
1435 {
1436 if (m_stop_music_when_dialog_open)
1437 music_manager->resumeMusic();
1438 SFXManager::get()->resumeAll();
1439
1440 WorldStatus::unpause();
1441
1442 for(unsigned int i=0; i<m_karts.size(); i++)
1443 {
1444 // Note that we can not test for isPlayerController here, since
1445 // an EndController will also return 'isPlayerController' if the
1446 // kart belonged to a player.
1447 LocalPlayerController *pc =
1448 dynamic_cast<LocalPlayerController*>(m_karts[i]->getController());
1449 if(pc)
1450 pc->resetInputState();
1451 }
1452 } // pause
1453
1454 //-----------------------------------------------------------------------------
escapePressed()1455 void World::escapePressed()
1456 {
1457 for (unsigned i = 0; i < m_karts.size(); i++)
1458 {
1459 for (unsigned j = 0; j < PA_PAUSE_RACE; j++)
1460 {
1461 if (m_karts[i]->isEliminated() || !m_karts[i]->getController()
1462 ->isLocalPlayerController())
1463 continue;
1464 m_karts[i]->getController()->action((PlayerAction)j, 0);
1465 }
1466 }
1467
1468 new RacePausedDialog(0.8f, 0.6f);
1469 } // escapePressed
1470
1471 // ----------------------------------------------------------------------------
1472 /** Returns the start transform with the give index.
1473 * \param rescue_pos Index of the start position to be returned.
1474 * \returns The transform of the corresponding start position.
1475 */
getRescueTransform(unsigned int rescue_pos) const1476 btTransform World::getRescueTransform(unsigned int rescue_pos) const
1477 {
1478 return Track::getCurrentTrack()->getStartTransform(rescue_pos);
1479 } // getRescueTransform
1480
1481 //-----------------------------------------------------------------------------
1482 /** Uses the start position as rescue positions, override if necessary
1483 */
getNumberOfRescuePositions() const1484 unsigned int World::getNumberOfRescuePositions() const
1485 {
1486 return Track::getCurrentTrack()->getNumberOfStartPositions();
1487 } // getNumberOfRescuePositions
1488
1489 //-----------------------------------------------------------------------------
createKartWithTeam(const std::string & kart_ident,int index,int local_player_id,int global_player_id,RaceManager::KartType kart_type,HandicapLevel handicap)1490 std::shared_ptr<AbstractKart> World::createKartWithTeam
1491 (const std::string &kart_ident, int index, int local_player_id,
1492 int global_player_id, RaceManager::KartType kart_type,
1493 HandicapLevel handicap)
1494 {
1495 int cur_red = getTeamNum(KART_TEAM_RED);
1496 int cur_blue = getTeamNum(KART_TEAM_BLUE);
1497 int pos_index = 0;
1498 int position = index + 1;
1499 KartTeam team = KART_TEAM_BLUE;
1500
1501 if (kart_type == RaceManager::KT_AI)
1502 {
1503 if (index < m_red_ai)
1504 team = KART_TEAM_RED;
1505 else
1506 team = KART_TEAM_BLUE;
1507 m_kart_team_map[index] = team;
1508 }
1509 else if (NetworkConfig::get()->isNetworking())
1510 {
1511 m_kart_team_map[index] = RaceManager::get()->getKartInfo(index).getKartTeam();
1512 team = RaceManager::get()->getKartInfo(index).getKartTeam();
1513 }
1514 else
1515 {
1516 int rm_id = index -
1517 (RaceManager::get()->getNumberOfKarts() - RaceManager::get()->getNumPlayers());
1518
1519 assert(rm_id >= 0);
1520 team = RaceManager::get()->getKartInfo(rm_id).getKartTeam();
1521 m_kart_team_map[index] = team;
1522 }
1523
1524 core::stringw online_name;
1525 if (global_player_id > -1)
1526 {
1527 online_name = RaceManager::get()->getKartInfo(global_player_id)
1528 .getPlayerName();
1529 }
1530
1531 // Notice: In blender, please set 1,3,5,7... for blue starting position;
1532 // 2,4,6,8... for red.
1533 if (team == KART_TEAM_BLUE)
1534 {
1535 pos_index = 1 + 2 * cur_blue;
1536 }
1537 else
1538 {
1539 pos_index = 2 + 2 * cur_red;
1540 }
1541
1542 btTransform init_pos = getStartTransform(pos_index - 1);
1543 m_kart_position_map[index] = (unsigned)(pos_index - 1);
1544
1545 std::shared_ptr<RenderInfo> ri = std::make_shared<RenderInfo>();
1546 ri = (team == KART_TEAM_BLUE ? std::make_shared<RenderInfo>(0.66f) :
1547 std::make_shared<RenderInfo>(1.0f));
1548
1549 std::shared_ptr<AbstractKart> new_kart;
1550 if (RewindManager::get()->isEnabled())
1551 {
1552 auto kr = std::make_shared<KartRewinder>(kart_ident, index, position,
1553 init_pos, handicap, ri);
1554 kr->rewinderAdd();
1555 new_kart = kr;
1556 }
1557 else
1558 {
1559 new_kart = std::make_shared<Kart>(kart_ident, index, position,
1560 init_pos, handicap, ri);
1561 }
1562
1563 new_kart->init(RaceManager::get()->getKartType(index));
1564 Controller *controller = NULL;
1565
1566 switch(kart_type)
1567 {
1568 case RaceManager::KT_PLAYER:
1569 controller = new LocalPlayerController(new_kart.get(), local_player_id, handicap);
1570 m_num_players ++;
1571 break;
1572 case RaceManager::KT_NETWORK_PLAYER:
1573 controller = new NetworkPlayerController(new_kart.get());
1574 if (!online_name.empty())
1575 new_kart->setOnScreenText(online_name.c_str());
1576 m_num_players++;
1577 break;
1578 case RaceManager::KT_AI:
1579 controller = loadAIController(new_kart.get());
1580 break;
1581 case RaceManager::KT_GHOST:
1582 case RaceManager::KT_LEADER:
1583 case RaceManager::KT_SPARE_TIRE:
1584 break;
1585 }
1586
1587 new_kart->setController(controller);
1588
1589 return new_kart;
1590 } // createKartWithTeam
1591
1592 //-----------------------------------------------------------------------------
getTeamNum(KartTeam team) const1593 int World::getTeamNum(KartTeam team) const
1594 {
1595 int total = 0;
1596 if (m_kart_team_map.empty()) return total;
1597
1598 for (unsigned int i = 0; i < (unsigned)m_karts.size(); ++i)
1599 {
1600 if (team == getKartTeam(m_karts[i]->getWorldKartId())) total++;
1601 }
1602
1603 return total;
1604 } // getTeamNum
1605
1606 //-----------------------------------------------------------------------------
getKartTeam(unsigned int kart_id) const1607 KartTeam World::getKartTeam(unsigned int kart_id) const
1608 {
1609 std::map<int, KartTeam>::const_iterator n =
1610 m_kart_team_map.find(kart_id);
1611
1612 assert(n != m_kart_team_map.end());
1613 return n->second;
1614 } // getKartTeam
1615
1616 //-----------------------------------------------------------------------------
setAITeam()1617 void World::setAITeam()
1618 {
1619 m_red_ai = RaceManager::get()->getNumberOfRedAIKarts();
1620 m_blue_ai = RaceManager::get()->getNumberOfBlueAIKarts();
1621
1622 for (int i = 0; i < (int)RaceManager::get()->getNumLocalPlayers(); i++)
1623 {
1624 KartTeam team = RaceManager::get()->getKartInfo(i).getKartTeam();
1625
1626 // Happen in profiling mode
1627 if (team == KART_TEAM_NONE)
1628 {
1629 RaceManager::get()->setKartTeam(i, KART_TEAM_BLUE);
1630 team = KART_TEAM_BLUE;
1631 continue; //FIXME, this is illogical
1632 }
1633 }
1634
1635 Log::debug("World", "Blue AI: %d red AI: %d", m_blue_ai, m_red_ai);
1636
1637 } // setAITeam
1638
1639 // As a class name can't be skipped with "using", we use a preprocessor macro
1640 // to clean up the two following functions
1641 #define ACS AchievementsStatus
1642
1643 //-----------------------------------------------------------------------------
1644 /* This function takes care to update all relevant achievements
1645 * and statistics counters related to a finished race. */
updateAchievementDataEndRace()1646 void World::updateAchievementDataEndRace()
1647 {
1648 const unsigned int kart_amount = getNumKarts();
1649
1650 for(unsigned int i = 0; i < kart_amount; i++)
1651 {
1652 // TODO : does this work in multiplayer ?
1653 // TODO : check what happens when abandonning a race in a GP
1654 // Retrieve the current player
1655 if (m_karts[i]->getController()->canGetAchievements())
1656 {
1657 // Increment won races counts and track finished counts
1658 if (RaceManager::get()->isLinearRaceMode())
1659 {
1660 ACS::AchievementData diff;
1661 diff = (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_EASY) ? ACS::EASY_FINISHED :
1662 (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM) ? ACS::MEDIUM_FINISHED :
1663 (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD) ? ACS::HARD_FINISHED :
1664 ACS::BEST_FINISHED;
1665 PlayerManager::increaseAchievement(diff,1);
1666
1667 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_FINISHED);
1668 if (RaceManager::get()->getReverseTrack())
1669 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_FINISHED_REVERSE);
1670
1671 if (RaceManager::get()->modeHasLaps())
1672 {
1673 Track* track = track_manager->getTrack(RaceManager::get()->getTrackName());
1674 int default_lap_num = track->getDefaultNumberOfLaps();
1675 if (RaceManager::get()->getNumLaps() < default_lap_num)
1676 {
1677 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_LESS_LAPS);
1678 }
1679 else if (RaceManager::get()->getNumLaps() > default_lap_num)
1680 {
1681 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_MORE_LAPS);
1682 if (RaceManager::get()->getNumLaps() >= 2*default_lap_num)
1683 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_MIN_TWICE_LAPS);
1684 }
1685 }
1686
1687 int winner_position = 1;
1688 //TODO : check this always work : what happens if the leader is overtaken between the last elimination
1689 // and the results screen ?
1690 if (RaceManager::get()->isFollowMode()) winner_position = 2;
1691 // Check if the player has won
1692 if (m_karts[i]->getPosition() == winner_position)
1693 {
1694 if (RaceManager::get()->getNumNonGhostKarts() >= 2)
1695 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_WON);
1696 else
1697 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_FINISHED_ALONE);
1698 if (RaceManager::get()->getNumberOfAIKarts() >= 3)
1699 {
1700 PlayerManager::increaseAchievement(ACS::WON_RACES,1);
1701 PlayerManager::increaseAchievement(ACS::CONS_WON_RACES,1);
1702 if (RaceManager::get()->isTimeTrialMode())
1703 PlayerManager::increaseAchievement(ACS::WON_TT_RACES,1);
1704 else if (RaceManager::get()->isFollowMode())
1705 PlayerManager::increaseAchievement(ACS::WON_FTL_RACES,1);
1706 else // normal race
1707 PlayerManager::increaseAchievement(ACS::WON_NORMAL_RACES,1);
1708 }
1709 if (RaceManager::get()->getNumberOfAIKarts() >= 5 &&
1710 (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD ||
1711 RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_BEST))
1712 PlayerManager::increaseAchievement(ACS::CONS_WON_RACES_HARD,1);
1713 }
1714 // Race lost, reset the consecutive wins counters
1715 else if (m_karts[i]->getPosition() > winner_position)
1716 {
1717 PlayerManager::resetAchievementData(ACS::CONS_WON_RACES);
1718 PlayerManager::resetAchievementData(ACS::CONS_WON_RACES_HARD);
1719 }
1720 } // if isLinearMode
1721
1722 // Increment egg hunt finished count
1723 else if (RaceManager::get()->isEggHuntMode())
1724 {
1725 PlayerManager::trackEvent(RaceManager::get()->getTrackName(), ACS::TR_EGG_HUNT_FINISHED);
1726 }
1727
1728 updateAchievementModeCounters(false /*start*/);
1729 } // if m_karts[i]->getController()->canGetAchievements()
1730 } // for i<kart_amount
1731 } // updateAchievementDataEndRace
1732
1733 //-----------------------------------------------------------------------------
1734 /* This function updates the race mode start and finish counters.
1735 * \param start - true if start, false if finish */
updateAchievementModeCounters(bool start)1736 void World::updateAchievementModeCounters(bool start)
1737 {
1738 if (RaceManager::get()->isTimeTrialMode())
1739 PlayerManager::increaseAchievement(start ? ACS::TT_STARTED : ACS::TT_FINISHED,1);
1740 else if (RaceManager::get()->isFollowMode())
1741 PlayerManager::increaseAchievement(start ? ACS::FTL_STARTED : ACS::FTL_FINISHED,1);
1742 else if (RaceManager::get()->isEggHuntMode())
1743 PlayerManager::increaseAchievement(start ? ACS::EGG_HUNT_STARTED : ACS::EGG_HUNT_FINISHED,1);
1744 else if (RaceManager::get()->isSoccerMode())
1745 PlayerManager::increaseAchievement(start ? ACS::SOCCER_STARTED : ACS::SOCCER_FINISHED,1);
1746 else if (RaceManager::get()->isBattleMode())
1747 {
1748 if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
1749 PlayerManager::increaseAchievement(start ? ACS::THREE_STRIKES_STARTED : ACS::THREE_STRIKES_FINISHED,1);
1750 else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_CAPTURE_THE_FLAG)
1751 PlayerManager::increaseAchievement(start ? ACS::CTF_STARTED : ACS::CTF_FINISHED,1);
1752 else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FREE_FOR_ALL)
1753 PlayerManager::increaseAchievement(start ? ACS::FFA_STARTED : ACS::FFA_FINISHED,1);
1754 }
1755 else // normal races
1756 PlayerManager::increaseAchievement(start ? ACS::NORMAL_STARTED : ACS::NORMAL_FINISHED,1);
1757
1758 if (RaceManager::get()->hasGhostKarts())
1759 PlayerManager::increaseAchievement(start ? ACS::WITH_GHOST_STARTED : ACS::WITH_GHOST_FINISHED,1);
1760 } // updateAchievementModeCounters
1761 #undef ACS
1762