/** A kart has finished the race at the specified time (which can be * different from World::getWorld()->getClock() in case of setting * extrapolated arrival times). This function is only called from * kart::finishedRace() * \param kart The kart that finished the race. * \param time Time at which the kart finished the race. */ void RaceManager::kartFinishedRace(const AbstractKart *kart, float time) { unsigned int id = kart->getWorldKartId(); int pos = kart->getPosition(); assert(pos-1 >= 0); assert(pos-1 < (int)m_kart_status.size()); m_kart_status[id].m_last_score = m_kart_status[id].m_score; // In follow the leader mode, the winner is actually the kart with // position 2, so adjust the points (#points for leader do not matter) WorldWithRank *wwr = dynamic_cast<WorldWithRank*>(World::getWorld()); if (wwr) m_kart_status[id].m_score += wwr->getScoreForPosition(pos); else { Log::error("RaceManager", "World with scores that is not a WorldWithRank??"); } m_kart_status[id].m_overall_time += time; m_kart_status[id].m_last_time = time; m_num_finished_karts ++; if(kart->getController()->isPlayerController()) m_num_finished_players++; } // kartFinishedRace
/** Displays the rank and the lap of the kart. * \param info Info object c */ void RaceGUI::drawRankLap(const KartIconDisplayInfo* info, const AbstractKart* kart, const core::recti &viewport) { // Don't display laps or ranks if the kart has already finished the race. if (kart->hasFinishedRace()) return; core::recti pos; pos.UpperLeftCorner.Y = viewport.UpperLeftCorner.Y; // If the time display in the top right is in this viewport, // move the lap/rank display down a little bit so that it is // displayed under the time. if(viewport.UpperLeftCorner.Y==0 && viewport.LowerRightCorner.X==UserConfigParams::m_width && race_manager->getNumPlayers()!=3) pos.UpperLeftCorner.Y += 40; pos.LowerRightCorner.Y = viewport.LowerRightCorner.Y; pos.UpperLeftCorner.X = viewport.LowerRightCorner.X - m_rank_lap_width - 10; pos.LowerRightCorner.X = viewport.LowerRightCorner.X; gui::ScalableFont* font = (race_manager->getNumLocalPlayers() > 2 ? GUIEngine::getSmallFont() : GUIEngine::getFont()); int font_height = (int)(font->getDimension(L"X").Height); static video::SColor color = video::SColor(255, 255, 255, 255); WorldWithRank *world = (WorldWithRank*)(World::getWorld()); if (world->displayRank()) { const int rank = kart->getPosition(); font->draw(m_string_rank.c_str(), pos, color); pos.UpperLeftCorner.Y += font_height; pos.LowerRightCorner.Y += font_height; char str[256]; const unsigned int kart_amount = world->getCurrentNumKarts(); sprintf(str, "%d/%d", rank, kart_amount); font->draw(core::stringw(str).c_str(), pos, color); pos.UpperLeftCorner.Y += font_height; pos.LowerRightCorner.Y += font_height; } // Don't display laps in follow the leader mode if(world->raceHasLaps()) { const int lap = info[kart->getWorldKartId()].lap; // don't display 'lap 0/...' if(lap>=0) { font->draw(m_string_lap.c_str(), pos, color); char str[256]; sprintf(str, "%d/%d", lap+1, race_manager->getNumLaps()); pos.UpperLeftCorner.Y += font_height; pos.LowerRightCorner.Y += font_height; font->draw(core::stringw(str).c_str(), pos, color); pos.UpperLeftCorner.Y += font_height; pos.LowerRightCorner.Y += font_height; } } } // drawRankLap
/** Draws the rank of a player. * \param kart The kart of the player. * \param offset Offset of top left corner for this display (for splitscreen). * \param min_ratio Scaling of the screen (for splitscreen). * \param meter_width Width of the meter (inside which the rank is shown). * \param meter_height Height of the meter (inside which the rank is shown). * \param dt Time step size. */ void RaceGUI::drawRank(const AbstractKart *kart, const core::vector2df &offset, float min_ratio, int meter_width, int meter_height, float dt) { // Draw rank WorldWithRank *world = dynamic_cast<WorldWithRank*>(World::getWorld()); if (!world || !world->displayRank()) return; int id = kart->getWorldKartId(); if (m_animation_states[id] == AS_NONE) { if (m_last_ranks[id] != kart->getPosition()) { m_rank_animation_duration[id] = 0.0f; m_animation_states[id] = AS_SMALLER; } } else { m_rank_animation_duration[id] += dt; } float scale = 1.0f; int rank = kart->getPosition(); const float DURATION = 0.4f; const float MIN_SHRINK = 0.3f; if (m_animation_states[id] == AS_SMALLER) { scale = 1.0f - m_rank_animation_duration[id]/ DURATION; rank = m_last_ranks[id]; if (scale < MIN_SHRINK) { m_animation_states[id] = AS_BIGGER; m_rank_animation_duration[id] = 0.0f; // Store the new rank m_last_ranks[id] = kart->getPosition(); scale = MIN_SHRINK; } } else if (m_animation_states[id] == AS_BIGGER) { scale = m_rank_animation_duration[id] / DURATION + MIN_SHRINK; rank = m_last_ranks[id]; if (scale > 1.0f) { m_animation_states[id] = AS_NONE; scale = 1.0f; } } else { m_last_ranks[id] = kart->getPosition(); } gui::ScalableFont* font = GUIEngine::getHighresDigitFont(); font->setScale(min_ratio * scale); font->setShadow(video::SColor(255, 128, 0, 0)); std::ostringstream oss; oss << rank; // the current font has no . :( << "."; core::recti pos; pos.LowerRightCorner = core::vector2di(int(offset.X + 0.65f*meter_width), int(offset.Y - 0.55f*meter_height)); pos.UpperLeftCorner = core::vector2di(int(offset.X + 0.65f*meter_width), int(offset.Y - 0.55f*meter_height)); static video::SColor color = video::SColor(255, 255, 255, 255); font->draw(oss.str().c_str(), pos, color, true, true); font->setScale(1.0f); } // drawRank
/** Displays the rank and the lap of the kart. * \param info Info object c */ void MinimalRaceGUI::drawRankLap(const KartIconDisplayInfo* info, const AbstractKart* kart, const core::recti &viewport) { // Don't display laps or ranks if the kart has already finished the race. if (kart->hasFinishedRace()) return; core::recti pos; gui::ScalableFont* font = (race_manager->getNumLocalPlayers() > 2 ? GUIEngine::getSmallFont() : GUIEngine::getFont()); float scale = font->getScale(); font->setScale(m_font_scale); // Add a black shadow to make the text better readable on // 'white' tracks (e.g. with snow and ice). font->setShadow(video::SColor(255, 0, 0, 0)); static video::SColor color = video::SColor(255, 255, 255, 255); WorldWithRank *world = (WorldWithRank*)(World::getWorld()); if (world->displayRank()) { pos.UpperLeftCorner.Y = viewport.UpperLeftCorner.Y; pos.LowerRightCorner.Y = viewport.UpperLeftCorner.Y+50; // Split screen 3 or 4 players, left side: if(viewport.LowerRightCorner.X < UserConfigParams::m_width) { pos.UpperLeftCorner.X = 10; pos.LowerRightCorner.X = viewport.LowerRightCorner.X; } else { pos.UpperLeftCorner.X = viewport.LowerRightCorner.X - m_rank_width-10; pos.LowerRightCorner.X = viewport.LowerRightCorner.X; } char str[256]; sprintf(str, "%d/%d", kart->getPosition(), world->getCurrentNumKarts()); font->draw(str, pos, color); } // Don't display laps in follow the leader mode if(world->raceHasLaps()) { const int lap = info[kart->getWorldKartId()].lap; // don't display 'lap 0/...' if(lap>=0) { pos.LowerRightCorner.Y = viewport.LowerRightCorner.Y; pos.UpperLeftCorner.Y = viewport.LowerRightCorner.Y-60; pos.LowerRightCorner.X = viewport.LowerRightCorner.X; // Split screen 3 or 4 players, left side: if(viewport.LowerRightCorner.X < UserConfigParams::m_width) { pos.UpperLeftCorner.X = 10; } else { pos.UpperLeftCorner.X = (int)(viewport.LowerRightCorner.X - m_lap_width -10 ); } char str[256]; sprintf(str, "%d/%d", lap+1, race_manager->getNumLaps()); core::stringw s = m_string_lap+" "+str; font->draw(s.c_str(), pos, color); } } font->setScale(scale); font->disableShadow(); } // drawRankLap
/** Draw players icons and their times (if defined in the current mode). * Also takes care of icon looking different due to plumber, squashing, ... */ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) { // For now, don't draw player icons when in soccer mode const RaceManager::MinorRaceModeType minor_mode = race_manager->getMinorMode(); if(minor_mode == RaceManager::MINOR_MODE_SOCCER) return; int x_base = 10; int y_base = 20; unsigned int y_space = UserConfigParams::m_height - bottom_margin - y_base; // Special case : when 3 players play, use 4th window to display such stuff if (race_manager->getNumLocalPlayers() == 3) { x_base = UserConfigParams::m_width/2 + x_base; y_base = UserConfigParams::m_height/2 + y_base; y_space = UserConfigParams::m_height - y_base; } // -2 because that's the spacing further on int ICON_PLAYER_WIDTH = y_space / race_manager->getNumberOfKarts() - 2; int icon_width_max = (int)(50*(UserConfigParams::m_width/800.0f)); int icon_width_min = (int)(35*(UserConfigParams::m_height/600.0f)); if (icon_width_min > icon_width_max) { int icon_width_tmp = icon_width_max; icon_width_max = icon_width_min; icon_width_min = icon_width_tmp; } // Make sure it fits within our boundaries if (ICON_PLAYER_WIDTH > icon_width_max) ICON_PLAYER_WIDTH = icon_width_max; if (ICON_PLAYER_WIDTH < icon_width_min) ICON_PLAYER_WIDTH = icon_width_min; // TODO: Is this absolute treshold necessary? if(UserConfigParams::m_height<600) { ICON_PLAYER_WIDTH = 35; } // Icon width for the AI karts int ICON_WIDTH = ICON_PLAYER_WIDTH * 4 / 5; WorldWithRank *world = (WorldWithRank*)(World::getWorld()); //initialize m_previous_icons_position if(m_previous_icons_position.size()==0) { for(unsigned int i=0; i<race_manager->getNumberOfKarts(); i++) { const AbstractKart *kart = world->getKart(i); int position = kart->getPosition(); core::vector2d<s32> pos(x_base,y_base+(position-1)*(ICON_PLAYER_WIDTH+2)); m_previous_icons_position.push_back(pos); } } int x; int y; float previous_distance=0.0;//no need to be far ahead, first kart won't try to overlap int previous_x=x_base; int previous_y=y_base-ICON_PLAYER_WIDTH-2; gui::ScalableFont* font = GUIEngine::getFont(); const unsigned int kart_amount = world->getNumKarts(); //where is the limit to hide last icons int y_icons_limit=UserConfigParams::m_height-bottom_margin-ICON_PLAYER_WIDTH; if (race_manager->getNumLocalPlayers() == 3) y_icons_limit=UserConfigParams::m_height-ICON_WIDTH; world->getKartsDisplayInfo(&m_kart_display_infos); for(int position = 1; position <= (int)kart_amount ; position++) { AbstractKart *kart = world->getKartAtPosition(position); if (kart->getPosition() == -1)//if position is not set { //we use karts ordered by id only //(needed for beginning of MINOR_MODE_3_STRIKES) kart= world->getKart(position-1); } if(kart->isEliminated()) continue; unsigned int kart_id = kart->getWorldKartId(); KartIconDisplayInfo &info = m_kart_display_infos[kart_id]; //x,y is the target position int lap = info.lap; // In battle mode mode there is no distance along track etc. if( minor_mode==RaceManager::MINOR_MODE_3_STRIKES || minor_mode==RaceManager::MINOR_MODE_EASTER_EGG) { x = x_base; y = previous_y+ICON_PLAYER_WIDTH+2; } else { LinearWorld *linear_world = (LinearWorld*)(World::getWorld()); float distance = linear_world->getDistanceDownTrackForKart(kart_id) + linear_world->getTrack()->getTrackLength()*lap; if ((position>1) && (previous_distance-distance<m_dist_show_overlap) && (!kart->hasFinishedRace()) ) { //linear translation : form (0,ICON_PLAYER_WIDTH+2) to // (previous_x-x_base+(ICON_PLAYER_WIDTH+2)/2,0) x=(int)(x_base+(1-(previous_distance-distance) /m_dist_show_overlap) *(previous_x-x_base+(ICON_PLAYER_WIDTH+2)/2)); y=(int)(previous_y+(previous_distance-distance) /m_dist_show_overlap*(ICON_PLAYER_WIDTH+2)); } else { x=x_base; y=previous_y+ICON_PLAYER_WIDTH+2; } previous_distance=distance; } // not three-strike-battle previous_x=x;//save coord of the previous kart in list previous_y=y; //soft movement using previous position: x=(int)((x+m_previous_icons_position[kart_id].X*m_icons_inertia) /(m_icons_inertia+1)); y=(int)((y+m_previous_icons_position[kart_id].Y*m_icons_inertia) /(m_icons_inertia+1)); //save position for next time m_previous_icons_position[kart_id].X=x; m_previous_icons_position[kart_id].Y=y; if (y>y_icons_limit) { //there are too many icons, write "Top 9", to express that //there is not everybody shown core::recti pos_top; pos_top.UpperLeftCorner.Y = y_base-22; pos_top.UpperLeftCorner.X = x_base; static video::SColor color = video::SColor(255, 255, 255, 255); pos_top.LowerRightCorner = pos_top.UpperLeftCorner; font->draw(StringUtils::insertValues( m_string_top, position-1 ), pos_top, color); break; } if (m_kart_display_infos[kart_id].m_text.size() > 0) { core::rect<s32> pos(x+ICON_PLAYER_WIDTH, y+5, x+ICON_PLAYER_WIDTH, y+5); core::stringw s=info.m_text.c_str(); font->draw(s.c_str(), pos, info.m_color, false, false, NULL, true /* ignore RTL */); } if (info.special_title.size() > 0) { core::rect<s32> pos(x+ICON_PLAYER_WIDTH, y+5, x+ICON_PLAYER_WIDTH, y+5); core::stringw s(info.special_title.c_str()); font->draw(s.c_str(), pos, info.m_color, false, false, NULL, true /* ignore RTL */); } // draw icon video::ITexture *icon = kart->getKartProperties()->getIconMaterial()->getTexture(); int w = kart->getController()->isPlayerController() ? ICON_PLAYER_WIDTH : ICON_WIDTH; const core::rect<s32> pos(x, y, x+w, y+w); //to bring to light the player's icon: add a background if (kart->getController()->isPlayerController()) { video::SColor colors[4]; for (unsigned int i=0;i<4;i++) { colors[i]=kart->getKartProperties()->getColor(); colors[i].setAlpha( 100+(int)(100*cos(M_PI/2*i+World::getWorld()->getTime()*2))); } const core::rect<s32> rect(core::position2d<s32>(0,0), m_icons_frame->getTexture()->getOriginalSize()); draw2DImage( m_icons_frame->getTexture(), pos, rect,NULL, colors, true); } // Fixes crash bug, why are certain icons not showing up? if (icon && !kart->getKartAnimation() && !kart->isSquashed()) { const core::rect<s32> rect(core::position2d<s32>(0,0), icon->getOriginalSize()); draw2DImage(icon, pos, rect, NULL, NULL, true); } //draw status info - icon fade out in case of rescue/explode if (icon && dynamic_cast<RescueAnimation*>(kart->getKartAnimation())) { //icon fades to the left float t = kart->getKartAnimation()->getAnimationTimer(); float t_anim=100*sin(0.5f*M_PI*t); const core::rect<s32> rect1(core::position2d<s32>(0,0), icon->getOriginalSize()); const core::rect<s32> pos1((int)(x-t_anim), y, (int)(x+w-t_anim), y+w); draw2DImage(icon, pos1, rect1, NULL, NULL, true); } if (icon && !kart->getKartAnimation() && kart->isSquashed() ) { //syncs icon squash with kart squash const core::rect<s32> destRect(core::position2d<s32>(x,y+w/4), core::position2d<s32>(x+w,y+w*3/4)); const core::rect<s32> sourceRect(core::position2d<s32>(0,0), icon->getOriginalSize()); draw2DImage(icon, destRect, sourceRect, NULL, NULL, true); } if (icon && dynamic_cast<ExplosionAnimation*>(kart->getKartAnimation()) ) { //exploses into 4 parts float t = kart->getKartAnimation()->getAnimationTimer(); float t_anim=50.0f*sin(0.5f*M_PI*t); u16 icon_size_x=icon->getOriginalSize().Width; u16 icon_size_y=icon->getOriginalSize().Height; const core::rect<s32> rect1(0, 0, icon_size_x/2,icon_size_y/2); const core::rect<s32> pos1((int)(x-t_anim), (int)(y-t_anim), (int)(x+w/2-t_anim), (int)(y+w/2-t_anim)); draw2DImage(icon, pos1, rect1, NULL, NULL, true); const core::rect<s32> rect2(icon_size_x/2,0, icon_size_x,icon_size_y/2); const core::rect<s32> pos2((int)(x+w/2+t_anim), (int)(y-t_anim), (int)(x+w+t_anim), (int)(y+w/2-t_anim)); draw2DImage(icon, pos2, rect2, NULL, NULL, true); const core::rect<s32> rect3(0, icon_size_y/2, icon_size_x/2,icon_size_y); const core::rect<s32> pos3((int)(x-t_anim), (int)(y+w/2+t_anim), (int)(x+w/2-t_anim), (int)(y+w+t_anim)); draw2DImage(icon, pos3, rect3, NULL, NULL, true); const core::rect<s32> rect4(icon_size_x/2,icon_size_y/2,icon_size_x,icon_size_y); const core::rect<s32> pos4((int)(x+w/2+t_anim), (int)(y+w/2+t_anim), (int)(x+w+t_anim), (int)(y+w+t_anim)); draw2DImage(icon, pos4, rect4, NULL, NULL, true); } //Plunger if (kart->getBlockedByPlungerTime()>0) { video::ITexture *icon_plunger = powerup_manager->getIcon(PowerupManager::POWERUP_PLUNGER)->getTexture(); if (icon_plunger != NULL) { const core::rect<s32> rect(core::position2d<s32>(0,0), icon_plunger->getOriginalSize()); const core::rect<s32> pos1(x+10, y-10, x+w+10, y+w-10); draw2DImage(icon_plunger, pos1, rect, NULL, NULL, true); } } //attachment if (kart->getAttachment()->getType() != Attachment::ATTACH_NOTHING) { video::ITexture *icon_attachment = attachment_manager->getIcon(kart->getAttachment()->getType()) ->getTexture(); if (icon_attachment != NULL) { const core::rect<s32> rect(core::position2d<s32>(0,0), icon_attachment->getOriginalSize()); const core::rect<s32> pos1(x-20, y-10, x+w-20, y+w-10); draw2DImage(icon_attachment, pos1, rect, NULL, NULL, true); } } } //next position } // drawGlobalPlayerIcons