From 8ab10035a1b739c5ee7883103ae8e0d6da023573 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Tue, 8 Feb 2011 12:59:32 +0000 Subject: [PATCH] Updated VS10 project file, and started to add minimal race gui. This was only committed early to update the VC10 project file. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@7656 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/Makefile.am | 2 + src/config/user_config.hpp | 2 + src/ide/vc10/supertuxkart.vcxproj | 4 + src/ide/vc10/supertuxkart.vcxproj.filters | 12 + src/modes/world.cpp | 6 +- src/states_screens/minimal_race_gui.cpp | 868 ++++++++++++++++++++++ src/states_screens/minimal_race_gui.hpp | 203 +++++ 7 files changed, 1096 insertions(+), 1 deletion(-) create mode 100644 src/states_screens/minimal_race_gui.cpp create mode 100644 src/states_screens/minimal_race_gui.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 72163bac6..692982c60 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -333,6 +333,8 @@ supertuxkart_SOURCES = \ states_screens/kart_selection.hpp \ states_screens/main_menu_screen.cpp \ states_screens/main_menu_screen.hpp \ + states_screens/minimal_race_gui.cpp \ + states_screens/minimal_race_gui.hpp \ states_screens/options_screen_audio.cpp \ states_screens/options_screen_audio.hpp \ states_screens/options_screen_input.cpp \ diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 08e1a2688..f91e8d076 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -344,6 +344,8 @@ namespace UserConfigParams // ---- Misc + PARAM_PREFIX BoolUserConfigParam m_minimal_race_gui + PARAM_DEFAULT( BoolUserConfigParam(false, "minimal-race-gui") ); PARAM_PREFIX BoolUserConfigParam m_crashed PARAM_DEFAULT( BoolUserConfigParam(false, "crashed") ); // TODO : is this used with new code? does it still work? PARAM_PREFIX BoolUserConfigParam m_log_errors diff --git a/src/ide/vc10/supertuxkart.vcxproj b/src/ide/vc10/supertuxkart.vcxproj index 7aa1a4fe8..130455b5c 100755 --- a/src/ide/vc10/supertuxkart.vcxproj +++ b/src/ide/vc10/supertuxkart.vcxproj @@ -164,6 +164,7 @@ + @@ -201,6 +202,7 @@ + @@ -361,6 +363,7 @@ + @@ -408,6 +411,7 @@ + diff --git a/src/ide/vc10/supertuxkart.vcxproj.filters b/src/ide/vc10/supertuxkart.vcxproj.filters index 5b89160cf..4f233e69e 100755 --- a/src/ide/vc10/supertuxkart.vcxproj.filters +++ b/src/ide/vc10/supertuxkart.vcxproj.filters @@ -717,6 +717,12 @@ Quelldateien\physics + + Quelldateien\states_screens + + + Quelldateien\graphics + @@ -1343,5 +1349,11 @@ Headerdateien\physics + + Headerdateien\states_screens + + + Headerdateien\graphics + \ No newline at end of file diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 3225dc232..bd9c5c20a 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -47,6 +47,7 @@ #include "states_screens/state_manager.hpp" #include "states_screens/race_gui_base.hpp" #include "states_screens/race_gui.hpp" +#include "states_screens/minimal_race_gui.hpp" #include "states_screens/race_result_gui.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" @@ -107,7 +108,10 @@ void World::init() // Create the race gui before anything else is attached to the scene node // (which happens when the track is loaded). This allows the race gui to // do any rendering on texture. - m_race_gui = new RaceGUI(); + if(UserConfigParams::m_minimal_race_gui) + m_race_gui = new MinimalRaceGUI(); + else + m_race_gui = new RaceGUI(); // Grab the track file m_track = track_manager->getTrack(race_manager->getTrackName()); diff --git a/src/states_screens/minimal_race_gui.cpp b/src/states_screens/minimal_race_gui.cpp new file mode 100644 index 000000000..00a791eff --- /dev/null +++ b/src/states_screens/minimal_race_gui.cpp @@ -0,0 +1,868 @@ +// $Id$ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2004-2005 Steve Baker +// Copyright (C) 2006 Joerg Henrichs, SuperTuxKart-Team, Steve Baker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/minimal_race_gui.hpp" + +#include "irrlicht.h" +using namespace irr; + +#include "audio/music_manager.hpp" +#include "config/user_config.hpp" +#include "graphics/camera.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "guiengine/engine.hpp" +#include "guiengine/modaldialog.hpp" +#include "guiengine/scalable_font.hpp" +#include "io/file_manager.hpp" +#include "input/input.hpp" +#include "input/input_manager.hpp" +#include "items/attachment.hpp" +#include "items/attachment_manager.hpp" +#include "items/powerup_manager.hpp" +#include "karts/kart_properties_manager.hpp" +#include "modes/follow_the_leader.hpp" +#include "modes/linear_world.hpp" +#include "modes/world.hpp" +#include "race/race_manager.hpp" +#include "tracks/track.hpp" +#include "utils/constants.hpp" +#include "utils/string_utils.hpp" +#include "utils/translation.hpp" + +/** The constructor is called before anything is attached to the scene node. + * So rendering to a texture can be done here. But world is not yet fully + * created, so only the race manager can be accessed safely. + */ +MinimalRaceGUI::MinimalRaceGUI() +{ + m_enabled = true; + + // Originally m_map_height was 100, and we take 480 as minimum res + const float scaling = irr_driver->getFrameSize().Height / 480.0f; + // Marker texture has to be power-of-two for (old) OpenGL compliance + m_marker_rendered_size = 2 << ((int) ceil(1.0 + log(32.0 * scaling))); + m_marker_ai_size = (int)( 28.0f * scaling); + m_marker_player_size = (int)( 24.0f * scaling); + m_map_width = (int)(200.0f * scaling); + m_map_height = (int)(200.0f * scaling); + m_map_left = (int)( 10.0f * scaling); + m_map_bottom = (int)( 10.0f * scaling); + + // Minimap is also rendered bigger via OpenGL, so find power-of-two again + const int map_texture = 2 << ((int) ceil(1.0 + log(128.0 * scaling))); + m_map_rendered_width = map_texture; + m_map_rendered_height = map_texture; + + m_max_font_height = GUIEngine::getFontHeight() + 10; + m_small_font_max_height = GUIEngine::getSmallFontHeight() + 5; + + // special case : when 3 players play, use available 4th space for such things + if (race_manager->getNumLocalPlayers() == 3) + { + m_map_left = UserConfigParams::m_width - m_map_width; + } + + m_speed_meter_icon = material_manager->getMaterial("speedback.png"); + m_speed_bar_icon = material_manager->getMaterial("speedfore.png"); + m_plunger_face = material_manager->getMaterial("plungerface.png"); + m_music_icon = material_manager->getMaterial("notes.png"); + createMarkerTexture(); + + m_gauge_full = irr_driver->getTexture( file_manager->getGUIDir() + "gauge_full.png" ); + m_gauge_empty = irr_driver->getTexture( file_manager->getGUIDir() + "gauge_empty.png" ); + m_gauge_goal = irr_driver->getTexture( file_manager->getGUIDir() + "gauge_goal.png" ); + + // Translate strings only one in constructor to avoid calling + // gettext in each frame. + //I18N: Shown at the end of a race + m_string_lap = _("Lap"); + m_string_rank = _("Rank"); + + //I18N: When some GlobalPlayerIcons are hidden, write "Top 10" to show it + m_string_top = _("Top %i"); + //I18N: as in "ready, set, go", shown at the beginning of the race + m_string_ready = _("Ready!"); + m_string_set = _("Set!"); + m_string_go = _("Go!"); + + //read icon frame picture + m_icons_frame=material_manager->getMaterial("icons-frame.png"); + + // Determine maximum length of the rank/lap text, in order to + // align those texts properly on the right side of the viewport. + gui::ScalableFont* font = GUIEngine::getFont(); + m_rank_lap_width = font->getDimension(m_string_lap.c_str()).Width; + + m_timer_width = font->getDimension(L"99:99:99").Width; + + font = (race_manager->getNumLocalPlayers() > 2 ? GUIEngine::getSmallFont() : GUIEngine::getFont()); + + int w; + if (race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER || + race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES || + race_manager->getNumLaps() > 9) + w = font->getDimension(L"99/99").Width; + else + w = font->getDimension(L"9/9").Width; + + // In some split screen configuration the energy bar might be next + // to the lap display - so make the lap X/Y display large enough to + // leave space for the energy bar (16 pixels) and 10 pixels of space + // to the right (see drawEnergyMeter for details). + w += 16 + 10; + if(m_rank_lap_width < w) m_rank_lap_width = w; + w = font->getDimension(m_string_rank.c_str()).Width; + if(m_rank_lap_width < w) m_rank_lap_width = w; + +} // MinimalRaceGUI + +//----------------------------------------------------------------------------- +MinimalRaceGUI::~MinimalRaceGUI() +{ + irr_driver->removeTexture(m_marker); +} // ~MinimalRaceGUI + +//----------------------------------------------------------------------------- +/** Creates a texture with the markers for all karts in the current race + * on it. This assumes that nothing is attached to the scene node at + * this stage. + */ +void MinimalRaceGUI::createMarkerTexture() +{ + unsigned int num_karts = race_manager->getNumberOfKarts(); + unsigned int npower2 = 1; + // Textures must be power of 2, so + while(npower2>1)-1; + IrrDriver::RTTProvider rttProvider(core::dimension2du(m_marker_rendered_size + *npower2, + m_marker_rendered_size), + "MinimalRaceGUI::markers"); + scene::ICameraSceneNode *camera = irr_driver->addCameraSceneNode(); + core::matrix4 projection; + projection.buildProjectionMatrixOrthoLH((float)(m_marker_rendered_size*npower2), + (float)(m_marker_rendered_size), + -1.0f, 1.0f); + camera->setProjectionMatrix(projection, true); + core::vector3df center( (float)(m_marker_rendered_size*npower2>>1), + (float)(m_marker_rendered_size>>1), 0.0f); + camera->setPosition(center); + camera->setUpVector(core::vector3df(0,1,0)); + camera->setTarget(center + core::vector3df(0,0,4)); + // The call to render sets the projection matrix etc. So we have to call + // this now before doing the direct OpenGL calls. + // FIXME: perhaps we should use three calls to irr_driver: begin(), + // render(), end() - so we could do the rendering by calling to + // draw2DPolygon() between render() and end(), avoiding the + // call to camera->render() + camera->render(); + // We have to reset the material here, since otherwise the last + // set material (i.e from the kart selection screen) will be used + // when rednering to the texture. + video::SMaterial m; + m.setTexture(0, NULL); + irr_driver->getVideoDriver()->setMaterial(m); + for(unsigned int i=0; igetKartIdent(i); + assert(kart_ident.size() > 0); + + const KartProperties *kp=kart_properties_manager->getKart(kart_ident); + assert(kp != NULL); + + core::vector2df center((float)((m_marker_rendered_size>>1) + +i*m_marker_rendered_size), + (float)(m_marker_rendered_size>>1) ); + int count = kp->getShape(); + video::ITexture *t = kp->getMinimapIcon(); + if(t) + { + video::ITexture *t = kp->getIconMaterial()->getTexture(); + core::recti dest_rect(i*m_marker_rendered_size, + 0, + (i+1)*m_marker_rendered_size, + m_marker_rendered_size); + core::recti source_rect(core::vector2di(0,0), t->getSize()); + irr_driver->getVideoDriver()->draw2DImage(t, dest_rect, + source_rect, + /*clipRect*/0, + /*color*/ 0, + /*useAlpha*/true); + } + else // no special minimap icon defined + { + video::S3DVertex *vertices = new video::S3DVertex[count+1]; + unsigned short int *index = new unsigned short int[count+1]; + video::SColor color = kp->getColor(); + createRegularPolygon(count, (float)radius, center, color, + vertices, index); + irr_driver->getVideoDriver()->draw2DVertexPrimitiveList(vertices, + count, index, count-2, + video::EVT_STANDARD, + scene::EPT_TRIANGLE_FAN); + delete [] vertices; + delete [] index; + } // if special minimap icon defined + } + + m_marker = rttProvider.renderToTexture(-1, /*is_2d_render*/true); + irr_driver->removeCameraSceneNode(camera); +} // createMarkerTexture + +//----------------------------------------------------------------------------- +/** Creates the 2D vertices for a regular polygon. Adopted from Irrlicht. + * \param n Number of vertices to use. + * \param radius Radius of the polygon. + * \param center The center point of the polygon. + * \param v Pointer to the array of vertices. + */ +void MinimalRaceGUI::createRegularPolygon(unsigned int n, float radius, + const core::vector2df ¢er, + const video::SColor &color, + video::S3DVertex *v, + unsigned short int *index) +{ + float f = 2*M_PI/(float)n; + for (unsigned int i=0; igetNumLocalPlayers() == 3 && + !GUIEngine::ModalDialog::isADialogActive()) + { + static video::SColor black = video::SColor(255,0,0,0); + irr_driver->getVideoDriver() + ->draw2DRectangle(black, + core::rect(UserConfigParams::m_width/2, + UserConfigParams::m_height/2, + UserConfigParams::m_width, + UserConfigParams::m_height)); + } + + World *world = World::getWorld(); + assert(world != NULL); + if(world->getPhase() >= WorldStatus::READY_PHASE && + world->getPhase() <= WorldStatus::GO_PHASE ) + { + drawGlobalReadySetGo(); + } + + // Timer etc. are not displayed unless the game is actually started. + if(!world->isRacePhase()) return; + if (!m_enabled) return; + + drawGlobalTimer(); + if(world->getPhase() == WorldStatus::GO_PHASE || + world->getPhase() == WorldStatus::MUSIC_PHASE) + { + drawGlobalMusicDescription(); + } + + drawGlobalMiniMap(); + KartIconDisplayInfo* info = world->getKartsDisplayInfo(); +} // renderGlobal + +//----------------------------------------------------------------------------- +/** Render the details for a single player, i.e. speed, energy, + * collectibles, ... + * \param kart Pointer to the kart for which to render the view. + */ +void MinimalRaceGUI::renderPlayerView(const Kart *kart) +{ + if (!m_enabled) return; + + const core::recti &viewport = kart->getCamera()->getViewport(); + core::vector2df scaling = kart->getCamera()->getScaling(); + //std::cout << "Applied ratio : " << viewport.getWidth()/800.0f << std::endl; + + scaling *= viewport.getWidth()/800.0f; // scale race GUI along screen size + + //std::cout << "Scale : " << scaling.X << ", " << scaling.Y << std::endl; + + if (kart->hasViewBlockedByPlunger()) + { + int offset_y = viewport.UpperLeftCorner.Y; + + const int screen_width = viewport.LowerRightCorner.X + - viewport.UpperLeftCorner.X; + const int plunger_size = viewport.LowerRightCorner.Y + - viewport.UpperLeftCorner.Y; + int plunger_x = viewport.UpperLeftCorner.X + screen_width/2 + - plunger_size/2; + + video::ITexture *t=m_plunger_face->getTexture(); + core::rect dest(plunger_x, offset_y, + plunger_x+plunger_size, offset_y+plunger_size); + const core::rect source(core::position2d(0,0), + t->getOriginalSize()); + + //static const video::SColor white = video::SColor(255, 255, 255, 255); + + irr_driver->getVideoDriver()->draw2DImage(t, dest, source, + NULL /* clip */, + NULL /* color */, + true /* alpha */); + } + + + drawAllMessages (kart, viewport, scaling); + if(!World::getWorld()->isRacePhase()) return; + + MinimalRaceGUI::KartIconDisplayInfo* info = World::getWorld()->getKartsDisplayInfo(); + + drawPowerupIcons (kart, viewport, scaling); + drawSpeedAndEnergy (kart, viewport, scaling); + drawRankLap (info, kart, viewport); + +} // renderPlayerView + +//----------------------------------------------------------------------------- +/** Displays the racing time on the screen.s + */ +void MinimalRaceGUI::drawGlobalTimer() +{ + assert(World::getWorld() != NULL); + + if(!World::getWorld()->shouldDrawTimer()) return; + std::string s = StringUtils::timeToString(World::getWorld()->getTime()); + core::stringw sw(s.c_str()); + + static video::SColor time_color = video::SColor(255, 255, 255, 255); + core::rect pos(UserConfigParams::m_width - m_timer_width - 10, 10, + UserConfigParams::m_width, 50); + + // special case : when 3 players play, use available 4th space for such things + if (race_manager->getNumLocalPlayers() == 3) + { + pos += core::vector2d(0, UserConfigParams::m_height/2); + } + + gui::ScalableFont* font = GUIEngine::getFont(); + font->draw(sw.c_str(), pos, time_color, false, false, NULL, true /* ignore RTL */); +} // drawGlobalTimer + +//----------------------------------------------------------------------------- +/** Draws the mini map and the position of all karts on it. + */ +void MinimalRaceGUI::drawGlobalMiniMap() +{ + World *world = World::getWorld(); + // arenas currently don't have a map. + if(world->getTrack()->isArena()) return; + + const video::ITexture *mini_map=world->getTrack()->getMiniMap(); + + int upper_y = UserConfigParams::m_height-m_map_bottom-m_map_height; + int lower_y = UserConfigParams::m_height-m_map_bottom; + + core::rect dest(m_map_left, upper_y, + m_map_left + m_map_width, lower_y); + core::rect source(core::position2di(0, 0), mini_map->getOriginalSize()); + irr_driver->getVideoDriver()->draw2DImage(mini_map, dest, source, 0, 0, true); + + for(unsigned int i=0; igetNumKarts(); i++) + { + const Kart *kart = world->getKart(i); + if(kart->isEliminated()) continue; // don't draw eliminated kart + const Vec3& xyz = kart->getXYZ(); + Vec3 draw_at; + world->getTrack()->mapPoint2MiniMap(xyz, &draw_at); + // int marker_height = m_marker->getOriginalSize().Height; + core::rect source(i *m_marker_rendered_size, + 0, + (i+1)*m_marker_rendered_size, + m_marker_rendered_size); + int marker_half_size = (kart->getController()->isPlayerController() + ? m_marker_player_size + : m_marker_ai_size )>>1; + core::rect position(m_map_left+(int)(draw_at.getX()-marker_half_size), + lower_y -(int)(draw_at.getY()+marker_half_size), + m_map_left+(int)(draw_at.getX()+marker_half_size), + lower_y -(int)(draw_at.getY()-marker_half_size)); + irr_driver->getVideoDriver()->draw2DImage(m_marker, position, source, + NULL, NULL, true); + } // for igetPowerup(); + if(powerup->getType() == PowerupManager::POWERUP_NOTHING) return; + int n = kart->getNumPowerup() ; + 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 rect(core::position2di(0, 0), t->getOriginalSize()); + + for ( int i = 0 ; i < n ; i++ ) + { + int x2=(int)(x1+i*itemSpacing); + core::rect pos(x2, y1, x2+nSize, y1+nSize); + irr_driver->getVideoDriver()->draw2DImage(t, pos, rect, NULL, + NULL, true); + } // for i +} // drawPowerupIcons + +//----------------------------------------------------------------------------- +/** Energy meter that gets filled with nitro. This function is called from + * drawSpeedAndEnergy, which defines the correct position of the energy + * meter. + * \param x X position of the meter. + * \param y Y position of the meter. + * \param kart Kart to display the data for. + * \param scaling Scaling applied (in case of split screen) + */ +void MinimalRaceGUI::drawEnergyMeter(int x, int y, const Kart *kart, + const core::recti &viewport, + const core::vector2df &scaling) +{ + float state = (float)(kart->getEnergy()) / MAX_NITRO; + if (state < 0.0f) state = 0.0f; + else if (state > 1.0f) state = 1.0f; + + int h = (int)(viewport.getHeight()/3); + int w = h/4; // gauge image is so 1:4 + + y -= h; + + x -= w; + + // Background + irr_driver->getVideoDriver()->draw2DImage(m_gauge_empty, core::rect(x, y, x+w, y+h) /* dest rect */, + core::rect(0, 0, 64, 256) /* source rect */, + NULL /* clip rect */, NULL /* colors */, + true /* alpha */); + + // Target + if (race_manager->getCoinTarget() > 0) + { + float coin_target = (float)race_manager->getCoinTarget() / MAX_NITRO; + + const int EMPTY_TOP_PIXELS = 4; + const int EMPTY_BOTTOM_PIXELS = 3; + int y1 = y + (int)(EMPTY_TOP_PIXELS + + (h - EMPTY_TOP_PIXELS - EMPTY_BOTTOM_PIXELS) + *(1.0f - coin_target) ); + if (state >= 1.0f) y1 = y; + + core::rect clip(x, y1, x + w, y + h); + irr_driver->getVideoDriver()->draw2DImage(m_gauge_goal, core::rect(x, y, x+w, y+h) /* dest rect */, + core::rect(0, 0, 64, 256) /* source rect */, + &clip, NULL /* colors */, true /* alpha */); + } + + // Filling (current state) + if (state > 0.0f) + { + const int EMPTY_TOP_PIXELS = 4; + const int EMPTY_BOTTOM_PIXELS = 3; + int y1 = y + (int)(EMPTY_TOP_PIXELS + + (h - EMPTY_TOP_PIXELS - EMPTY_BOTTOM_PIXELS) + *(1.0f - state) ); + if (state >= 1.0f) y1 = y; + + core::rect clip(x, y1, x + w, y + h); + irr_driver->getVideoDriver()->draw2DImage(m_gauge_full, core::rect(x, y, x+w, y+h) /* dest rect */, + core::rect(0, 0, 64, 256) /* source rect */, + &clip, NULL /* colors */, true /* alpha */); + } + + +} // drawEnergyMeter + +//----------------------------------------------------------------------------- +void MinimalRaceGUI::drawSpeedAndEnergy(const Kart* kart, const core::recti &viewport, + const core::vector2df &scaling) +{ + + float minRatio = std::min(scaling.X, scaling.Y); + const int SPEEDWIDTH = 128; + int meter_height = (int)(SPEEDWIDTH*minRatio); + + drawEnergyMeter(viewport.LowerRightCorner.X, + (int)(viewport.LowerRightCorner.Y - meter_height*0.75f), + kart, viewport, scaling); + +} // drawSpeedAndEnergy + +//----------------------------------------------------------------------------- +/** Displays the rank and the lap of the kart. + * \param info Info object c +*/ +void MinimalRaceGUI::drawRankLap(const KartIconDisplayInfo* info, const Kart* 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 + +//----------------------------------------------------------------------------- +/** Removes messages which have been displayed long enough. This function + * must be called after drawAllMessages, otherwise messages which are only + * displayed once will not be drawn! + **/ + +void MinimalRaceGUI::cleanupMessages(const float dt) +{ + AllMessageType::iterator p =m_messages.begin(); + while(p!=m_messages.end()) + { + if((*p).done(dt)) + { + p = m_messages.erase(p); + } + else + { + ++p; + } + } +} // cleanupMessages + +//----------------------------------------------------------------------------- +/** Displays all messages in the message queue + **/ +void MinimalRaceGUI::drawAllMessages(const Kart* kart, + const core::recti &viewport, + const core::vector2df &scaling) +{ + int y = viewport.LowerRightCorner.Y - m_small_font_max_height - 10; + + const int x = (viewport.LowerRightCorner.X + viewport.UpperLeftCorner.X)/2; + const int w = (viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X); + + // draw less important first, at the very bottom of the screen + // unimportant messages are skipped in multiplayer, they take too much screen space + if (race_manager->getNumLocalPlayers() < 2) + { + for (AllMessageType::const_iterator i = m_messages.begin(); + i != m_messages.end(); ++i) + { + TimedMessage const &msg = *i; + if (!msg.m_important) + { + // Display only messages for all karts, or messages for this kart + if (msg.m_kart && msg.m_kart!=kart) continue; + + core::rect pos(x - w/2, y, x + w/2, y + m_max_font_height); + GUIEngine::getSmallFont()->draw( + core::stringw(msg.m_message.c_str()).c_str(), + pos, msg.m_color, true /* hcenter */, true /* vcenter */); + y -= m_small_font_max_height; + } + } + } + + // First line of text somewhat under the top of the viewport. + y = (int)(viewport.UpperLeftCorner.Y + 164*scaling.Y); + + gui::ScalableFont* font = GUIEngine::getFont(); + int font_height = m_max_font_height; + if (race_manager->getNumLocalPlayers() > 2) + { + font = GUIEngine::getSmallFont(); + font_height = m_small_font_max_height; + } + + // The message are displayed in reverse order, so that a multi-line + // message (addMessage("1", ...); addMessage("2",...) is displayed + // in the right order: "1" on top of "2" + for (AllMessageType::const_iterator i = m_messages.begin(); + i != m_messages.end(); ++i) + { + TimedMessage const &msg = *i; + + // less important messages were already displayed + if (!msg.m_important) continue; + + // Display only messages for all karts, or messages for this kart + if (msg.m_kart && msg.m_kart!=kart) continue; + + core::rect pos(x - w/2, y, x + w/2, y + font_height); + + font->draw(core::stringw(msg.m_message.c_str()).c_str(), + pos, msg.m_color, true /* hcenter */, + true /* vcenter */); + + y += font_height; + } // for i in all messages +} // drawAllMessages + +//----------------------------------------------------------------------------- +/** Adds a message to the message queue. The message is displayed for a + * certain amount of time (unless time<0, then the message is displayed + * once). + **/ +void MinimalRaceGUI::addMessage(const core::stringw &msg, const Kart *kart, + float time, int font_size, + const video::SColor &color, bool important) +{ + m_messages.push_back(TimedMessage(msg, kart, time, font_size, color, + important)); +} // addMessage + +//----------------------------------------------------------------------------- +// Displays the description given for the music currently being played - +// usually the title and composer. +void MinimalRaceGUI::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; + + std::string s="\""+mi->getTitle()+"\""; + core::stringw thetext(s.c_str()); + + 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 pos_by(textXFrom, text_y+40, + textXTo, text_y+40); + std::string s="by "+mi->getComposer(); + font->draw(core::stringw(s.c_str()).c_str(), pos_by, white, + true, true); + } + + // ---- Draw "song name" text + core::rect 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 dest(noteX-iconSizeX/2+20, + noteY-iconSizeY/2+ICON_SIZE/2, + noteX+iconSizeX/2+20, + noteY+iconSizeY/2+ICON_SIZE/2); + const core::rect source(core::position2d(0,0), + t->getOriginalSize()); + + irr_driver->getVideoDriver()->draw2DImage(t, dest, source, + NULL, NULL, true); +} // drawGlobalMusicDescription + +// ---------------------------------------------------------------------------- +/** Draws the ready-set-go message on the screen. + */ +void MinimalRaceGUI::drawGlobalReadySetGo() +{ + switch (World::getWorld()->getPhase()) + { + case WorldStatus::READY_PHASE: + { + static video::SColor color = video::SColor(255, 255, 255, 255); + core::rect pos(UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1, + UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1); + gui::IGUIFont* font = GUIEngine::getTitleFont(); + font->draw(m_string_ready.c_str(), pos, color, true, true); + } + break; + case WorldStatus::SET_PHASE: + { + static video::SColor color = video::SColor(255, 255, 255, 255); + core::rect pos(UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1, + UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1); + gui::IGUIFont* font = GUIEngine::getTitleFont(); + //I18N: as in "ready, set, go", shown at the beginning of the race + font->draw(m_string_set.c_str(), pos, color, true, true); + } + break; + case WorldStatus::GO_PHASE: + { + static video::SColor color = video::SColor(255, 255, 255, 255); + core::rect pos(UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1, + UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1); + //gui::IGUIFont* font = irr_driver->getRaceFont(); + gui::IGUIFont* font = GUIEngine::getTitleFont(); + //I18N: as in "ready, set, go", shown at the beginning of the race + font->draw(m_string_go.c_str(), pos, color, true, true); + } + break; + default: + break; + } // switch +} // drawGlobalReadySetGo diff --git a/src/states_screens/minimal_race_gui.hpp b/src/states_screens/minimal_race_gui.hpp new file mode 100644 index 000000000..2eda4fa9e --- /dev/null +++ b/src/states_screens/minimal_race_gui.hpp @@ -0,0 +1,203 @@ +// $Id$ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2004-2005 Steve Baker +// Copyright (C) 2006 Joerg Henrichs, SuperTuxKart-Team, Steve Baker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_MINIMAL_RACE_GUI_HPP +#define HEADER_MINIMAL_RACE_GUI_HPP + +#include +#include + +#include "irrlicht.h" +using namespace irr; + +#include "config/player.hpp" +#include "states_screens/race_gui_base.hpp" + +class InputMap; +class Kart; +class Material; +class RaceSetup; + +/** + * \brief Handles the in-race GUI (messages, mini-map, rankings, timer, etc...) + * \ingroup states_screens + */ +class MinimalRaceGUI : public RaceGUIBase +{ +private: + class TimedMessage + { + public: + irr::core::stringw m_message; //!< message to display + float m_remaining_time; //!< time remaining before removing this message from screen + video::SColor m_color; //!< color of message + int m_font_size; //!< size + const Kart *m_kart; + bool m_important; //!< Important msgs are displayed in the middle of the screen + // ----------------------------------------------------- + // std::vector needs standard copy-ctor and std-assignment op. + // let compiler create defaults .. they'll do the job, no + // deep copies here .. + TimedMessage(const irr::core::stringw &message, + const Kart *kart, float time, int size, + const video::SColor &color, const bool important) + { + m_message = message; + m_font_size = size; + m_kart = kart; + m_remaining_time = ( time < 0.0f ) ? -1.0f : time; + m_color = color; + m_important = important; + } // TimedMessage + // ----------------------------------------------------- + // in follow leader the clock counts backwards + bool done(const float dt) + { + m_remaining_time -= dt; + return m_remaining_time < 0; + } // done + }; // TimedMessage + // --------------------------------------------------------- + + Material *m_speed_meter_icon; + Material *m_speed_bar_icon; + Material *m_plunger_face; + typedef std::vector AllMessageType; + AllMessageType m_messages; + + /** A texture with all mini dots to be displayed in the minimap for all karts. */ + video::ITexture *m_marker; + + /** Musical notes icon (for music description and credits) */ + Material *m_music_icon; + + /** Translated string 'lap' displayed every frame. */ + core::stringw m_string_lap; + + /** Translated string 'rank' displayed every frame. */ + core::stringw m_string_rank; + + /** Translated string 'Top %d' displayed every frame. */ + core::stringw m_string_top; + + /** Translated strings 'ready', 'set', 'go'. */ + core::stringw m_string_ready, m_string_set, m_string_go; + + video::ITexture *m_gauge_empty; + video::ITexture *m_gauge_full; + video::ITexture *m_gauge_goal; + + // Minimap related variables + // ------------------------- + /** The mini map of the track. */ + video::ITexture *m_mini_map; + + /** The size of a single marker in pixels, must be a power of 2. */ + int m_marker_rendered_size; + + /** The size of a single marker on the screen for AI karts, + * need not be a power of 2. */ + int m_marker_ai_size; + + /** The size of a single marker on the screen or player karts, + * need not be a power of 2. */ + int m_marker_player_size; + + /** The width of the rendered mini map in pixels, must be a power of 2. */ + int m_map_rendered_width; + + /** The height of the rendered mini map in pixels, must be a power of 2. */ + int m_map_rendered_height; + + /** Width of the map in pixels on the screen, need not be a power of 2. */ + int m_map_width; + + /** Height of the map in pixels on the screen, need not be a power of 2. */ + int m_map_height; + + /** Distance of map from left side of screen. */ + int m_map_left; + + /** Distance of map from bottom of screen. */ + int m_map_bottom; + + /** Used to display messages without overlapping */ + int m_max_font_height; + int m_small_font_max_height; + + /** Maximum string length of 'rank', 'lap', '99/99'. Used to position + * the rank/lap text correctly close to the right border. */ + int m_rank_lap_width; + + /** Maximum string length for the timer */ + int m_timer_width; + + /** previous position of icons */ + std::vector< core::vector2d > m_previous_icons_position; + Material *m_icons_frame; + + void createMarkerTexture(); + void createRegularPolygon(unsigned int n, float radius, + const core::vector2df ¢er, + const video::SColor &color, + video::S3DVertex *v, unsigned short int *index); + + /* Display informat for one player on the screen. */ + void drawEnergyMeter (int x, int y, const Kart *kart, + const core::recti &viewport, + const core::vector2df &scaling); + void drawPowerupIcons (const Kart* kart, + const core::recti &viewport, + const core::vector2df &scaling); + void drawAllMessages (const Kart* kart, + const core::recti &viewport, + const core::vector2df &scaling); + void drawSpeedAndEnergy (const Kart* kart, const core::recti &viewport, + const core::vector2df &scaling); + void drawRankLap (const KartIconDisplayInfo* info, const Kart* kart, + const core::recti &viewport); + /** Display items that are shown once only (for all karts). */ + void drawGlobalMiniMap (); + void drawGlobalTimer (); + void drawGlobalMusicDescription(); + void cleanupMessages (const float dt); + void drawGlobalReadySetGo (); + +public: + + MinimalRaceGUI(); + ~MinimalRaceGUI(); + virtual void renderGlobal(float dt); + virtual void renderPlayerView(const Kart *kart); + + virtual void addMessage(const irr::core::stringw &m, const Kart *kart, + float time, int fonst_size, + const video::SColor &color= + video::SColor(255, 255, 0, 255), + bool important=true); + + virtual void clearAllMessages() { m_messages.clear(); } + + /** Returns the size of the texture on which to render the minimap to. */ + virtual const core::dimension2du getMiniMapSize() const + { return core::dimension2du(m_map_width, m_map_height); } +}; // MinimalRaceGUI + +#endif