/** 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(); } StateManager::get()->pushScreen(results); WorldStatus::terminateRace(); } // terminateRace
//----------------------------------------------------------------------------- RaceGUIBase::KartIconDisplayInfo* LinearWorld::getKartsDisplayInfo() { 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 = m_kart_display_info[i]; Kart* kart = m_karts[i]; // reset color rank_info.r = 1.0; rank_info.g = 1.0; rank_info.b = 1.0; 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 = m_kart_display_info[i]; KartInfo& kart_info = m_kart_info[i]; Kart* kart = m_karts[i]; const int position = kart->getPosition(); if(laps_of_leader>0 && // Don't compare times when crossing the start line first (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.g = rank_info.b = 0; } else if(kart_info.m_race_lap>=0 && numLaps>1) { rank_info.g = rank_info.b = 1.0f-(float)kart_info.m_race_lap/((float)numLaps-1.0f); } } // next kart return m_kart_display_info; } // getKartsDisplayInfo
/** 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
/** 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; // If the kart is off road, and 'flying' over a reset plane // don't adjust the distance of the kart, to avoid a jump // in the position of the kart (e.g. while falling the kart // might get too close to another part of the track, shortly // jump to position one, then on reset fall back to last) if ((!getTrackSector(n)->isOnRoad() && (!kart->getMaterial() || kart->getMaterial()->isDriveReset())) && !kart->isGhostKart()) continue; getTrackSector(n)->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
/** 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() != SFXManager::SFX_PLAYING) { if(music_manager->getCurrentMusic()) 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]; 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->getXYZ()); 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); } #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++) { printf("kart id=%d, position=%d, finished=%d, laps=%d, " "distanceDownTrack=%f overallDistance=%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_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 is hit. * \param kart_id The world kart id of the kart that was hit. */ void ThreeStrikesBattle::kartHit(const int kart_id) { assert(kart_id >= 0); assert(kart_id < (int)m_karts.size()); // make kart lose a life m_kart_info[kart_id].m_lives--; // record event BattleEvent evt; evt.m_time = getTime(); evt.m_kart_info = m_kart_info; m_battle_events.push_back(evt); updateKartRanks(); // check if kart is 'dead' if (m_kart_info[kart_id].m_lives < 1) { m_karts[kart_id]->finishedRace(WorldStatus::getTime()); scene::ISceneNode** wheels = m_karts[kart_id]->getKartModel() ->getWheelNodes(); if(wheels[0]) wheels[0]->setVisible(false); if(wheels[1]) wheels[1]->setVisible(false); if(wheels[2]) wheels[2]->setVisible(false); if(wheels[3]) wheels[3]->setVisible(false); eliminateKart(kart_id, /*notify_of_elimination*/ true); m_insert_tire = 4; } const unsigned int NUM_KARTS = getNumKarts(); int num_karts_many_lives = 0; for (unsigned int n = 0; n < NUM_KARTS; ++n) { if (m_kart_info[n].m_lives > 1) num_karts_many_lives++; } // when almost over, use fast music if (num_karts_many_lives<=1 && !m_faster_music_active) { music_manager->switchToFastMusic(); m_faster_music_active = true; } scene::ISceneNode* kart_node = m_karts[kart_id]->getNode(); // FIXME: sorry for this ugly const_cast, irrlicht doesn't seem to allow // getting a writable list of children, wtf?? core::list<scene::ISceneNode*>& children = const_cast<core::list<scene::ISceneNode*>&>(kart_node->getChildren()); for (core::list<scene::ISceneNode*>::Iterator it = children.begin(); it != children.end(); it++) { scene::ISceneNode* curr = *it; if (core::stringc(curr->getName()) == "tire1") { curr->setVisible(m_kart_info[kart_id].m_lives >= 3); } else if (core::stringc(curr->getName()) == "tire2") { curr->setVisible(m_kart_info[kart_id].m_lives >= 2); } } // schedule a tire to be thrown away (but can't do it in this callback // because the caller is currently iterating the list of track objects) m_insert_tire++; core::vector3df wheel_pos(m_karts[kart_id]->getKartWidth()*0.5f, 0.0f, 0.0f); m_tire_position = kart_node->getPosition() + wheel_pos; m_tire_rotation = 0; if(m_insert_tire > 1) { m_tire_position = kart_node->getPosition(); m_tire_rotation = m_karts[kart_id]->getHeading(); } for(unsigned int i=0; i<4; i++) { m_tire_offsets[i] = m_karts[kart_id]->getKartModel() ->getWheelGraphicsPosition(i).toIrrVector(); m_tire_offsets[i].rotateXZBy(-m_tire_rotation / M_PI * 180 + 180); m_tire_radius[i] = m_karts[kart_id]->getKartModel() ->getWheelGraphicsRadius(i); } m_tire_dir = m_karts[kart_id]->getKartProperties()->getKartDir(); if(m_insert_tire == 5 && m_karts[kart_id]->isWheeless()) m_insert_tire = 0; } // kartHit