//----------------------------------------------------------------------------- void LinearWorld::restartRace() { WorldWithRank::restartRace(); //if(m_last_lap_sfx->getStatus()== SFXManager::SFX_PLAYING) // m_last_lap_sfx->stop(); m_last_lap_sfx_played = false; m_last_lap_sfx_playing = false; const unsigned int kart_amount = m_karts.size(); for(unsigned int i=0; i<kart_amount; i++) { KartInfo& info = m_kart_info[i]; info.m_track_sector = QuadGraph::UNKNOWN_SECTOR; info.m_last_valid_sector = 0; info.m_lap_start_time = 0; m_track->getQuadGraph().findRoadSector(m_karts[i]->getXYZ(), &info.m_track_sector); //If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of //the road, so we have to use another function to find the sector. info.m_on_road = info.m_track_sector != QuadGraph::UNKNOWN_SECTOR; if (!info.m_on_road) { info.m_track_sector = m_track->getQuadGraph().findOutOfRoadSector(m_karts[i]->getXYZ(), QuadGraph::UNKNOWN_SECTOR ); } m_track->getQuadGraph().spatialToTrack(&info.m_curr_track_coords, m_karts[i]->getXYZ(), info.m_track_sector ); info.m_race_lap = -1; info.m_lap_start_time = -0; info.m_time_at_last_lap = 99999.9f; } // next kart // First all kart infos must be updated before the kart position can be // recomputed, since otherwise 'new' (initialised) valued will be compared // with old values. updateRacePosition(); #ifdef DEBUG //FIXME: this could be defined somewhere in a central header so it can be used everywhere #define assertExpr( ARG1, OP, ARG2 ) if (!(ARG1 OP ARG2)) \ { \ std::cerr << "Failed assert " << #ARG1 << #OP << #ARG2 << " @ " << __FILE__ << ":" << __LINE__ \ << "; values are (" << ARG1 << #OP << ARG2 << ")\n"; \ assert(false); \ } for (unsigned int i=0; i<kart_amount; i++) { for (unsigned int j=i+1; j<kart_amount; j++) { assertExpr( m_karts[i]->getPosition(), !=, m_karts[j]->getPosition() ); } } #endif } // restartRace
/** Called before a race is started (or restarted). It resets the data * structures that keep track of position and distance long track of * all karts. */ void LinearWorld::reset() { WorldWithRank::reset(); m_last_lap_sfx_played = false; m_last_lap_sfx_playing = false; const unsigned int kart_amount = (unsigned int) m_karts.size(); for(unsigned int i=0; i<kart_amount; i++) { m_kart_info[i].reset(); m_karts[i]->setWrongwayCounter(0); } // next kart // At the moment the last kart would be the one that is furthest away // from the start line, i.e. it would determine the amount by which // the track length must be extended (to avoid negative numbers in // estimateFinishTimeForKart()). On the other hand future game modes // might not follow this rule, so check the distance for all karts here: m_distance_increase = m_track->getTrackLength(); for(unsigned int i=0; i<kart_amount; i++) { m_distance_increase = std::min(m_distance_increase, getDistanceDownTrackForKart(i)); } // Track length - minimum distance is how much the track length must // be increased to avoid negative values in estimateFinishTimeForKart // Increase this value somewhat in case that a kart drivess/slides // backwards a little bit at start. m_distance_increase = m_track->getTrackLength() - m_distance_increase + 5.0f; if(m_distance_increase<0) m_distance_increase = 1.0f; // shouldn't happen // First all kart infos must be updated before the kart position can be // recomputed, since otherwise 'new' (initialised) valued will be compared // with old values. updateRacePosition(); #ifdef DEBUG //FIXME: this could be defined somewhere in a central header so it can // be used everywhere #define assertExpr( ARG1, OP, ARG2 ) if (!(ARG1 OP ARG2)) \ { \ std::cerr << "Failed assert " << #ARG1 << #OP << #ARG2 << " @ " \ << __FILE__ << ":" << __LINE__ \ << "; values are (" << ARG1 << #OP << ARG2 << ")\n"; \ assert(false); \ } for (unsigned int i=0; i<kart_amount; i++) { for (unsigned int j=i+1; j<kart_amount; j++) { assertExpr( m_karts[i]->getPosition(), !=, m_karts[j]->getPosition() ); } } #endif } // reset
//----------------------------------------------------------------------------- void LinearWorld::restartRace() { WorldWithRank::restartRace(); //if(m_last_lap_sfx->getStatus()== SFXManager::SFX_PLAYING) // m_last_lap_sfx->stop(); m_last_lap_sfx_played = false; m_last_lap_sfx_playing = false; const unsigned int kart_amount = m_karts.size(); for(unsigned int i=0; i<kart_amount; i++) { m_kart_info[i].reset(); m_kart_info[i].getSector()->update(m_karts[i]->getXYZ()); } // next kart // First all kart infos must be updated before the kart position can be // recomputed, since otherwise 'new' (initialised) valued will be compared // with old values. updateRacePosition(); #ifdef DEBUG //FIXME: this could be defined somewhere in a central header so it can // be used everywhere #define assertExpr( ARG1, OP, ARG2 ) if (!(ARG1 OP ARG2)) \ { \ std::cerr << "Failed assert " << #ARG1 << #OP << #ARG2 << " @ " << __FILE__ << ":" << __LINE__ \ << "; values are (" << ARG1 << #OP << ARG2 << ")\n"; \ assert(false); \ } for (unsigned int i=0; i<kart_amount; i++) { for (unsigned int j=i+1; j<kart_amount; j++) { assertExpr( m_karts[i]->getPosition(), !=, m_karts[j]->getPosition() ); } } #endif } // restartRace
/** 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
/** General update function called once per frame. This updates the kart * sectors, which are then used to determine the kart positions. * \param dt Time step size. */ void LinearWorld::update(float dt) { // run generic parent stuff that applies to all modes. It // especially updates the kart positions. WorldWithRank::update(dt); if (m_last_lap_sfx_playing && m_last_lap_sfx->getStatus() != SFXBase::SFX_PLAYING) { music_manager->resetTemporaryVolume(); m_last_lap_sfx_playing = false; } const unsigned int kart_amount = getNumKarts(); // Do stuff specific to this subtype of race. // ------------------------------------------ for(unsigned int n=0; n<kart_amount; n++) { KartInfo& kart_info = m_kart_info[n]; AbstractKart* kart = m_karts[n]; // Nothing to do for karts that are currently being // rescued or eliminated if(kart->getKartAnimation()) continue; kart_info.getTrackSector()->update(kart->getFrontXYZ()); kart_info.m_overall_distance = kart_info.m_race_lap * m_track->getTrackLength() + getDistanceDownTrackForKart(kart->getWorldKartId()); } // for n // Update all positions. This must be done after _all_ karts have // updated their position and laps etc, otherwise inconsistencies // (like two karts at same position) can occur. // --------------------------------------------------------------- WorldWithRank::updateTrack(dt); updateRacePosition(); for (unsigned int i=0; i<kart_amount; i++) { // ---------- update rank ------ if (m_karts[i]->hasFinishedRace() || m_karts[i]->isEliminated() ) continue; // During the last lap update the estimated finish time. // This is used to play the faster music, and by the AI if (m_kart_info[i].m_race_lap == race_manager->getNumLaps()-1) { m_kart_info[i].m_estimated_finish = estimateFinishTimeForKart(m_karts[i]); } checkForWrongDirection(i, dt); } #ifdef DEBUG // Debug output in case that the double position error occurs again. std::vector<int> pos_used; pos_used.resize(kart_amount+1, -99); for(unsigned int i=0; i<kart_amount; i++) { if(pos_used[m_karts[i]->getPosition()]!=-99) { for(unsigned int j=0; j<kart_amount; j++) { Log::verbose("[LinearWorld]", "kart id=%u, position=%d, finished=%d, laps=%d, " "distanceDownTrack=%f overallDistance=%f %s", j, m_karts[j]->getPosition(), m_karts[j]->hasFinishedRace(), m_kart_info[j].m_race_lap, getDistanceDownTrackForKart(m_karts[j]->getWorldKartId()), m_kart_info[j].m_overall_distance, (m_karts[j]->getPosition() == m_karts[i]->getPosition() ? "<--- !!!" : "") ); } } pos_used[m_karts[i]->getPosition()]=i; } #endif } // update
/** Called when a kart must be eliminated. */ void FollowTheLeaderRace::countdownReachedZero() { if(m_leader_intervals.size()>1) m_leader_intervals.erase(m_leader_intervals.begin()); WorldStatus::setTime(m_leader_intervals[0]); // If the leader kart is not the first kart, remove the first // kart, otherwise remove the last kart. int position_to_remove = m_karts[0]->getPosition()==1 ? getCurrentNumKarts() : 1; AbstractKart *kart = getKartAtPosition(position_to_remove); if(!kart || kart->isEliminated()) { fprintf(stderr,"Problem with removing leader: position %d not found\n", position_to_remove); for(unsigned int i=0; i<m_karts.size(); i++) { fprintf(stderr,"kart %d: eliminated %d position %d\n", i,m_karts[i]->isEliminated(), m_karts[i]->getPosition()); } // for i } // else { if(UserConfigParams::m_ftl_debug) { printf("[ftl] Eliminiating kart '%s' at position %d.\n", kart->getIdent().c_str(), position_to_remove); } eliminateKart(kart->getWorldKartId()); // In case that the kart on position 1 was removed, we have // to set the correct position (which equals the remaining // number of karts) for the removed kart, as well as recompute // the position for all other karts if(position_to_remove==1) { // We have to add 1 to the number of karts to get the correct // position, since the eliminated kart was already removed // from the value returned by getCurrentNumKarts (and we have // to remove the kart before we can call updateRacePosition). // Note that we can not call WorldWithRank::setKartPosition // here, since it would not properly support debugging kart // ranks (since this kart would get its position set again // in updateRacePosition). We only set the rank of the eliminated // kart, and updateRacePosition will then call setKartPosition // for the now eliminated kart. kart->setPosition(getCurrentNumKarts()+1); updateRacePosition(); } // Time doesn't make any sense in FTL (and it is not displayed) kart->finishedRace(-1.0f); // Move any camera for this kart to the leader, facing backwards, // so that the eliminated player has something to watch. if (race_manager->getNumPlayers() > 1) { for(unsigned int i=0; i<Camera::getNumCameras(); i++) { Camera *camera = Camera::getCamera(i); if(camera->getKart()==kart) { camera->setMode(Camera::CM_LEADER_MODE); camera->setKart(getKart(0)); } } // for i<number of cameras } } // if kart to eliminate exists // almost over, use fast music if(getCurrentNumKarts()==3) { music_manager->switchToFastMusic(); } if (isRaceOver()) { // Handle special FTL situation: the leader is kart number 3 when // the last kart gets eliminated. In this case kart on position 1 // is eliminated, and the kart formerly on position 2 is on // position 1, the leader now position 2. In this case the kart // on position 1 would get more points for this victory. So if // this is the case, change the position if(m_karts[0]->getPosition()!=1) { // Adjust the position of all still driving karts that // are ahead of the leader by +1, and move the leader // to position 1. for (unsigned int i=1; i<m_karts.size(); i++) { if(!m_karts[i]->hasFinishedRace() && !m_karts[i]->isEliminated() && m_karts[i]->getPosition()<m_karts[0]->getPosition()) { m_karts[i]->setPosition(m_karts[i]->getPosition()+1); } } m_karts[0]->setPosition(1); } // Mark all still racing karts to be finished. for (unsigned int n=0; n<m_karts.size(); n++) { if (!m_karts[n]->isEliminated() && !m_karts[n]->hasFinishedRace()) { m_karts[n]->finishedRace(getTime()); } } } // End of race is detected from World::updateWorld() } // countdownReachedZero
/** 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
/** General update function called once per frame. This updates the kart * sectors, which are then used to determine the kart positions. * \param dt Time step size. */ void LinearWorld::update(float delta) { // run generic parent stuff that applies to all modes. It // especially updates the kart positions. WorldWithRank::update(delta); if (m_last_lap_sfx_playing && m_last_lap_sfx->getStatus() != SFXManager::SFX_PLAYING) { music_manager->getCurrentMusic()->resetTemporaryVolume(); m_last_lap_sfx_playing = false; } const unsigned int kart_amount = getNumKarts(); // Do stuff specific to this subtype of race. // ------------------------------------------ for(unsigned int n=0; n<kart_amount; n++) { KartInfo& kart_info = m_kart_info[n]; Kart* kart = m_karts[n]; // Nothing to do for karts that are currently being rescued or eliminated if(kart->playingEmergencyAnimation()) continue; // ---------- deal with sector data --------- // update sector variables int prev_sector = kart_info.m_track_sector; m_track->getQuadGraph().findRoadSector(kart->getXYZ(), &kart_info.m_track_sector); kart_info.m_on_road = kart_info.m_track_sector != QuadGraph::UNKNOWN_SECTOR; if(kart_info.m_on_road) { kart_info.m_last_valid_sector = kart_info.m_track_sector; kart_info.m_last_valid_race_lap = kart_info.m_race_lap; } else { // Kart off road. Find the closest sector instead. kart_info.m_track_sector = m_track->getQuadGraph().findOutOfRoadSector(kart->getXYZ(), prev_sector ); } // Update track coords (=progression) m_track->getQuadGraph().spatialToTrack(&kart_info.m_curr_track_coords, kart->getXYZ(), kart_info.m_track_sector ); } // for n // Update all positions. This must be done after _all_ karts have // updated their position and laps etc, otherwise inconsistencies // (like two karts at same position) can occur. // --------------------------------------------------------------- updateRacePosition(); for (unsigned int i=0; i<kart_amount; i++) { // ---------- update rank ------ if (m_karts[i]->hasFinishedRace() || m_karts[i]->isEliminated()) continue; // During the last lap update the estimated finish time. // This is used to play the faster music, and by the AI if (m_kart_info[i].m_race_lap == race_manager->getNumLaps()-1) { m_kart_info[i].m_estimated_finish = estimateFinishTimeForKart(m_karts[i]); } checkForWrongDirection(i); } #ifdef DEBUG // FIXME: Debug output in case that the double position error occurs again. std::vector<int> pos_used; pos_used.resize(kart_amount+1, -99); for(unsigned int i=0; i<kart_amount; i++) { if(pos_used[m_karts[i]->getPosition()]!=-99) { for(unsigned int j =0; j<kart_amount; j++) { printf("kart id=%d, position=%d, finished=%d, laps=%d, distanceDownTrack=%f %s\n", j, m_karts[j]->getPosition(), m_karts[j]->hasFinishedRace(), m_kart_info[j].m_race_lap, getDistanceDownTrackForKart(m_karts[j]->getWorldKartId()), (m_karts[j]->getPosition() == m_karts[i]->getPosition() ? "<--- !!!" : "")); } } pos_used[m_karts[i]->getPosition()]=i; } #endif } // update