/** Called at the end of a race. Updates highscores, pauses the game, and * informs the unlock manager about the finished race. This function must * be called after all other stats were updated from the different game * modes. */ void World::terminateRace() { m_schedule_pause = false; m_schedule_unpause = false; // Update the estimated finishing time for all karts that haven't // finished yet. const unsigned int kart_amount = getNumKarts(); for(unsigned int i = 0; i < kart_amount ; i++) { if(!m_karts[i]->hasFinishedRace() && !m_karts[i]->isEliminated()) { m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i])); } } // i<kart_amount // Update highscores, and retrieve the best highscore if relevant // to show it in the GUI int best_highscore_rank = -1; int best_finish_time = -1; std::string highscore_who = ""; StateManager::ActivePlayer* best_player = NULL; if (!this->isNetworkWorld()) { updateHighscores(&best_highscore_rank, &best_finish_time, &highscore_who, &best_player); } // Check achievements PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_COLUMBUS, getTrack()->getIdent(), 1); if (raceHasLaps()) { PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_MARATHONER, "laps", race_manager->getNumLaps()); } Achievement *achiev = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_GOLD_DRIVER); if (achiev) { std::string mode_name = getIdent(); // Get the race mode name int winner_position = 1; unsigned int opponents = achiev->getInfo()->getGoalValue("opponents"); // Get the required opponents number if (mode_name == IDENT_FTL) { winner_position = 2; opponents++; } for(unsigned int i = 0; i < kart_amount; i++) { // Retrieve the current player StateManager::ActivePlayer* p = m_karts[i]->getController()->getPlayer(); if (p && p->getConstProfile() == PlayerManager::getCurrentPlayer()) { // Check if the player has won if (m_karts[i]->getPosition() == winner_position && kart_amount > opponents ) { // Update the achievement mode_name = StringUtils::toLowerCase(mode_name); if (achiev->getValue("opponents") <= 0) PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_GOLD_DRIVER, "opponents", opponents); PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_GOLD_DRIVER, mode_name, 1); } } } // for i < kart_amount } // if (achiev) Achievement *win = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_UNSTOPPABLE); //if achivement has been unlocked if (win->getValue("wins") < 5 ) { for(unsigned int i = 0; i < kart_amount; i++) { // Retrieve the current player StateManager::ActivePlayer* p = m_karts[i]->getController()->getPlayer(); if (p && p->getConstProfile() == PlayerManager::getCurrentPlayer()) { // Check if the player has won if (m_karts[i]->getPosition() == 1 ) { // Increase number of consecutive wins PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_UNSTOPPABLE, "wins", 1); } else { //Set number of consecutive wins to 0 win->reset(); } } } } PlayerManager::getCurrentPlayer()->raceFinished(); if (m_race_gui) m_race_gui->clearAllMessages(); // we can't delete the race gui here, since it is needed in case of // a restart: the constructor of it creates some textures which assume // that no scene nodes exist. In case of a restart there are scene nodes, // so we can't create the race gui again, so we keep it around // and save the pointer. assert(m_saved_race_gui==NULL); m_saved_race_gui = m_race_gui; RaceResultGUI* results = RaceResultGUI::getInstance(); m_race_gui = results; if (best_highscore_rank > 0) { results->setHighscore(highscore_who, best_player, best_highscore_rank, best_finish_time); } else { results->clearHighscores(); } results->push(); WorldStatus::terminateRace(); } // terminateRace
/** Is called by check structures if a kart starts a new lap. * \param kart_index Index of the kart. */ void LinearWorld::newLap(unsigned int kart_index) { KartInfo &kart_info = m_kart_info[kart_index]; AbstractKart *kart = m_karts[kart_index]; // Reset reset-after-lap achievements StateManager::ActivePlayer *c = kart->getController()->getPlayer(); PlayerProfile *p = PlayerManager::getCurrentPlayer(); if (c && c->getConstProfile() == p) { p->getAchievementsStatus()->onLapEnd(); } // Only update the kart controller if a kart that has already finished // the race crosses the start line again. This avoids 'fastest lap' // messages if the end controller does a fastest lap, but especially // allows the end controller to switch end cameras if(kart->hasFinishedRace()) { kart->getController()->newLap(kart_info.m_race_lap); return; } const int lap_count = race_manager->getNumLaps(); // Only increase the lap counter and set the new time if the // kart hasn't already finished the race (otherwise the race_gui // will begin another countdown). if(kart_info.m_race_lap+1 <= lap_count) { assert(kart->getWorldKartId()==kart_index); kart_info.m_time_at_last_lap=getTime(); kart_info.m_race_lap++; m_kart_info[kart_index].m_overall_distance = m_kart_info[kart_index].m_race_lap * m_track->getTrackLength() + getDistanceDownTrackForKart(kart->getWorldKartId()); } // Last lap message (kart_index's assert in previous block already) if (raceHasLaps() && kart_info.m_race_lap+1 == lap_count) { m_race_gui->addMessage(_("Final lap!"), kart, 3.0f, video::SColor(255, 210, 100, 50), true); if(!m_last_lap_sfx_played && lap_count > 1) { if (UserConfigParams::m_music) { m_last_lap_sfx->play(); m_last_lap_sfx_played = true; m_last_lap_sfx_playing = true; // In case that no music is defined if(music_manager->getCurrentMusic() && music_manager->getMasterMusicVolume() > 0.2f) { music_manager->setTemporaryVolume(0.2f); } } else { m_last_lap_sfx_played = true; m_last_lap_sfx_playing = false; } } } else if (raceHasLaps() && kart_info.m_race_lap > 0 && kart_info.m_race_lap+1 < lap_count) { m_race_gui->addMessage(_("Lap %i", kart_info.m_race_lap+1), kart, 3.0f, video::SColor(255, 210, 100, 50), true); } // The race positions must be updated here: consider the situation where // the first kart does not cross the finish line in its last lap, instead // it passes it, the kart reverses and crosses the finishing line // backwards. Just before crossing the finishing line the kart will be on // the last lap, but with a distance along the track close to zero. // Therefore its position will be wrong. If the race position gets updated // after increasing the number of laps (but before tagging the kart to have // finished the race) the position will be correct (since the kart now // has one additional lap it will be ahead of the other karts). // Without this call the incorrect position for this kart would remain // (since a kart that has finished the race does not get its position // changed anymore), potentially resulting in a duplicated race position // (since the first kart does not have position 1, no other kart can get // position 1, so one rank will be duplicated). // Similarly the situation can happen if the distance along track should // go back to zero before actually crossing the finishing line. While this // should not happen, it could potentially be caused by floating point // errors. In this case the call to updateRacePosition will avoid // duplicated race positions as well. updateRacePosition(); // Race finished if(kart_info.m_race_lap >= race_manager->getNumLaps() && raceHasLaps()) { kart->finishedRace(getTime()); } float time_per_lap; if (kart_info.m_race_lap == 1) // just completed first lap { time_per_lap=getTime(); } else //completing subsequent laps { time_per_lap=getTime() - kart_info.m_lap_start_time; } // if new fastest lap if(time_per_lap < m_fastest_lap && raceHasLaps() && kart_info.m_race_lap>0) { m_fastest_lap = time_per_lap; std::string s = StringUtils::timeToString(time_per_lap); // Store the temporary string because clang would mess this up // (remove the stringw before the wchar_t* is used). const core::stringw &kart_name = kart->getName(); //I18N: as in "fastest lap: 60 seconds by Wilber" irr::core::stringw m_fastest_lap_message = _C("fastest_lap", "%s by %s", s.c_str(), kart_name); m_race_gui->addMessage(m_fastest_lap_message, NULL, 3.0f, video::SColor(255, 255, 255, 255), false); m_race_gui->addMessage(_("New fastest lap"), NULL, 3.0f, video::SColor(255, 255, 255, 255), false); } // end if new fastest lap kart_info.m_lap_start_time = getTime(); kart->getController()->newLap(kart_info.m_race_lap); } // newLap
//----------------------------------------------------------------------------- void LinearWorld::getKartsDisplayInfo( std::vector<RaceGUIBase::KartIconDisplayInfo> *info) { int laps_of_leader = -1; float time_of_leader = -1; // Find the best time for the lap. We can't simply use // the time of the kart at position 1, since the kart // might have been overtaken by now const unsigned int kart_amount = getNumKarts(); for(unsigned int i = 0; i < kart_amount ; i++) { RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i]; AbstractKart* kart = m_karts[i]; // reset color rank_info.m_color = video::SColor(255, 255, 255, 255); rank_info.lap = -1; if(kart->isEliminated()) continue; const float lap_time = getTimeAtLapForKart(kart->getWorldKartId()); const int current_lap = getLapForKart( kart->getWorldKartId() ); rank_info.lap = current_lap; if(current_lap > laps_of_leader) { // more laps than current leader --> new leader and // new time computation laps_of_leader = current_lap; time_of_leader = lap_time; } else if(current_lap == laps_of_leader) { // Same number of laps as leader: use fastest time time_of_leader=std::min(time_of_leader,lap_time); } } // we now know the best time of the lap. fill the remaining bits of info for(unsigned int i = 0; i < kart_amount ; i++) { RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i]; KartInfo& kart_info = m_kart_info[i]; AbstractKart* kart = m_karts[i]; const int position = kart->getPosition(); // Don't compare times when crossing the start line first if(laps_of_leader>0 && (getTime() - getTimeAtLapForKart(kart->getWorldKartId())<5.0f || rank_info.lap != laps_of_leader) && raceHasLaps()) { // Display for 5 seconds std::string str; if(position == 1) { str = " " + StringUtils::timeToString( getTimeAtLapForKart(kart->getWorldKartId()) ); } else { float timeBehind; timeBehind = (kart_info.m_race_lap==laps_of_leader ? getTimeAtLapForKart(kart->getWorldKartId()) : getTime()) - time_of_leader; str = "+" + StringUtils::timeToString(timeBehind); } rank_info.m_text = irr::core::stringw(str.c_str()); } else { rank_info.m_text = ""; } int numLaps = race_manager->getNumLaps(); if(kart_info.m_race_lap>=numLaps) { // kart is finished, display in green rank_info.m_color.setGreen(0); rank_info.m_color.setBlue(0); } else if(kart_info.m_race_lap>=0 && numLaps>1) { int col = (int)(255*(1.0f-(float)kart_info.m_race_lap /((float)numLaps-1.0f) )); rank_info.m_color.setBlue(col); rank_info.m_color.setGreen(col); } } // next kart } // getKartsDisplayInfo
/** Is called by check structures if a kart starts a new lap. * \param kart_index Index of the kart. */ void LinearWorld::newLap(unsigned int kart_index) { KartInfo &kart_info = m_kart_info[kart_index]; Kart *kart = m_karts[kart_index]; // Don't do anything if a kart that has already finished the race // crosses the start line again. This avoids 'fastest lap' messages // if the end controller does a fastest lap. if(kart->hasFinishedRace()) return; const int lap_count = race_manager->getNumLaps(); // Only increase the lap counter and set the new time if the // kart hasn't already finished the race (otherwise the race_gui // will begin another countdown). if(kart_info.m_race_lap+1 <= lap_count) { assert(kart->getWorldKartId()==kart_index); setTimeAtLapForKart(getTime(), kart_index ); kart_info.m_race_lap++ ; } // Last lap message (kart_index's assert in previous block already) if(kart_info.m_race_lap+1 == lap_count) { m_race_gui->addMessage(_("Final lap!"), m_karts[kart_index], 3.0f, 40, video::SColor(255, 210, 100, 50), true); if(!m_last_lap_sfx_played && lap_count > 1) { m_last_lap_sfx->play(); m_last_lap_sfx_played = true; m_last_lap_sfx_playing = true; // In case that no music is defined if(music_manager->getCurrentMusic() && music_manager->getMasterMusicVolume() > 0.2f) { music_manager->getCurrentMusic()->setTemporaryVolume(0.2f); } } } // The race positions must be updated here: consider the situation where // the first kart does not cross the finish line in its last lap, instead // it passes it, the kart reverses and crosses the finishing line // backwards. Just before crossing the finishing line the kart will be on // the last lap, but with a distance along the track close to zero. // Therefore its position will be wrong. If the race position gets updated // after increasing the number of laps (but before tagging the kart to have // finished the race) the position will be correct (since the kart now // has one additional lap it will be ahead of the other karts). // Without this call the incorrect position for this kart would remain // (since a kart that has finished the race does not get its position // changed anymore), potentially resulting in a duplicated race position // (since the first kart does not have position 1, no other kart can get // position 1, so one rank will be duplicated). // Similarly the situation can happen if the distance along track should // go back to zero before actually crossing the finishing line. While this // should not happen, it could potentially be caused by floating point // errors. In this case the call to updateRacePosition will avoid // duplicated race positions as well. updateRacePosition(); // Race finished if(kart_info.m_race_lap >= race_manager->getNumLaps() && raceHasLaps()) { // A client wait does not detect race finished by itself, it will // receive a message from the server. So a client does not do // anything here. if(network_manager->getMode()!=NetworkManager::NW_CLIENT) { kart->finishedRace(getTime()); } } { float time_per_lap; if (kart_info.m_race_lap == 1) // just completed first lap { time_per_lap=getTime(); } else //completing subsequent laps { time_per_lap=getTime() - kart_info.m_lap_start_time; } // if new fastest lap if(time_per_lap < getFastestLapTime() && raceHasLaps() && kart_info.m_race_lap>0) { setFastestLap(kart, time_per_lap); m_race_gui->addMessage(_("New fastest lap"), NULL, 2.0f, 40, video::SColor(255, 100, 210, 100), true); std::string s = StringUtils::timeToString(time_per_lap); irr::core::stringw m_fastest_lap_message; //I18N: as in "fastest lap: 60 seconds by Wilber" m_fastest_lap_message += StringUtils::insertValues(_("%s by %s"), s.c_str(), kart->getName().c_str()).c_str(); m_race_gui->addMessage(m_fastest_lap_message, NULL, 2.0f, 40, video::SColor(255, 100, 210, 100)); } // end if new fastest lap } kart_info.m_lap_start_time = getTime(); } // newLap