/** Sets the kart position and controls to the recorded history value. * \param dt Time step size. */ void History::updateReplay(float dt) { m_current++; if(m_current>=(int)m_all_deltas.size()) { printf("Replay finished.\n"); exit(2); } World *world = World::getWorld(); unsigned int num_karts = world->getNumKarts(); for(unsigned k=0; k<num_karts; k++) { Kart *kart = world->getKart(k); unsigned int index=m_current*num_karts+k; if(m_replay_mode==HISTORY_POSITION) { kart->setXYZ(m_all_xyz[index]); kart->setRotation(m_all_rotations[index]); } else { kart->setControls(m_all_controls[index]); } } } // updateReplay
/** Determine the nearest kart or item and update the current target * accordingly. */ void Swatter::chooseTarget() { // TODO: for the moment, only handle karts... const World *world = World::getWorld(); Kart *closest_kart = NULL; float min_dist2 = FLT_MAX; for(unsigned int i=0; i<world->getNumKarts(); i++) { Kart *kart = world->getKart(i); // TODO: isSwatterReady(), isSquashable()? if(kart->isEliminated() || kart==m_kart) continue; // don't squash an already hurt kart if (kart->isInvulnerable() || kart->isSquashed()) continue; float dist2 = (kart->getXYZ()-m_kart->getXYZ()).length2(); if(dist2<min_dist2) { min_dist2 = dist2; closest_kart = kart; } } m_target = closest_kart; // may be NULL }
/** Actually sets the camera based on the given parameter. * \param above_kart How far above the camera should aim at. * \param cam_angle Angle above the kart plane for the camera. * \param sideway Sideway movement of the camera. * \param distance Distance from kart. */ void Camera::positionCamera(float dt, float above_kart, float cam_angle, float side_way, float distance, float smoothing) { Vec3 wanted_position; Vec3 wanted_target = m_kart->getXYZ(); if(UserConfigParams::m_camera_debug==2) wanted_target.setY(m_kart->getVehicle()->getWheelInfo(2).m_raycastInfo.m_contactPointWS.getY()); else wanted_target.setY(wanted_target.getY()+above_kart); float tan_up = tan(cam_angle); Vec3 relative_position(side_way, fabsf(distance)*tan_up+above_kart, distance); btTransform t=m_kart->getTrans(); if(stk_config->m_camera_follow_skid && m_kart->getSkidding()->getVisualSkidRotation()!=0) { // If the camera should follow the graphical skid, add the // visual rotation to the relative vector: btQuaternion q(m_kart->getSkidding()->getVisualSkidRotation(), 0, 0); t.setBasis(t.getBasis() * btMatrix3x3(q)); } wanted_position = t(relative_position); if (smoothing) { smoothMoveCamera(dt, wanted_position, wanted_target); } else { if (m_mode!=CM_FALLING) m_camera->setPosition(wanted_position.toIrrVector()); m_camera->setTarget(wanted_target.toIrrVector()); if (race_manager->getNumLocalPlayers() < 2) { sfx_manager->positionListener(m_camera->getPosition(), wanted_target - m_camera->getPosition()); } } Kart *kart = dynamic_cast<Kart*>(m_kart); if (kart && !kart->isFlying()) { // Rotate the up vector (0,1,0) by the rotation ... which is just column 1 Vec3 up = m_kart->getTrans().getBasis().getColumn(1); float f = 0.04f; // weight for new up vector to reduce shaking f = 0; m_camera->setUpVector(f * up.toIrrVector() + (1.0f - f) * m_camera->getUpVector()); } // kart && !flying else m_camera->setUpVector(core::vector3df(0, 1, 0)); } // positionCamera
/** Activates the next debug mode, or switches the mode off again. */ void IrrDebugDrawer::nextDebugMode() { // Go to next debug mode. Note that debug mode 3 ( m_debug_mode = (DebugModeType) ((m_debug_mode+1) % 3); World *world = World::getWorld(); unsigned int num_karts = world->getNumKarts(); for(unsigned int i=0; i<num_karts; i++) { Kart *kart = world->getKart(i); if(kart->isEliminated()) continue; kart->getNode()->setVisible(!(m_debug_mode & DM_NO_KARTS_GRAPHICS)); } } // nextDebugMode
/** Actually sets the camera based on the given parameter. * \param above_kart How far above the camera should aim at. * \param cam_angle Angle above the kart plane for the camera. * \param sideway Sideway movement of the camera. * \param distance Distance from kart. */ void CameraDebug::positionCamera(float dt, float above_kart, float cam_angle, float side_way, float distance ) { Vec3 wanted_position; Vec3 wanted_target = m_kart->getXYZ(); if(m_default_debug_Type==CM_DEBUG_GROUND) { const btWheelInfo &w = m_kart->getVehicle()->getWheelInfo(2); wanted_target.setY(w.m_raycastInfo.m_contactPointWS.getY()); } else wanted_target.setY(wanted_target.getY()+above_kart); float tan_up = tan(cam_angle); Vec3 relative_position(side_way, fabsf(distance)*tan_up+above_kart, distance); btTransform t=m_kart->getTrans(); if(stk_config->m_camera_follow_skid && m_kart->getSkidding()->getVisualSkidRotation()!=0) { // If the camera should follow the graphical skid, add the // visual rotation to the relative vector: btQuaternion q(m_kart->getSkidding()->getVisualSkidRotation(), 0, 0); t.setBasis(t.getBasis() * btMatrix3x3(q)); } if (m_default_debug_Type == CM_DEBUG_GROUND) { wanted_position = t(relative_position); // Make sure that the Y position is a the same height as the wheel. wanted_position.setY(wanted_target.getY()); } else wanted_position = t(relative_position); if (getMode() != CM_FALLING) m_camera->setPosition(wanted_position.toIrrVector()); m_camera->setTarget(wanted_target.toIrrVector()); Kart *kart = dynamic_cast<Kart*>(m_kart); if (kart && !kart->isFlying()) { // Rotate the up vector (0,1,0) by the rotation ... which is just column 1 Vec3 up = m_kart->getTrans().getBasis().getColumn(1); float f = 0.04f; // weight for new up vector to reduce shaking m_camera->setUpVector( f * up.toIrrVector() + (1.0f - f) * m_camera->getUpVector()); } // kart && !flying else m_camera->setUpVector(core::vector3df(0, 1, 0)); } // positionCamera
// ---------------------------------------------------------------------------- KartUpdateMessage::KartUpdateMessage(ENetPacket* pkt) : Message(pkt, MT_KART_INFO) { World *world = World::getWorld(); unsigned int num_karts = getInt(); for(unsigned int i=0; i<num_karts; i++) { // Currently not used KartControl kc(this); Vec3 xyz = getVec3(); btQuaternion q = getQuaternion(); Kart *kart = world->getKart(i); kart->setXYZ(xyz); kart->setRotation(q); } // for i }; // KartUpdateMessage
/** Checks if the kart was overtaken, and if so plays a sound */ void PlayerController::setPosition(int p) { if(m_kart->getPosition()<p) { World *world = World::getWorld(); //have the kart that did the passing beep. //I'm not sure if this method of finding the passing kart is fail-safe. for(unsigned int i = 0 ; i < world->getNumKarts(); i++ ) { Kart *kart = world->getKart(i); if(kart->getPosition() == p + 1) { kart->beep(); break; } } } } // setPosition
/** Squash karts or items that are around the end position (determined using * a joint) of the swatter. */ void Swatter::squashThingsAround() { const KartProperties* kp = m_kart->getKartProperties(); // Square of the minimum distance float min_dist2 = kp->getSwatterDistance2(); const World* world = World::getWorld(); // Get the node corresponding to the joint at the center of the swatter // (by swatter, I mean the thing hold in the hand, not the whole thing) scene::ISceneNode* swatter_node = m_scene_node->getJointNode("Swatter"); assert(swatter_node); Vec3 swatter_pos = swatter_node->getAbsolutePosition(); m_swat_sound->position(swatter_pos); m_swat_sound->play(); // Squash karts around for(unsigned int i=0; i<world->getNumKarts(); i++) { Kart *kart = world->getKart(i); // TODO: isSwatterReady() if(kart->isEliminated() || kart==m_kart) continue; // don't swat an already hurt kart if (kart->isInvulnerable() || kart->isSquashed()) continue; float dist2 = (kart->getXYZ()-swatter_pos).length2(); if(dist2 >= min_dist2) continue; // too far away, ignore this kart kart->setSquash(kp->getSquashDuration(), kp->getSquashSlowdown()); if (kart->getAttachment()->getType()==Attachment::ATTACH_BOMB) { // make bomb explode kart->getAttachment()->update(10000); HitEffect *he = new Explosion(m_kart->getXYZ(), "explosion"); if(m_kart->getController()->isPlayerController()) he->setPlayerKartHit(); projectile_manager->addHitEffect(he); m_kart->handleExplosion(m_kart->getXYZ(), /*direct_hit*/ true); } // if kart has bomb attached World::getWorld()->kartHit(kart->getWorldKartId()); } // for i < num_kartrs // TODO: squash items }
/** Called at the end of a race. Checks if the current times are worth a new * score, if so it notifies the HighscoreManager so the new score is added * and saved. */ void World::updateHighscores(int* best_highscore_rank, int* best_finish_time, std::string* highscore_who, StateManager::ActivePlayer** best_player) { *best_highscore_rank = -1; *best_player = NULL; if(!m_use_highscores) return; // Add times to highscore list. First compute the order of karts, // so that the timing of the fastest kart is added first (otherwise // someone might get into the highscore list, only to be kicked out // again by a faster kart in the same race), which might be confusing // if we ever decide to display a message (e.g. during a race) unsigned int *index = new unsigned int[m_karts.size()]; const unsigned int kart_amount = (unsigned int) m_karts.size(); for (unsigned int i=0; i<kart_amount; i++ ) { index[i] = 999; // first reset the contents of the array } for (unsigned int i=0; i<kart_amount; i++ ) { const int pos = m_karts[i]->getPosition()-1; if(pos < 0 || pos >= (int)kart_amount) continue; // wrong position index[pos] = i; } for (unsigned int pos=0; pos<kart_amount; pos++) { if(index[pos] == 999) { // no kart claimed to be in this position, most likely means // the kart location data is wrong #ifdef DEBUG Log::error("[World]", "Incorrect kart positions:"); for (unsigned int i=0; i<m_karts.size(); i++ ) { Log::error("[World]", "i=%d position %d.",i, m_karts[i]->getPosition()); } #endif continue; } // Only record times for player karts and only if // they finished the race if(!m_karts[index[pos]]->getController()->isPlayerController()) continue; if (!m_karts[index[pos]]->hasFinishedRace()) continue; assert(index[pos] < m_karts.size()); Kart *k = (Kart*)m_karts[index[pos]]; Highscores* highscores = getHighscores(); PlayerController *controller = (PlayerController*)(k->getController()); int highscore_rank = 0; if (controller->getPlayer()->getProfile() != NULL) // if we have the player profile here highscore_rank = highscores->addData(k->getIdent(), controller->getPlayer()->getProfile()->getName(), k->getFinishTime()); if (highscore_rank > 0) { if (*best_highscore_rank == -1 || highscore_rank < *best_highscore_rank) { *best_highscore_rank = highscore_rank; *best_finish_time = (int)(k->getFinishTime()); *best_player = controller->getPlayer(); *highscore_who = k->getIdent(); } highscore_manager->saveHighscores(); } } // next position delete []index; } // updateHighscores
/** Moves the camera smoothly from the current camera position (and target) * to the new position and target. * \param wanted_position The position the camera wanted to reach. * \param wanted_target The point the camera wants to point to. */ void Camera::smoothMoveCamera(float dt) { Kart *kart = dynamic_cast<Kart*>(m_kart); if (kart->isFlying()) { Vec3 vec3 = m_kart->getXYZ() + Vec3(sin(m_kart->getHeading()) * -4.0f, 0.5f, cos(m_kart->getHeading()) * -4.0f); m_camera->setTarget(m_kart->getXYZ().toIrrVector()); m_camera->setPosition(vec3.toIrrVector()); return; } core::vector3df current_position = m_camera->getPosition(); // Smoothly interpolate towards the position and target const KartProperties *kp = m_kart->getKartProperties(); float max_increase_with_zipper = kp->getZipperMaxSpeedIncrease(); float max_speed_without_zipper = kp->getMaxSpeed(); float current_speed = m_kart->getSpeed(); const Skidding *ks = m_kart->getSkidding(); float skid_factor = ks->getVisualSkidRotation(); float skid_angle = asin(skid_factor); float ratio = (current_speed - max_speed_without_zipper) / max_increase_with_zipper; ratio = ratio > -0.12f ? ratio : -0.12f; float camera_distance = -3 * (0.5f + ratio);// distance of camera from kart in x and z plane if (camera_distance > -2.0f) camera_distance = -2.0f; Vec3 camera_offset(camera_distance * sin(skid_angle / 2), 1.1f * (1 + ratio / 2), camera_distance * cos(skid_angle / 2));// defines how far camera should be from player kart. Vec3 m_kart_camera_position_with_offset = m_kart->getTrans()(camera_offset); core::vector3df current_target = m_kart->getXYZ().toIrrVector();// next target current_target.Y += 0.5f; core::vector3df wanted_position = m_kart_camera_position_with_offset.toIrrVector();// new required position of camera if ((m_kart->getSpeed() > 5 ) || (m_kart->getSpeed() < 0 )) { current_position += ((wanted_position - current_position) * dt * (m_kart->getSpeed()>0 ? m_kart->getSpeed()/3 + 1.0f : -1.5f * m_kart->getSpeed() + 2.0f)); } else { current_position += (wanted_position - current_position) * dt * 5; } if(m_mode!=CM_FALLING) m_camera->setPosition(current_position); m_camera->setTarget(current_target);//set new target assert(!isnan(m_camera->getPosition().X)); assert(!isnan(m_camera->getPosition().Y)); assert(!isnan(m_camera->getPosition().Z)); if (race_manager->getNumLocalPlayers() < 2) { sfx_manager->positionListener(current_position, current_target - current_position); } } // smoothMoveCamera
/** Find the position (rank) of every kart */ void LinearWorld::updateRacePosition() { // Mostly for debugging: beginSetKartPositions(); const unsigned int kart_amount = m_karts.size(); #ifdef DEBUG bool rank_changed = false; #endif // NOTE: if you do any changes to this loop, the next loop (see // DEBUG_KART_RANK below) needs to have the same changes applied // so that debug output is still correct!!!!!!!!!!! for (unsigned int i=0; i<kart_amount; i++) { Kart* kart = m_karts[i]; // Karts that are either eliminated or have finished the // race already have their (final) position assigned. If // these karts would get their rank updated, it could happen // that a kart that finished first will be overtaken after // crossing the finishing line and become second! if(kart->isEliminated() || kart->hasFinishedRace()) { // This is only necessary to support debugging inconsistencies // in kart position parameters. setKartPosition(i, kart->getPosition()); continue; } KartInfo& kart_info = m_kart_info[i]; int p = 1 ; const int my_id = kart->getWorldKartId(); const int my_laps = getLapForKart(my_id); const float my_progression = getDistanceDownTrackForKart(my_id); // Count karts ahead of the current kart, i.e. kart that are already finished, // have done more laps, or the same number of laps, but a greater distance. for (unsigned int j = 0 ; j < kart_amount ; j++) { if(j == kart->getWorldKartId()) continue; // don't compare a kart with itself if(m_karts[j]->isEliminated()) continue; // dismiss eliminated karts if(!kart->hasFinishedRace() && m_karts[j]->hasFinishedRace()) { p++; continue; } /* has done more or less lapses */ assert(j==m_karts[j]->getWorldKartId()); int other_laps = getLapForKart(j); if (other_laps != my_laps) { if(other_laps > my_laps) { p++; // Other kart has more lapses } continue; } // Now both karts have the same number of lapses. Test progression. // A kart is ahead if it's driven further, or driven the same // distance, but started further to the back. float other_progression = getDistanceDownTrackForKart(j); if(other_progression > my_progression || (other_progression == my_progression && m_karts[j]->getInitialPosition() > kart->getInitialPosition()) ) { p++; #if _DEBUG_PRINTS_LIKE_MAD_ std::cout << " " << p << " : " << m_karts[j]->getIdent() << " because he has is further within the track (my progression is " << my_progression << ", his progression is " << other_progression << ")\n"; #endif } } //next kart #ifndef DEBUG setKartPosition(i, p); #else rank_changed |= kart->getPosition()!=p; if (!setKartPosition(i,p)) { std::cerr << "ERROR, same rank used twice!!\n"; std::cerr << "Info used to decide ranking :\n"; for (unsigned int d=0; d<kart_amount; d++) { std::cerr << " kart " << m_karts[d]->getIdent() << " has finished(" << m_karts[d]->hasFinishedRace() << "), is at lap (" << getLapForKart(d) << "), is at distance(" << getDistanceDownTrackForKart(d) << "), is eliminated(" << m_karts[d]->isEliminated() << ")" << std::endl; } std::cerr << "Who has each ranking so far :\n"; for (unsigned int d=0; d<i; d++) { std::cerr << " " << m_karts[d]->getIdent() << " has rank " << m_karts[d]->getPosition() << std::endl; } std::cerr << " --> And " << kart->getIdent() << " is being set at rank " << p << std::endl; history->Save(); assert(false); } #endif // Switch on faster music if not already done so, if the // first kart is doing its last lap, and if the estimated // remaining time is less than 30 seconds. if(!m_faster_music_active && kart_info.m_race_lap == race_manager->getNumLaps()-1 && p==1 && useFastMusicNearEnd() && kart_info.m_estimated_finish > 0 && kart_info.m_estimated_finish - getTime() < 30.0f ) { music_manager->switchToFastMusic(); m_faster_music_active=true; } } // for i<kart_amount // Define this to get a detailled analyses each time a race position // changes. #ifdef DEBUG #undef DEBUG_KART_RANK #ifdef DEBUG_KART_RANK if(rank_changed) { std::cout << "Counting laps at "<<getTime()<<" seconds.\n"; for (unsigned int i=0; i<kart_amount; i++) { Kart* kart = m_karts[i]; if(kart->isEliminated() || kart->hasFinishedRace()) continue; KartInfo& kart_info = m_kart_info[i]; int p = 1 ; const int my_id = kart->getWorldKartId(); const int my_laps = getLapForKart(my_id); const float my_progression = getDistanceDownTrackForKart(my_id); std::cout << "counting karts ahead of " << kart->getIdent() << " (laps "<<m_kart_info[i].m_race_lap<<", progress " << my_progression<<").\n"; for (unsigned int j = 0 ; j < kart_amount ; j++) { if(j == kart->getWorldKartId()) continue; // don't compare a kart with itself if(!kart->hasFinishedRace() && m_karts[j]->hasFinishedRace()) { p++; std::cout << " " << p << " : " << m_karts[j]->getIdent() << " because he has finished.\n"; continue; } int other_laps = getLapForKart(j); if (other_laps != my_laps) { if(other_laps > my_laps) { p++; // Other kart has more lapses std::cout << " " << p << " : " << m_karts[j]->getIdent() << " because he has more laps than me.\n"; } continue; } float other_progression = getDistanceDownTrackForKart(j); if(other_progression > my_progression || (other_progression == my_progression && m_karts[j]->getInitialPosition() > kart->getInitialPosition()) ) { p++; std::cout << " " << p << " : " << m_karts[j]->getIdent() << " because he is further within the track (my progression is " << my_progression << ", his progression is " << other_progression << ")\n"; } } //next kart } // for i<kart_amount std::cout << "-------------------------------------------\n"; } // if rank_changed #endif #endif endSetKartPositions(); } // updateRacePosition
//----------------------------------------------------------------------------- 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
/** 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