/** Draws the powerup icons on the screen (called once for each player). * \param kart The kart for which to draw the powerup icons. * \param viewport The viewport into which to draw the icons. * \param scaling The scaling to use when draing the icons. */ void RaceGUIBase::drawPowerupIcons(const AbstractKart* kart, const core::recti &viewport, const core::vector2df &scaling) { // If player doesn't have any powerups or has completed race, do nothing. const Powerup* powerup = kart->getPowerup(); if (powerup->getType() == PowerupManager::POWERUP_NOTHING || kart->hasFinishedRace()) return; int n = kart->getPowerup()->getNum() ; if (n<1) return; // shouldn't happen, but just in case if (n>5) n=5; // Display at most 5 items int nSize = (int)(64.0f*std::min(scaling.X, scaling.Y)); int itemSpacing = (int)(std::min(scaling.X, scaling.Y)*30); int x1 = viewport.UpperLeftCorner.X + viewport.getWidth()/2 - (n * itemSpacing)/2; int y1 = viewport.UpperLeftCorner.Y + (int)(20 * scaling.Y); assert(powerup != NULL); assert(powerup->getIcon() != NULL); video::ITexture *t=powerup->getIcon()->getTexture(); assert(t != NULL); core::rect<s32> rect(core::position2di(0, 0), t->getOriginalSize()); for ( int i = 0 ; i < n ; i++ ) { int x2 = (int)(x1+i*itemSpacing); core::rect<s32> pos(x2, y1, x2+nSize, y1+nSize); draw2DImage(t, pos, rect, NULL, NULL, true); } // for i } // drawPowerupIcons
//! draws an 2d image void CNullDriver::draw2DImage(const ITexture* texture, const position2d<s32>& destPos) { if (!texture) return; draw2DImage(texture,destPos, rectangle<s32>(position2d<s32>(0,0), dimension2di(texture->getOriginalSize()))); }
void draw2DImage(const video::ITexture* texture, const core::rect<s32>& destRect, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect, const video::SColor &colors, bool useAlphaChannelOfTexture) { if (!irr_driver->isGLSL()) { video::SColor duplicatedArray[4] = { colors, colors, colors, colors }; draw2DImage(texture, destRect, sourceRect, clipRect, duplicatedArray, useAlphaChannelOfTexture); return; } float width, height, center_pos_x, center_pos_y, tex_width, tex_height, tex_center_pos_x, tex_center_pos_y; getSize(texture->getOriginalSize().Width, texture->getOriginalSize().Height, texture->isRenderTarget(), destRect, sourceRect, width, height, center_pos_x, center_pos_y, tex_width, tex_height, tex_center_pos_x, tex_center_pos_y); if (useAlphaChannelOfTexture) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } if (clipRect) { if (!clipRect->isValid()) return; glEnable(GL_SCISSOR_TEST); const core::dimension2d<u32>& renderTargetSize = irr_driver->getVideoDriver()->getCurrentRenderTargetSize(); glScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height - clipRect->LowerRightCorner.Y, clipRect->getWidth(), clipRect->getHeight()); } glUseProgram(UIShader::UniformColoredTextureRectShader::getInstance()->Program); glBindVertexArray(SharedObject::UIVAO); UIShader::UniformColoredTextureRectShader::getInstance()->SetTextureUnits(createVector<GLuint>(static_cast<const irr::video::COpenGLTexture*>(texture)->getOpenGLTextureName())); UIShader::UniformColoredTextureRectShader::getInstance()->setUniforms( core::vector2df(center_pos_x, center_pos_y), core::vector2df(width, height), core::vector2df(tex_center_pos_x, tex_center_pos_y), core::vector2df(tex_width, tex_height), colors); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); if (clipRect) glDisable(GL_SCISSOR_TEST); glUseProgram(0); glGetError(); }
//! Draws a part of the texture into the rectangle. void CNullDriver::draw2DImage(const ITexture* texture, const rectangle<s32>& destRect, const rectangle<s32>& sourceRect, const rectangle<s32>* clipRect, const SColor* const colors, bool useAlphaChannelOfTexture) { if (destRect.isValid()) draw2DImage(texture, position2d<s32>(destRect.UpperLeftCorner), sourceRect, clipRect, colors?colors[0]:SColor(0xffffffff), useAlphaChannelOfTexture); }
// ---------------------------------------------------------------------------- //! draws a sprite in 2d with scale and color void STKModifiedSpriteBank::draw2DSprite(u32 index, const core::position2di& pos, const core::rect<s32>* clip, const video::SColor& color, u32 starttime, u32 currenttime, bool loop, bool center) { assert( m_magic_number == 0xCAFEC001 ); if (index >= Sprites.size() || Sprites[index].Frames.empty() ) return; // work out frame number u32 frame = 0; if (Sprites[index].frameTime) { u32 f = ((currenttime - starttime) / Sprites[index].frameTime); if (loop) frame = f % Sprites[index].Frames.size(); else frame = (f >= Sprites[index].Frames.size()) ? Sprites[index].Frames.size()-1 : f; } const video::ITexture* tex = Textures[Sprites[index].Frames[frame].textureNumber]; if (!tex) return; const u32 rn = Sprites[index].Frames[frame].rectNumber; if (rn >= Rectangles.size()) return; const core::rect<s32>& r = Rectangles[rn]; const core::dimension2d<s32>& dim = r.getSize(); core::rect<s32> dest( pos, core::dimension2d<s32>((int)(dim.Width*m_scale), (int)(dim.Height*m_scale)) ); if (center) { dest -= dest.getSize() / 2; } /* draw2DImage (const video::ITexture *texture, const core::rect< s32 > &destRect, const core::rect< s32 > &sourceRect, const core::rect< s32 > *clipRect=0, const video::SColor *const colors=0, bool useAlphaChannelOfTexture=false)=0 */ draw2DImage(tex, dest, r /* source rect */, clip, NULL /* colors */, true); } // draw2DSprite
//! draws a set of 2d images, using a color and the alpha channel of the //! texture if desired. void CNullDriver::draw2DImageBatch(const ITexture* texture, const array<position2d<s32> >& positions, const array<rectangle<s32> >& sourceRects, const rectangle<s32>* clipRect, SColor color, bool useAlphaChannelOfTexture) { const u32 drawCount = min_<u32>(positions.size(), sourceRects.size()); for (u32 i=0; i<drawCount; ++i) { draw2DImage(texture, positions[i], sourceRects[i], clipRect, color, useAlphaChannelOfTexture); } }
//! draws a set of 2d images, using a color and the alpha channel of the //! texture if desired. The images are drawn beginning at pos and concatenated //! in one line. All drawings are clipped against clipRect (if != 0). //! The subtextures are defined by the array of sourceRects and are chosen //! by the indices given. void CNullDriver::draw2DImageBatch(const ITexture* texture, const position2d<s32>& pos, const array<rectangle<s32> >& sourceRects, const array<s32>& indices, s32 kerningWidth, const rectangle<s32>* clipRect, SColor color, bool useAlphaChannelOfTexture) { position2d<s32> target(pos); for (u32 i=0; i<indices.size(); ++i) { draw2DImage(texture, target, sourceRects[indices[i]], clipRect, color, useAlphaChannelOfTexture); target.X += sourceRects[indices[i]].getWidth(); target.X += kerningWidth; } }
// ---------------------------------------------------------------------------- void draw2DImage(const video::ITexture* texture, const core::rect<float>& destRect, const core::rect<s32>& sourceRect, const core::rect<s32>* clip_rect, const video::SColor &colors, bool use_alpha_channel_of_texture) { if (!CVS->isGLSL()) { core::rect<irr::s32> dest_rect (irr::s32(destRect.UpperLeftCorner.X), irr::s32(destRect.UpperLeftCorner.Y), irr::s32(destRect.LowerRightCorner.X), irr::s32(destRect.LowerRightCorner.Y)); video::SColor duplicatedArray[4] = { colors, colors, colors, colors }; draw2DImage(texture, dest_rect, sourceRect, clip_rect, duplicatedArray, use_alpha_channel_of_texture); return; } float width, height, center_pos_x, center_pos_y; float tex_width, tex_height, tex_center_pos_x, tex_center_pos_y; getSize(texture->getSize().Width, texture->getSize().Height, texture->isRenderTarget(), destRect, sourceRect, width, height, center_pos_x, center_pos_y, tex_width, tex_height, tex_center_pos_x, tex_center_pos_y); if (use_alpha_channel_of_texture) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } if (clip_rect) { if (!clip_rect->isValid()) return; glEnable(GL_SCISSOR_TEST); const core::dimension2d<u32>& render_target_size = irr_driver->getActualScreenSize(); glScissor(clip_rect->UpperLeftCorner.X, render_target_size.Height - clip_rect->LowerRightCorner.Y, clip_rect->getWidth(), clip_rect->getHeight()); } UniformColoredTextureRectShader::getInstance()->use(); glBindVertexArray(SharedGPUObjects::getUI_VAO()); UniformColoredTextureRectShader::getInstance() ->setTextureUnits(texture->getOpenGLTextureName()); UniformColoredTextureRectShader::getInstance() ->setUniforms(core::vector2df(center_pos_x, center_pos_y), core::vector2df(width, height), core::vector2df(tex_center_pos_x, tex_center_pos_y), core::vector2df(tex_width, tex_height), colors); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); if (clip_rect) glDisable(GL_SCISSOR_TEST); glUseProgram(0); glGetError(); } // draw2DImage
// ---------------------------------------------------------------------------- void FontWithFace::render(const core::stringw& text, const core::rect<s32>& position, const video::SColor& color, bool hcenter, bool vcenter, const core::rect<s32>* clip, FontSettings* font_settings, FontCharCollector* char_collector) { const bool is_bold_face = dynamic_cast<BoldFace*>(this); const bool black_border = font_settings ? font_settings->useBlackBorder() : false; const bool rtl = font_settings ? font_settings->isRTL() : false; const float scale = font_settings ? font_settings->getScale() : 1.0f; const float shadow = font_settings ? font_settings->useShadow() : false; if (shadow) { assert(font_settings); // Avoid infinite recursion font_settings->setShadow(false); core::rect<s32> shadowpos = position; shadowpos.LowerRightCorner.X += 2; shadowpos.LowerRightCorner.Y += 2; render(text, shadowpos, font_settings->getShadowColor(), hcenter, vcenter, clip, font_settings); // Set back font_settings->setShadow(true); } core::position2d<float> offset(float(position.UpperLeftCorner.X), float(position.UpperLeftCorner.Y)); core::dimension2d<s32> text_dimension; if (rtl || hcenter || vcenter || clip) { text_dimension = getDimension(text.c_str(), font_settings); if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) / 2; else if (rtl) offset.X += (position.getWidth() - text_dimension.Width); if (vcenter) offset.Y += (position.getHeight() - text_dimension.Height) / 2; if (clip) { core::rect<s32> clippedRect(core::position2d<s32> (s32(offset.X), s32(offset.Y)), text_dimension); clippedRect.clipAgainst(*clip); if (!clippedRect.isValid()) return; } } // Collect character locations const unsigned int text_size = text.size(); core::array<s32> indices(text_size); core::array<core::position2d<float>> offsets(text_size); std::vector<bool> fallback(text_size); // Test again if lazy load char is needed, // as some text isn't drawn with getDimension insertCharacters(text.c_str()); updateCharactersList(); for (u32 i = 0; i < text_size; i++) { wchar_t c = text[i]; if (c == L'\r' || // Windows breaks c == L'\n' ) // Unix breaks { if (c==L'\r' && text[i+1]==L'\n') c = text[++i]; offset.Y += m_font_max_height * scale; offset.X = position.UpperLeftCorner.X; if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) >> 1; continue; } // if lineBreak bool use_fallback_font = false; const FontArea &area = getAreaFromCharacter(c, &use_fallback_font); fallback[i] = use_fallback_font; if (char_collector == NULL) { float glyph_offset_x = area.bearing_x * (fallback[i] ? m_fallback_font_scale : scale); float glyph_offset_y = area.offset_y * (fallback[i] ? m_fallback_font_scale : scale); offset.X += glyph_offset_x; offset.Y += glyph_offset_y; offsets.push_back(offset); offset.X -= glyph_offset_x; offset.Y -= glyph_offset_y; } else { // Prevent overwriting texture used by billboard text when // using lazy loading characters if (supportLazyLoadChar() && fallback[i]) { const int cur_texno = m_fallback_font->getSpriteBank() ->getSprites()[area.spriteno].Frames[0].textureNumber; if (cur_texno == int(m_fallback_font->getSpriteBank() ->getTextureCount() - 1)) { m_fallback_font->createNewGlyphPage(); } } else if (supportLazyLoadChar()) { const int cur_texno = m_spritebank ->getSprites()[area.spriteno].Frames[0].textureNumber; if (cur_texno == int(m_spritebank->getTextureCount() - 1)) { createNewGlyphPage(); } } // Billboard text specific, use offset_y_bt instead float glyph_offset_x = area.bearing_x * (fallback[i] ? m_fallback_font_scale : scale); float glyph_offset_y = area.offset_y_bt * (fallback[i] ? m_fallback_font_scale : scale); offset.X += glyph_offset_x; offset.Y += glyph_offset_y; offsets.push_back(offset); offset.X -= glyph_offset_x; offset.Y -= glyph_offset_y; } indices.push_back(area.spriteno); offset.X += getCharWidth(area, fallback[i], scale); } // for i < text_size // Do the actual rendering const int indice_amount = indices.size(); core::array<gui::SGUISprite>& sprites = m_spritebank->getSprites(); core::array<core::rect<s32>>& positions = m_spritebank->getPositions(); core::array<gui::SGUISprite>* fallback_sprites; core::array<core::rect<s32>>* fallback_positions; if (m_fallback_font != NULL) { fallback_sprites = &m_fallback_font->m_spritebank->getSprites(); fallback_positions = &m_fallback_font->m_spritebank->getPositions(); } else { fallback_sprites = NULL; fallback_positions = NULL; } const int sprite_amount = sprites.size(); if ((black_border || is_bold_face) && char_collector == NULL) { // Draw black border first, to make it behind the real character // which make script language display better video::SColor black(color.getAlpha(),0,0,0); for (int n = 0; n < indice_amount; n++) { const int sprite_id = indices[n]; if (!fallback[n] && (sprite_id < 0 || sprite_id >= sprite_amount)) continue; if (indices[n] == -1) continue; const int tex_id = (fallback[n] ? (*fallback_sprites)[sprite_id].Frames[0].textureNumber : sprites[sprite_id].Frames[0].textureNumber); core::rect<s32> source = (fallback[n] ? (*fallback_positions) [(*fallback_sprites)[sprite_id].Frames[0].rectNumber] : positions[sprites[sprite_id].Frames[0].rectNumber]); core::dimension2d<float> size(0.0f, 0.0f); float cur_scale = (fallback[n] ? m_fallback_font_scale : scale); size.Width = source.getSize().Width * cur_scale; size.Height = source.getSize().Height * cur_scale; core::rect<float> dest(offsets[n], size); video::ITexture* texture = (fallback[n] ? m_fallback_font->m_spritebank->getTexture(tex_id) : m_spritebank->getTexture(tex_id)); for (int x_delta = -2; x_delta <= 2; x_delta++) { for (int y_delta = -2; y_delta <= 2; y_delta++) { if (x_delta == 0 || y_delta == 0) continue; draw2DImage(texture, dest + core::position2d<float> (float(x_delta), float(y_delta)), source, clip, black, true); } } } } for (int n = 0; n < indice_amount; n++) { const int sprite_id = indices[n]; if (!fallback[n] && (sprite_id < 0 || sprite_id >= sprite_amount)) continue; if (indices[n] == -1) continue; const int tex_id = (fallback[n] ? (*fallback_sprites)[sprite_id].Frames[0].textureNumber : sprites[sprite_id].Frames[0].textureNumber); core::rect<s32> source = (fallback[n] ? (*fallback_positions)[(*fallback_sprites)[sprite_id].Frames[0] .rectNumber] : positions[sprites[sprite_id].Frames[0].rectNumber]); core::dimension2d<float> size(0.0f, 0.0f); float cur_scale = (fallback[n] ? m_fallback_font_scale : scale); size.Width = source.getSize().Width * cur_scale; size.Height = source.getSize().Height * cur_scale; core::rect<float> dest(offsets[n], size); video::ITexture* texture = (fallback[n] ? m_fallback_font->m_spritebank->getTexture(tex_id) : m_spritebank->getTexture(tex_id)); if (fallback[n] || is_bold_face) { video::SColor top = GUIEngine::getSkin()->getColor("font::top"); video::SColor bottom = GUIEngine::getSkin() ->getColor("font::bottom"); top.setAlpha(color.getAlpha()); bottom.setAlpha(color.getAlpha()); video::SColor title_colors[] = {top, bottom, top, bottom}; if (char_collector != NULL) { char_collector->collectChar(texture, dest, source, title_colors); } else { draw2DImage(texture, dest, source, clip, title_colors, true); } } else { if (char_collector != NULL) { video::SColor colors[] = {color, color, color, color}; char_collector->collectChar(texture, dest, source, colors); } else { draw2DImage(texture, dest, source, clip, color, true); } } } } // render
/** Draws the plunger-in-face if necessary. Does nothing if there is no * plunger in face atm. */ void RaceGUIBase::drawPlungerInFace(const Camera *camera, float dt) { const AbstractKart *kart = camera->getKart(); if (kart->getBlockedByPlungerTime()<=0) { m_plunger_state = PLUNGER_STATE_INIT; return; } const core::recti &viewport = camera->getViewport(); const int screen_width = viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X; if(m_plunger_state == PLUNGER_STATE_INIT) { m_plunger_move_time = 0.0f; m_plunger_offset = core::vector2di(0,0); m_plunger_state = PLUNGER_STATE_SLOW_2; m_plunger_speed = core::vector2df(0, 0); } if(World::getWorld()->getPhase()!=World::IN_GAME_MENU_PHASE) { m_plunger_move_time -= dt; if(m_plunger_move_time < dt && m_plunger_state!=PLUNGER_STATE_FAST) { const float fast_time = 0.3f; if(kart->getBlockedByPlungerTime()<fast_time) { // First time we reach faste state: select random target point // at top of screen and set speed accordingly RandomGenerator random; float movement_fraction = 0.3f; int plunger_x_target = screen_width/2 + random.get((int)(screen_width*movement_fraction)) - (int)(screen_width*movement_fraction*0.5f); m_plunger_state = PLUNGER_STATE_FAST; m_plunger_speed = core::vector2df((plunger_x_target-screen_width/2)/fast_time, viewport.getHeight()*0.5f/fast_time); m_plunger_move_time = fast_time; } else { RandomGenerator random; m_plunger_move_time = 0.1f+random.get(50)/200.0f; // Plunger is either moving or not moving if(m_plunger_state==PLUNGER_STATE_SLOW_1) { m_plunger_state = PLUNGER_STATE_SLOW_2; m_plunger_speed = core::vector2df(0, 0.05f*viewport.getHeight() /m_plunger_move_time ); } else { m_plunger_state = PLUNGER_STATE_SLOW_1; m_plunger_speed = core::vector2df(0, 0.02f*viewport.getHeight() /m_plunger_move_time ); } } // has not reach fast moving state } m_plunger_offset.X += (int)(m_plunger_speed.X * dt); m_plunger_offset.Y += (int)(m_plunger_speed.Y * dt); } const int plunger_size = (int)(0.6f * screen_width); int offset_y = viewport.UpperLeftCorner.Y + viewport.getHeight()/2 - plunger_size/2 - m_plunger_offset.Y; int plunger_x = viewport.UpperLeftCorner.X + screen_width/2 - plunger_size/2; video::ITexture *t=m_plunger_face->getTexture(); plunger_x += (int)m_plunger_offset.X; core::rect<s32> dest(plunger_x, offset_y, plunger_x+plunger_size, offset_y+plunger_size); const core::rect<s32> source(core::position2d<s32>(0,0), t->getOriginalSize()); draw2DImage(t, dest, source, &viewport /* clip */, NULL /* color */, true /* alpha */ ); } // drawPlungerInFace
/** 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
/** Displays the description given for the music currently being played. * This is usually the title and composer. */ void RaceGUIBase::drawGlobalMusicDescription() { // show no music description when it's off if (!UserConfigParams::m_music) return; gui::IGUIFont* font = GUIEngine::getFont(); float race_time = World::getWorld()->getTime(); // In follow the leader the clock counts backwards, so convert the // countdown time to time since start: if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER) race_time = ((FollowTheLeaderRace*)World::getWorld())->getClockStartTime() - race_time; // ---- Manage pulsing effect // 3.0 is the duration of ready/set (TODO: don't hardcode) float timeProgression = (float)(race_time) / (float)(stk_config->m_music_credit_time - 2.0f); const int x_pulse = (int)(sin(race_time*9.0f)*10.0f); const int y_pulse = (int)(cos(race_time*9.0f)*10.0f); float resize = 1.0f; if (timeProgression < 0.1) { resize = timeProgression/0.1f; } else if (timeProgression > 0.9) { resize = 1.0f - (timeProgression - 0.9f)/0.1f; } const float resize3 = resize*resize*resize; // Get song name, and calculate its size, allowing us to position stuff const MusicInformation* mi = music_manager->getCurrentMusic(); if (!mi) return; core::stringw thetext = core::stringw(L"\"") + mi->getTitle() + L"\""; core::dimension2d< u32 > textSize = font->getDimension(thetext.c_str()); int textWidth = textSize.Width; int textWidth2 = 0; core::stringw thetext_composer; if (mi->getComposer()!="") { // I18N: string used to show the author of the music. (e.g. "Sunny Song" by "John Doe") thetext_composer = _("by"); thetext_composer += " "; thetext_composer += mi->getComposer().c_str(); textWidth2 = font->getDimension(thetext_composer.c_str()).Width; } const int max_text_size = (int)(UserConfigParams::m_width*2.0f/3.0f); if (textWidth > max_text_size) textWidth = max_text_size; if (textWidth2 > max_text_size) textWidth2 = max_text_size; const int ICON_SIZE = 64; const int y = UserConfigParams::m_height - 80; // the 20 is an arbitrary space left between the note icon and the text const int noteX = (UserConfigParams::m_width / 2) - std::max(textWidth, textWidth2)/2 - ICON_SIZE/2 - 20; const int noteY = y; // the 20 is an arbitrary space left between the note icon and the text const int textXFrom = (UserConfigParams::m_width / 2) - std::max(textWidth, textWidth2)/2 + 20; const int textXTo = (UserConfigParams::m_width / 2) + std::max(textWidth, textWidth2)/2 + 20; // ---- Draw "by" text const int text_y = (int)(UserConfigParams::m_height - 80*(resize3) + 40*(1-resize)); static const video::SColor white = video::SColor(255, 255, 255, 255); if(mi->getComposer()!="") { core::rect<s32> pos_by(textXFrom, text_y+40, textXTo, text_y+40); font->draw(thetext_composer, pos_by, white, true, true); } // ---- Draw "song name" text core::rect<s32> pos(textXFrom, text_y, textXTo, text_y); font->draw(thetext.c_str(), pos, white, true /* hcenter */, true /* vcenter */); // Draw music icon int iconSizeX = (int)(ICON_SIZE*resize + x_pulse*resize*resize); int iconSizeY = (int)(ICON_SIZE*resize + y_pulse*resize*resize); video::ITexture *t = m_music_icon->getTexture(); core::rect<s32> dest(noteX-iconSizeX/2+20, noteY-iconSizeY/2+ICON_SIZE/2, noteX+iconSizeX/2+20, noteY+iconSizeY/2+ICON_SIZE/2); const core::rect<s32> source(core::position2d<s32>(0,0), t->getOriginalSize()); draw2DImage(t, dest, source, NULL, NULL, true); } // drawGlobalMusicDescription
//! draws some text and clips it to the specified rectangle if wanted void ScalableFont::doDraw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip, FontCharCollector* charCollector) { if (!Driver) return; if (m_shadow) { m_shadow = false; // avoid infinite recursion core::rect<s32> shadowpos = position; shadowpos.LowerRightCorner.X += 2; shadowpos.LowerRightCorner.Y += 2; draw(text, shadowpos, m_shadow_color, hcenter, vcenter, clip); m_shadow = true; // set back } core::position2d<s32> offset = position.UpperLeftCorner; core::dimension2d<s32> text_dimension; if (m_rtl || hcenter || vcenter || clip) { text_dimension = getDimension(text.c_str()); if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) / 2; else if (m_rtl) offset.X += (position.getWidth() - text_dimension.Width); if (vcenter) offset.Y += (position.getHeight() - text_dimension.Height) / 2; if (clip) { core::rect<s32> clippedRect(offset, text_dimension); clippedRect.clipAgainst(*clip); if (!clippedRect.isValid()) return; } } // ---- collect character locations const unsigned int text_size = text.size(); core::array<s32> indices(text_size); core::array<core::position2di> offsets(text_size); std::vector<bool> fallback(text_size); for (u32 i = 0; i<text_size; i++) { wchar_t c = text[i]; if (c == L'\r' || // Windows breaks c == L'\n' ) // Unix breaks { if(c==L'\r' && text[i+1]==L'\n') c = text[++i]; offset.Y += (int)(MaxHeight*m_scale); offset.X = position.UpperLeftCorner.X; if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) >> 1; continue; } // if lineBreak bool use_fallback_font = false; const SFontArea &area = getAreaFromCharacter(c, &use_fallback_font); fallback[i] = use_fallback_font; offset.X += area.underhang; offsets.push_back(offset); // Invisible character. add something to the array anyway so that // indices from the various arrays remain in sync indices.push_back( Invisible.findFirst(c) < 0 ? area.spriteno : -1 ); offset.X += getCharWidth(area, fallback[i]); } // for i<text_size // ---- do the actual rendering const int indiceAmount = indices.size(); core::array< SGUISprite >& sprites = SpriteBank->getSprites(); core::array< core::rect<s32> >& positions = SpriteBank->getPositions(); core::array< SGUISprite >* fallback_sprites; core::array< core::rect<s32> >* fallback_positions; if(m_fallback_font!=NULL) { fallback_sprites = &m_fallback_font->SpriteBank->getSprites(); fallback_positions = &m_fallback_font->SpriteBank->getPositions(); } else { fallback_sprites = NULL; fallback_positions = NULL; } const int spriteAmount = sprites.size(); for (int n=0; n<indiceAmount; n++) { const int spriteID = indices[n]; if (!fallback[n] && (spriteID < 0 || spriteID >= spriteAmount)) continue; if (indices[n] == -1) continue; //assert(sprites[spriteID].Frames.size() > 0); const int texID = (fallback[n] ? (*fallback_sprites)[spriteID].Frames[0].textureNumber : sprites[spriteID].Frames[0].textureNumber); core::rect<s32> source = (fallback[n] ? (*fallback_positions)[(*fallback_sprites)[spriteID].Frames[0].rectNumber] : positions[sprites[spriteID].Frames[0].rectNumber]); const TextureInfo& info = (fallback[n] ? (*(m_fallback_font->m_texture_files.find(texID))).second : (*(m_texture_files.find(texID))).second ); float char_scale = info.m_scale; core::dimension2d<s32> size = source.getSize(); float scale = (fallback[n] ? m_scale*m_fallback_font_scale : m_scale); size.Width = (int)(size.Width * scale * char_scale); size.Height = (int)(size.Height * scale * char_scale); // align vertically if character is smaller int y_shift = (size.Height < MaxHeight*m_scale ? (int)((MaxHeight*m_scale - size.Height)/2.0f) : 0); core::rect<s32> dest(offsets[n] + core::position2di(0, y_shift), size); video::ITexture* texture = (fallback[n] ? m_fallback_font->SpriteBank->getTexture(texID) : SpriteBank->getTexture(texID) ); /* if (fallback[n]) { Log::info("ScalableFont", "Using fallback font %s; source area is %d, %d; size %d, %d; dest = %d, %d", core::stringc(texture->getName()).c_str(), source.UpperLeftCorner.X, source.UpperLeftCorner.Y, source.getWidth(), source.getHeight(), offsets[n].X, offsets[n].Y); } */ if (texture == NULL) { // perform lazy loading if (fallback[n]) { m_fallback_font->lazyLoadTexture(texID); texture = m_fallback_font->SpriteBank->getTexture(texID); } else { lazyLoadTexture(texID); texture = SpriteBank->getTexture(texID); } if (texture == NULL) { Log::warn("ScalableFont", "Character not found in current font"); continue; // no such character } } if (m_black_border && charCollector == NULL) { // draw black border video::SColor black(color.getAlpha(),0,0,0); for (int x_delta=-2; x_delta<=2; x_delta++) { for (int y_delta=-2; y_delta<=2; y_delta++) { if (x_delta == 0 || y_delta == 0) continue; draw2DImage(texture, dest + core::position2d<s32>(x_delta, y_delta), source, clip, black, true); } } } if (fallback[n]) { // TODO: don't hardcode colors? video::SColor orange(color.getAlpha(), 255, 100, 0); video::SColor yellow(color.getAlpha(), 255, 220, 15); video::SColor title_colors[] = {orange, yellow, orange, yellow}; if (charCollector != NULL) { charCollector->collectChar(texture, dest, source, title_colors); } else { draw2DImage(texture, dest, source, clip, title_colors, true); } } else { if (charCollector != NULL) { video::SColor colors[] = { color, color, color, color }; charCollector->collectChar(texture, dest, source, colors); } else { draw2DImage(texture, dest, source, clip, color, true); } #ifdef FONT_DEBUG video::IVideoDriver* driver = GUIEngine::getDriver(); driver->draw2DLine(core::position2d<s32>(dest.UpperLeftCorner.X, dest.UpperLeftCorner.Y), core::position2d<s32>(dest.UpperLeftCorner.X, dest.LowerRightCorner.Y), video::SColor(255, 255,0,0)); driver->draw2DLine(core::position2d<s32>(dest.LowerRightCorner.X, dest.LowerRightCorner.Y), core::position2d<s32>(dest.LowerRightCorner.X, dest.UpperLeftCorner.Y), video::SColor(255, 255,0,0)); driver->draw2DLine(core::position2d<s32>(dest.LowerRightCorner.X, dest.LowerRightCorner.Y), core::position2d<s32>(dest.UpperLeftCorner.X, dest.LowerRightCorner.Y), video::SColor(255, 255,0,0)); driver->draw2DLine(core::position2d<s32>(dest.UpperLeftCorner.X, dest.UpperLeftCorner.Y), core::position2d<s32>(dest.LowerRightCorner.X, dest.UpperLeftCorner.Y), video::SColor(255, 255,0,0)); #endif } } }
//----------------------------------------------------------------------------- void RaceResultGUI::displaySoccerResults() { //Draw win text core::stringw resultText; static video::SColor color = video::SColor(255, 255, 255, 255); gui::IGUIFont* font = GUIEngine::getTitleFont(); int currX = UserConfigParams::m_width/2; RowInfo *ri = &(m_all_row_infos[0]); int currY = (int)ri->m_y_pos; SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); int teamScore[2] = {soccerWorld->getScore(0), soccerWorld->getScore(1)}; GUIEngine::Widget *table_area = getWidget("result-table"); int height = table_area->m_h + table_area->m_y; if(teamScore[0] > teamScore[1]) { resultText = _("Red Team Wins"); } else if(teamScore[1] > teamScore[0]) { resultText = _("Blue Team Wins"); } else { //Cannot really happen now. Only in time limited matches. resultText = _("It's a draw"); } core::rect<s32> pos(currX, currY, currX, currY); font->draw(resultText.c_str(), pos, color, true, true); core::dimension2du rect = m_font->getDimension(resultText.c_str()); //Draw team scores: currY += rect.Height; currX /= 2; irr::video::ITexture* redTeamIcon = irr_driver->getTexture(FileManager::GUI, "soccer_ball_red.png"); irr::video::ITexture* blueTeamIcon = irr_driver->getTexture(FileManager::GUI, "soccer_ball_blue.png"); core::recti sourceRect(core::vector2di(0,0), redTeamIcon->getSize()); core::recti destRect(currX, currY, currX+redTeamIcon->getSize().Width/2, currY+redTeamIcon->getSize().Height/2); draw2DImage(redTeamIcon, destRect,sourceRect, NULL,NULL, true); currX += UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; destRect = core::recti(currX, currY, currX+redTeamIcon->getSize().Width/2, currY+redTeamIcon->getSize().Height/2); draw2DImage(blueTeamIcon,destRect,sourceRect, NULL, NULL, true); resultText = StringUtils::toWString(teamScore[1]); rect = m_font->getDimension(resultText.c_str()); currX += redTeamIcon->getSize().Width/4; currY += redTeamIcon->getSize().Height/2 + rect.Height/4; pos = core::rect<s32>(currX, currY, currX, currY); color = video::SColor(255,255,255,255); font->draw(resultText.c_str(), pos, color, true, false); currX -= UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; resultText = StringUtils::toWString(teamScore[0]); pos = core::rect<s32>(currX,currY,currX,currY); font->draw(resultText.c_str(), pos, color, true, false); int centerX = UserConfigParams::m_width/2; pos = core::rect<s32>(centerX, currY, centerX, currY); font->draw("-", pos, color, true, false); //Draw goal scorers: //The red scorers: currY += rect.Height/2 + rect.Height/4; font = GUIEngine::getSmallFont(); std::vector<int> scorers = soccerWorld->getScorers(0); std::vector<float> scoreTimes = soccerWorld->getScoreTimes(0); irr::video::ITexture* scorerIcon; int prevY = currY; for(unsigned int i=0; i<scorers.size(); i++) { resultText = soccerWorld->getKart(scorers.at(i))-> getKartProperties()->getName(); resultText.append(" "); resultText.append(StringUtils::timeToString(scoreTimes.at(i)).c_str()); rect = m_font->getDimension(resultText.c_str()); if(height-prevY < ((short)scorers.size()+1)*(short)rect.Height) currY += (height-prevY)/((short)scorers.size()+1); else currY += rect.Height; if(currY > height) break; pos = core::rect<s32>(currX,currY,currX,currY); font->draw(resultText,pos, color, true, false); scorerIcon = soccerWorld->getKart(scorers.at(i)) ->getKartProperties()->getIconMaterial()->getTexture(); sourceRect = core::recti(core::vector2di(0,0), scorerIcon->getSize()); irr::u32 offsetX = GUIEngine::getFont()->getDimension(resultText.c_str()).Width/2; destRect = core::recti(currX-offsetX-30, currY, currX-offsetX, currY+ 30); draw2DImage(scorerIcon, destRect, sourceRect, NULL, NULL, true); } //The blue scorers: currY = prevY; currX += UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; scorers = soccerWorld->getScorers(1); scoreTimes = soccerWorld->getScoreTimes(1); for(unsigned int i=0; i<scorers.size(); i++) { resultText = soccerWorld->getKart(scorers.at(i))-> getKartProperties()->getName(); resultText.append(" "); resultText.append(StringUtils::timeToString(scoreTimes.at(i)).c_str()); rect = m_font->getDimension(resultText.c_str()); if(height-prevY < ((short)scorers.size()+1)*(short)rect.Height) currY += (height-prevY)/((short)scorers.size()+1); else currY += rect.Height; if(currY > height) break; pos = core::rect<s32>(currX,currY,currX,currY); font->draw(resultText,pos, color, true, false); scorerIcon = soccerWorld->getKart(scorers.at(i))-> getKartProperties()->getIconMaterial()->getTexture(); sourceRect = core::recti(core::vector2di(0,0), scorerIcon->getSize()); irr::u32 offsetX = GUIEngine::getFont()->getDimension(resultText.c_str()).Width/2; destRect = core::recti(currX-offsetX-30, currY, currX-offsetX, currY+ 30); draw2DImage(scorerIcon, destRect, sourceRect, NULL, NULL, true); } }
/** Displays the race results for a single kart. * \param n Index of the kart to be displayed. * \param display_points True if GP points should be displayed, too */ void RaceResultGUI::displayOneEntry(unsigned int x, unsigned int y, unsigned int n, bool display_points) { RowInfo *ri = &(m_all_row_infos[n]); video::SColor color = ri->m_is_player_kart ? video::SColor(255,255,0, 0 ) : video::SColor(255,255,255,255); unsigned int current_x = x; // First draw the icon // ------------------- if(ri->m_kart_icon) { core::recti source_rect(core::vector2di(0,0), ri->m_kart_icon->getSize()); core::recti dest_rect(current_x, y, current_x+m_width_icon, y+m_width_icon); draw2DImage(ri->m_kart_icon, dest_rect, source_rect, NULL, NULL, true); } current_x += m_width_icon + m_width_column_space; // Draw the name // ------------- core::recti pos_name(current_x, y, UserConfigParams::m_width, y+m_distance_between_rows); m_font->draw(ri->m_kart_name, pos_name, color, false, false, NULL, true /* ignoreRTL */); current_x += m_width_kart_name + m_width_column_space; // Draw the time except in FTL mode // -------------------------------- if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_FOLLOW_LEADER) { core::recti dest_rect = core::recti(current_x, y, current_x+100, y+10); m_font->draw(ri->m_finish_time_string, dest_rect, color, false, false, NULL, true /* ignoreRTL */); current_x += m_width_finish_time + m_width_column_space; } // Only display points in GP mode and when the GP results are displayed. // ===================================================================== if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX && m_animation_state != RR_RACE_RESULT) { // Draw the new points // ------------------- if(ri->m_new_points > 0) { core::recti dest_rect = core::recti(current_x, y, current_x+100, y+10); core::stringw point_string = core::stringw("+") + core::stringw((int)ri->m_new_points); // With mono-space digits space has the same width as each digit, // so we can simply fill up the string with spaces to get the // right aligned. while(point_string.size()<3) point_string = core::stringw(" ")+point_string; m_font->draw(point_string, dest_rect, color, false, false, NULL, true /* ignoreRTL */); } current_x += m_width_new_points + m_width_column_space; // Draw the old_points plus increase value // --------------------------------------- core::recti dest_rect = core::recti(current_x, y, current_x+100, y+10); core::stringw point_inc_string = core::stringw((int)(ri->m_current_displayed_points)); while(point_inc_string.size()<3) point_inc_string = core::stringw(" ")+point_inc_string; m_font->draw(point_inc_string, dest_rect, color, false, false, NULL, true /* ignoreRTL */); } } // displayOneEntry
// ---------------------------------------------------------------------------- void RaceResultGUI::displayHighScores() { // This happens in demo world if(!World::getWorld()) return; Highscores* scores = World::getWorld()->getHighscores(); // In some case for exemple FTL they will be no highscores if (scores != NULL) { video::SColor white_color = video::SColor(255,255,255,255); int x = (int)(UserConfigParams::m_width*0.55f); int y = m_top; // First draw title GUIEngine::getFont()->draw(_("Highscores"), core::recti(x, y, 0, 0), white_color, false, false, NULL, true /* ignoreRTL */); std::string kart_name; irr::core::stringw player_name; // prevent excessive long name unsigned int max_characters = 15; float time; for (int i = 0; i < scores->getNumberEntries(); i++) { scores->getEntry(i,kart_name,player_name, &time); if (player_name.size() > max_characters) { int begin = (int(m_timer/0.4f)) % ( player_name.size() - max_characters ); player_name = player_name.subString(begin,max_characters,false); } video::SColor text_color = white_color; if (m_highscore_rank-1 == i) { text_color = video::SColor(255,255,0, 0 ); } int current_x = x; int current_y = y+(i+1)*50; const KartProperties* prop = kart_properties_manager->getKart(kart_name); if (prop != NULL) { const std::string &icon_path = prop->getAbsoluteIconFile(); video::ITexture* kart_icon_texture = irr_driver->getTexture( icon_path ); if (kart_icon_texture != NULL) { core::recti source_rect(core::vector2di(0,0), kart_icon_texture->getSize()); core::recti dest_rect(current_x, current_y, current_x+m_width_icon, current_y+m_width_icon); draw2DImage( kart_icon_texture, dest_rect, source_rect, NULL, NULL, true); current_x += m_width_icon + m_width_column_space; } } // draw the player name GUIEngine::getSmallFont()->draw(player_name.c_str(), core::recti(current_x, current_y, current_x+150, current_y+10), text_color, false, false, NULL, true /* ignoreRTL */); current_x += 180; // Finally draw the time std::string time_string = StringUtils::timeToString(time); GUIEngine::getSmallFont()->draw(time_string.c_str(), core::recti(current_x, current_y, current_x+100, current_y+10), text_color, false, false, NULL, true /* ignoreRTL */); } } }